myapps
This commit is contained in:
parent
9bdd5f522d
commit
6e1ef89d65
@ -1,4 +1,4 @@
|
|||||||
import { Model, Kb } from '../schema.js';
|
import { App, Kb } from '../schema.js';
|
||||||
import { auth } from './system.js';
|
import { auth } from './system.js';
|
||||||
|
|
||||||
export const useAppRoute = (app) => {
|
export const useAppRoute = (app) => {
|
||||||
@ -17,7 +17,7 @@ export const useAppRoute = (app) => {
|
|||||||
...(id && { _id: id })
|
...(id && { _id: id })
|
||||||
};
|
};
|
||||||
|
|
||||||
const modelsRaw = await Model.find(where)
|
const modelsRaw = await App.find(where)
|
||||||
.skip(start)
|
.skip(start)
|
||||||
.limit(end - start)
|
.limit(end - start)
|
||||||
.sort({ [sort]: order, 'share.isShare': -1, 'share.collection': -1 });
|
.sort({ [sort]: order, 'share.isShare': -1, 'share.collection': -1 });
|
||||||
@ -50,7 +50,7 @@ export const useAppRoute = (app) => {
|
|||||||
|
|
||||||
models.push(orderedModel);
|
models.push(orderedModel);
|
||||||
}
|
}
|
||||||
const totalCount = await Model.countDocuments(where);
|
const totalCount = await App.countDocuments(where);
|
||||||
res.header('Access-Control-Expose-Headers', 'X-Total-Count');
|
res.header('Access-Control-Expose-Headers', 'X-Total-Count');
|
||||||
res.header('X-Total-Count', totalCount);
|
res.header('X-Total-Count', totalCount);
|
||||||
res.json(models);
|
res.json(models);
|
||||||
@ -70,7 +70,7 @@ export const useAppRoute = (app) => {
|
|||||||
intro
|
intro
|
||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
await Model.findByIdAndUpdate(_id, {
|
await App.findByIdAndUpdate(_id, {
|
||||||
$set: {
|
$set: {
|
||||||
intro: intro,
|
intro: intro,
|
||||||
'share.topNum': Number(topNum),
|
'share.topNum': Number(topNum),
|
||||||
|
|||||||
@ -56,7 +56,7 @@ const kbSchema = new mongoose.Schema({
|
|||||||
__v: Number
|
__v: Number
|
||||||
});
|
});
|
||||||
|
|
||||||
const modelSchema = new mongoose.Schema({
|
const appSchema = new mongoose.Schema({
|
||||||
userId: mongoose.Schema.Types.ObjectId,
|
userId: mongoose.Schema.Types.ObjectId,
|
||||||
name: String,
|
name: String,
|
||||||
avatar: String,
|
avatar: String,
|
||||||
@ -104,7 +104,7 @@ const SystemSchema = new mongoose.Schema({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export const Model = mongoose.models['model'] || mongoose.model('model', modelSchema);
|
export const App = mongoose.models['model'] || mongoose.model('model', appSchema);
|
||||||
export const Kb = mongoose.models['kb'] || mongoose.model('kb', kbSchema);
|
export const Kb = mongoose.models['kb'] || mongoose.model('kb', kbSchema);
|
||||||
export const User = mongoose.models['user'] || mongoose.model('user', userSchema);
|
export const User = mongoose.models['user'] || mongoose.model('user', userSchema);
|
||||||
export const Pay = mongoose.models['pay'] || mongoose.model('pay', paySchema);
|
export const Pay = mongoose.models['pay'] || mongoose.model('pay', paySchema);
|
||||||
|
|||||||
@ -1,44 +1,44 @@
|
|||||||
import { GET, POST, DELETE, PUT } from './request';
|
import { GET, POST, DELETE, PUT } from './request';
|
||||||
import type { AppSchema } from '@/types/mongoSchema';
|
import type { AppSchema } from '@/types/mongoSchema';
|
||||||
import type { ModelUpdateParams } from '@/types/model';
|
import type { AppUpdateParams } from '@/types/app';
|
||||||
import { RequestPaging } from '../types/index';
|
import { RequestPaging } from '../types/index';
|
||||||
import type { ModelListResponse } from './response/model';
|
import type { AppListResponse } from './response/app';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取模型列表
|
* 获取模型列表
|
||||||
*/
|
*/
|
||||||
export const getMyModels = () => GET<ModelListResponse>('/model/list');
|
export const getMyModels = () => GET<AppListResponse>('/app/list');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建一个模型
|
* 创建一个模型
|
||||||
*/
|
*/
|
||||||
export const postCreateModel = (data: { name: string }) => POST<string>('/model/create', data);
|
export const postCreateModel = (data: { name: string }) => POST<string>('/app/create', data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据 ID 删除模型
|
* 根据 ID 删除模型
|
||||||
*/
|
*/
|
||||||
export const delModelById = (id: string) => DELETE(`/model/del?modelId=${id}`);
|
export const delModelById = (id: string) => DELETE(`/app/del?modelId=${id}`);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据 ID 获取模型
|
* 根据 ID 获取模型
|
||||||
*/
|
*/
|
||||||
export const getModelById = (id: string) => GET<AppSchema>(`/model/detail?modelId=${id}`);
|
export const getModelById = (id: string) => GET<AppSchema>(`/app/detail?modelId=${id}`);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据 ID 更新模型
|
* 根据 ID 更新模型
|
||||||
*/
|
*/
|
||||||
export const putAppById = (id: string, data: ModelUpdateParams) =>
|
export const putAppById = (id: string, data: AppUpdateParams) =>
|
||||||
PUT(`/model/update?appId=${id}`, data);
|
PUT(`/app/update?appId=${id}`, data);
|
||||||
|
|
||||||
/* 共享市场 */
|
/* 共享市场 */
|
||||||
/**
|
/**
|
||||||
* 获取共享市场模型
|
* 获取共享市场模型
|
||||||
*/
|
*/
|
||||||
export const getShareModelList = (data: { searchText?: string } & RequestPaging) =>
|
export const getShareModelList = (data: { searchText?: string } & RequestPaging) =>
|
||||||
POST(`/model/share/getModels`, data);
|
POST(`/app/share/getModels`, data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 收藏/取消收藏模型
|
* 收藏/取消收藏模型
|
||||||
*/
|
*/
|
||||||
export const triggerModelCollection = (modelId: string) =>
|
export const triggerModelCollection = (modelId: string) =>
|
||||||
POST<number>(`/model/share/collection?modelId=${modelId}`);
|
POST<number>(`/app/share/collection?modelId=${modelId}`);
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import type { HistoryItemType } from '@/types/chat';
|
|||||||
import type { InitChatResponse, InitShareChatResponse } from './response/chat';
|
import type { InitChatResponse, InitShareChatResponse } from './response/chat';
|
||||||
import { RequestPaging } from '../types/index';
|
import { RequestPaging } from '../types/index';
|
||||||
import type { ShareChatSchema } from '@/types/mongoSchema';
|
import type { ShareChatSchema } from '@/types/mongoSchema';
|
||||||
import type { ShareChatEditType } from '@/types/model';
|
import type { ShareChatEditType } from '@/types/app';
|
||||||
import { Obj2Query } from '@/utils/tools';
|
import { Obj2Query } from '@/utils/tools';
|
||||||
import type { QuoteItemType } from '@/pages/api/openapi/kb/appKbSearch';
|
import type { QuoteItemType } from '@/pages/api/openapi/kb/appKbSearch';
|
||||||
import type { Props as UpdateHistoryProps } from '@/pages/api/chat/history/updateChatHistory';
|
import type { Props as UpdateHistoryProps } from '@/pages/api/chat/history/updateChatHistory';
|
||||||
|
|||||||
6
client/src/api/response/app.d.ts
vendored
Normal file
6
client/src/api/response/app.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { AppListItemType } from '@/types/app';
|
||||||
|
|
||||||
|
export type AppListResponse = {
|
||||||
|
myApps: AppListItemType[];
|
||||||
|
myCollectionApps: AppListItemType[];
|
||||||
|
};
|
||||||
6
client/src/api/response/model.d.ts
vendored
6
client/src/api/response/model.d.ts
vendored
@ -1,6 +0,0 @@
|
|||||||
import { ModelListItemType } from '@/types/model';
|
|
||||||
|
|
||||||
export type ModelListResponse = {
|
|
||||||
myApps: ModelListItemType[];
|
|
||||||
myCollectionApps: ModelListItemType[];
|
|
||||||
};
|
|
||||||
@ -7,7 +7,7 @@ import { useQuery } from '@tanstack/react-query';
|
|||||||
const unAuthPage: { [key: string]: boolean } = {
|
const unAuthPage: { [key: string]: boolean } = {
|
||||||
'/': true,
|
'/': true,
|
||||||
'/login': true,
|
'/login': true,
|
||||||
'/model/share': true,
|
'/appStore': true,
|
||||||
'/chat/share': true
|
'/chat/share': true
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -32,12 +32,6 @@ const Navbar = ({ unread }: { unread: number }) => {
|
|||||||
link: `/app/list`,
|
link: `/app/list`,
|
||||||
activeLink: ['/app/list']
|
activeLink: ['/app/list']
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: '旧应用',
|
|
||||||
icon: 'model',
|
|
||||||
link: `/model?modelId=${lastModelId}`,
|
|
||||||
activeLink: ['/model']
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: '知识库',
|
label: '知识库',
|
||||||
icon: 'kb',
|
icon: 'kb',
|
||||||
@ -47,8 +41,8 @@ const Navbar = ({ unread }: { unread: number }) => {
|
|||||||
{
|
{
|
||||||
label: '市场',
|
label: '市场',
|
||||||
icon: 'appStore',
|
icon: 'appStore',
|
||||||
link: '/model/share',
|
link: '/appStore',
|
||||||
activeLink: ['/model/share']
|
activeLink: ['/appStore']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '账号',
|
label: '账号',
|
||||||
@ -57,7 +51,7 @@ const Navbar = ({ unread }: { unread: number }) => {
|
|||||||
activeLink: ['/number']
|
activeLink: ['/number']
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[lastChatId, lastChatModelId, lastModelId]
|
[lastChatId, lastChatModelId]
|
||||||
);
|
);
|
||||||
|
|
||||||
const itemStyles: any = {
|
const itemStyles: any = {
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { ShareChatEditType } from '@/types/model';
|
import { getSystemModelList } from '@/api/system';
|
||||||
|
import type { ShareChatEditType } from '@/types/app';
|
||||||
import type { AppSchema } from '@/types/mongoSchema';
|
import type { AppSchema } from '@/types/mongoSchema';
|
||||||
|
|
||||||
export const embeddingModel = 'text-embedding-ada-002';
|
export const embeddingModel = 'text-embedding-ada-002';
|
||||||
|
|||||||
@ -274,7 +274,7 @@ export const theme = extendTheme({
|
|||||||
borders: {
|
borders: {
|
||||||
sm: '1px solid #EFF0F1',
|
sm: '1px solid #EFF0F1',
|
||||||
base: '1px solid #DEE0E2',
|
base: '1px solid #DEE0E2',
|
||||||
md: '1px solid #BDC1C5'
|
md: '1px solid #DAE0E2'
|
||||||
},
|
},
|
||||||
breakpoints: {
|
breakpoints: {
|
||||||
sm: '900px',
|
sm: '900px',
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
|||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase } from '@/service/mongo';
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authUser } from '@/service/utils/auth';
|
||||||
import { Model } from '@/service/models/model';
|
import { App } from '@/service/models/model';
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
try {
|
try {
|
||||||
@ -21,7 +21,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
// 上限校验
|
// 上限校验
|
||||||
const authCount = await Model.countDocuments({
|
const authCount = await App.countDocuments({
|
||||||
userId
|
userId
|
||||||
});
|
});
|
||||||
if (authCount >= 50) {
|
if (authCount >= 50) {
|
||||||
@ -29,7 +29,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 创建模型
|
// 创建模型
|
||||||
const response = await Model.create({
|
const response = await App.create({
|
||||||
name,
|
name,
|
||||||
userId
|
userId
|
||||||
});
|
});
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { Chat, Model, connectToDatabase, Collection, ShareChat } from '@/service/mongo';
|
import { Chat, App, connectToDatabase, Collection, ShareChat } from '@/service/mongo';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authUser } from '@/service/utils/auth';
|
||||||
import { authApp } from '@/service/utils/auth';
|
import { authApp } from '@/service/utils/auth';
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 删除模型
|
// 删除模型
|
||||||
await Model.deleteOne({
|
await App.deleteOne({
|
||||||
_id: modelId,
|
_id: modelId,
|
||||||
userId
|
userId
|
||||||
});
|
});
|
||||||
@ -1,8 +1,8 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase, Collection, Model } from '@/service/mongo';
|
import { connectToDatabase, Collection, App } from '@/service/mongo';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authUser } from '@/service/utils/auth';
|
||||||
import type { ModelListResponse } from '@/api/response/model';
|
import type { AppListResponse } from '@/api/response/app';
|
||||||
|
|
||||||
/* 获取模型列表 */
|
/* 获取模型列表 */
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
@ -14,7 +14,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
|
|
||||||
// 根据 userId 获取模型信息
|
// 根据 userId 获取模型信息
|
||||||
const [myApps, myCollections] = await Promise.all([
|
const [myApps, myCollections] = await Promise.all([
|
||||||
Model.find(
|
App.find(
|
||||||
{
|
{
|
||||||
userId
|
userId
|
||||||
},
|
},
|
||||||
@ -31,7 +31,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
.then((res) => res.filter((item) => item.modelId))
|
.then((res) => res.filter((item) => item.modelId))
|
||||||
]);
|
]);
|
||||||
|
|
||||||
jsonRes<ModelListResponse>(res, {
|
jsonRes<AppListResponse>(res, {
|
||||||
data: {
|
data: {
|
||||||
myApps: myApps.map((item) => ({
|
myApps: myApps.map((item) => ({
|
||||||
_id: item._id,
|
_id: item._id,
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase, Collection, Model } from '@/service/mongo';
|
import { connectToDatabase, Collection, App } from '@/service/mongo';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authUser } from '@/service/utils/auth';
|
||||||
|
|
||||||
/* 模型收藏切换 */
|
/* 模型收藏切换 */
|
||||||
@ -30,7 +30,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await Model.findByIdAndUpdate(modelId, {
|
await App.findByIdAndUpdate(modelId, {
|
||||||
'share.collection': await Collection.countDocuments({ modelId })
|
'share.collection': await Collection.countDocuments({ modelId })
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1,8 +1,8 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase, Model } from '@/service/mongo';
|
import { connectToDatabase, App } from '@/service/mongo';
|
||||||
import type { PagingData } from '@/types';
|
import type { PagingData } from '@/types';
|
||||||
import type { ShareModelItem } from '@/types/model';
|
import type { ShareAppItem } from '@/types/app';
|
||||||
import { parseCookie } from '@/service/utils/auth';
|
import { parseCookie } from '@/service/utils/auth';
|
||||||
import { Types } from 'mongoose';
|
import { Types } from 'mongoose';
|
||||||
|
|
||||||
@ -91,11 +91,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
// 获取被分享的模型
|
// 获取被分享的模型
|
||||||
const [models, total] = await Promise.all([
|
const [models, total] = await Promise.all([
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
Model.aggregate(pipeline),
|
App.aggregate(pipeline),
|
||||||
Model.countDocuments(where)
|
App.countDocuments(where)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
jsonRes<PagingData<ShareModelItem>>(res, {
|
jsonRes<PagingData<ShareAppItem>>(res, {
|
||||||
data: {
|
data: {
|
||||||
pageNum,
|
pageNum,
|
||||||
pageSize,
|
pageSize,
|
||||||
@ -2,14 +2,14 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
|||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase } from '@/service/mongo';
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authUser } from '@/service/utils/auth';
|
||||||
import { Model } from '@/service/models/model';
|
import { App } from '@/service/models/model';
|
||||||
import type { ModelUpdateParams } from '@/types/model';
|
import type { AppUpdateParams } from '@/types/app';
|
||||||
import { authApp } from '@/service/utils/auth';
|
import { authApp } from '@/service/utils/auth';
|
||||||
|
|
||||||
/* 获取我的模型 */
|
/* 获取我的模型 */
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
try {
|
try {
|
||||||
const { name, avatar, chat, share, intro, modules } = req.body as ModelUpdateParams;
|
const { name, avatar, chat, share, intro, modules } = req.body as AppUpdateParams;
|
||||||
const { appId } = req.query as { appId: string };
|
const { appId } = req.query as { appId: string };
|
||||||
|
|
||||||
if (!appId) {
|
if (!appId) {
|
||||||
@ -27,7 +27,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 更新模型
|
// 更新模型
|
||||||
await Model.updateOne(
|
await App.updateOne(
|
||||||
{
|
{
|
||||||
_id: appId,
|
_id: appId,
|
||||||
userId
|
userId
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase, Chat, Model } from '@/service/mongo';
|
import { connectToDatabase, Chat, App } from '@/service/mongo';
|
||||||
import type { InitChatResponse } from '@/api/response/chat';
|
import type { InitChatResponse } from '@/api/response/chat';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authUser } from '@/service/utils/auth';
|
||||||
import { ChatItemType } from '@/types/chat';
|
import { ChatItemType } from '@/types/chat';
|
||||||
@ -23,13 +23,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
// 没有 modelId 时,直接获取用户的第一个id
|
// 没有 modelId 时,直接获取用户的第一个id
|
||||||
const app = await (async () => {
|
const app = await (async () => {
|
||||||
if (!modelId) {
|
if (!modelId) {
|
||||||
const myModel = await Model.findOne({ userId });
|
const myModel = await App.findOne({ userId });
|
||||||
if (!myModel) {
|
if (!myModel) {
|
||||||
const { _id } = await Model.create({
|
const { _id } = await App.create({
|
||||||
name: '应用1',
|
name: '应用1',
|
||||||
userId
|
userId
|
||||||
});
|
});
|
||||||
return (await Model.findById(_id)) as AppSchema;
|
return (await App.findById(_id)) as AppSchema;
|
||||||
} else {
|
} else {
|
||||||
return myModel;
|
return myModel;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { ChatItemType } from '@/types/chat';
|
import { ChatItemType } from '@/types/chat';
|
||||||
import { connectToDatabase, Chat, Model } from '@/service/mongo';
|
import { connectToDatabase, Chat, App } from '@/service/mongo';
|
||||||
import { authApp } from '@/service/utils/auth';
|
import { authApp } from '@/service/utils/auth';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authUser } from '@/service/utils/auth';
|
||||||
import { Types } from 'mongoose';
|
import { Types } from 'mongoose';
|
||||||
@ -60,7 +60,7 @@ export async function saveChat({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
if (String(app.userId) === userId) {
|
if (String(app.userId) === userId) {
|
||||||
await Model.findByIdAndUpdate(modelId, {
|
await App.findByIdAndUpdate(modelId, {
|
||||||
updateTime: new Date()
|
updateTime: new Date()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -96,7 +96,7 @@ export async function saveChat({
|
|||||||
// update app
|
// update app
|
||||||
...(String(app.userId) === userId
|
...(String(app.userId) === userId
|
||||||
? [
|
? [
|
||||||
Model.findByIdAndUpdate(modelId, {
|
App.findByIdAndUpdate(modelId, {
|
||||||
updateTime: new Date()
|
updateTime: new Date()
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
|||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase, ShareChat } from '@/service/mongo';
|
import { connectToDatabase, ShareChat } from '@/service/mongo';
|
||||||
import { authApp, authUser } from '@/service/utils/auth';
|
import { authApp, authUser } from '@/service/utils/auth';
|
||||||
import type { ShareChatEditType } from '@/types/model';
|
import type { ShareChatEditType } from '@/types/app';
|
||||||
|
|
||||||
/* create a shareChat */
|
/* create a shareChat */
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
|||||||
@ -117,6 +117,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
|||||||
},
|
},
|
||||||
stream
|
stream
|
||||||
});
|
});
|
||||||
|
console.log(responseData, answerText);
|
||||||
|
|
||||||
// save chat
|
// save chat
|
||||||
if (typeof chatId === 'string') {
|
if (typeof chatId === 'string') {
|
||||||
@ -282,14 +283,17 @@ async function dispatchModules({
|
|||||||
if (res.closed) return Promise.resolve();
|
if (res.closed) return Promise.resolve();
|
||||||
console.log('run=========', module.type, module.url);
|
console.log('run=========', module.type, module.url);
|
||||||
|
|
||||||
|
// direct answer
|
||||||
if (module.type === AppModuleItemTypeEnum.answer) {
|
if (module.type === AppModuleItemTypeEnum.answer) {
|
||||||
|
const text =
|
||||||
|
module.inputs.find((item) => item.key === SpecificInputEnum.answerText)?.value || '';
|
||||||
pushStore({
|
pushStore({
|
||||||
answer: module.inputs.find((item) => item.key === SpecificInputEnum.answerText)?.value || ''
|
answer: text
|
||||||
});
|
});
|
||||||
return StreamAnswer({
|
return StreamAnswer({
|
||||||
res,
|
res,
|
||||||
stream,
|
stream,
|
||||||
text: module.inputs.find((item) => item.key === SpecificInputEnum.answerText)?.value
|
text: text
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,9 +369,9 @@ function StreamAnswer({
|
|||||||
}: {
|
}: {
|
||||||
res: NextApiResponse;
|
res: NextApiResponse;
|
||||||
stream?: boolean;
|
stream?: boolean;
|
||||||
text?: '';
|
text?: string;
|
||||||
}) {
|
}) {
|
||||||
if (stream) {
|
if (stream && text) {
|
||||||
return sseResponse({
|
return sseResponse({
|
||||||
res,
|
res,
|
||||||
event: sseResponseEventEnum.answer,
|
event: sseResponseEventEnum.answer,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase, KB, Model, TrainingData } from '@/service/mongo';
|
import { connectToDatabase, KB, App, TrainingData } from '@/service/mongo';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authUser } from '@/service/utils/auth';
|
||||||
import { PgClient } from '@/service/pg';
|
import { PgClient } from '@/service/pg';
|
||||||
import { Types } from 'mongoose';
|
import { Types } from 'mongoose';
|
||||||
@ -32,7 +32,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
});
|
});
|
||||||
|
|
||||||
// delete related model
|
// delete related model
|
||||||
await Model.updateMany(
|
await App.updateMany(
|
||||||
{
|
{
|
||||||
userId
|
userId
|
||||||
},
|
},
|
||||||
|
|||||||
@ -36,7 +36,7 @@ import { getShareChatList, delShareChatById, createShareChat } from '@/api/chat'
|
|||||||
import { formatTimeToChatTime, useCopyData, getErrText } from '@/utils/tools';
|
import { formatTimeToChatTime, useCopyData, getErrText } from '@/utils/tools';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { defaultShareChat } from '@/constants/model';
|
import { defaultShareChat } from '@/constants/model';
|
||||||
import type { ShareChatEditType } from '@/types/model';
|
import type { ShareChatEditType } from '@/types/app';
|
||||||
|
|
||||||
const Share = ({ modelId }: { modelId: string }) => {
|
const Share = ({ modelId }: { modelId: string }) => {
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|||||||
@ -4,8 +4,8 @@ import { Box, Flex } from '@chakra-ui/react';
|
|||||||
import { useUserStore } from '@/store/user';
|
import { useUserStore } from '@/store/user';
|
||||||
import { useGlobalStore } from '@/store/global';
|
import { useGlobalStore } from '@/store/global';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import Tabs from '@/components/Tabs';
|
|
||||||
|
|
||||||
|
import Tabs from '@/components/Tabs';
|
||||||
import Settings from './components/Settings';
|
import Settings from './components/Settings';
|
||||||
import { defaultApp } from '@/constants/model';
|
import { defaultApp } from '@/constants/model';
|
||||||
|
|
||||||
|
|||||||
@ -1,24 +1,81 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, useTheme } from '@chakra-ui/react';
|
import { Box, Grid, Card, useTheme, Flex, IconButton, Button } from '@chakra-ui/react';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
|
import { useUserStore } from '@/store/user';
|
||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import Avatar from '@/components/Avatar';
|
||||||
|
|
||||||
|
import styles from './index.module.scss';
|
||||||
|
import MyIcon from '@/components/Icon';
|
||||||
|
import { AddIcon } from '@chakra-ui/icons';
|
||||||
|
|
||||||
const MyApps = () => {
|
const MyApps = () => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const { myApps, loadMyModels } = useUserStore();
|
||||||
|
|
||||||
|
/* 加载模型 */
|
||||||
|
useQuery(['loadModels'], () => loadMyModels(false));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Box
|
<Flex py={3} px={5} borderBottom={theme.borders.base} alignItems={'center'}>
|
||||||
className="textlg"
|
<Box flex={1} className="textlg" letterSpacing={1} fontSize={'24px'} fontWeight={'bold'}>
|
||||||
borderBottom={theme.borders.base}
|
我的应用
|
||||||
letterSpacing={1}
|
</Box>
|
||||||
py={3}
|
<Button leftIcon={<AddIcon />} variant={'base'}>
|
||||||
px={5}
|
新建
|
||||||
fontSize={'24px'}
|
</Button>
|
||||||
fontWeight={'bold'}
|
</Flex>
|
||||||
onClick={() => router.push(`/app/detail?appId=642adec15f01d67d4613efdb`)}
|
<Grid
|
||||||
|
p={5}
|
||||||
|
gridTemplateColumns={['1fr', 'repeat(3,1fr)', 'repeat(4,1fr)', 'repeat(5,1fr)']}
|
||||||
|
gridGap={5}
|
||||||
>
|
>
|
||||||
我的应用
|
{myApps.map((app) => (
|
||||||
</Box>
|
<Card
|
||||||
|
key={app._id}
|
||||||
|
py={4}
|
||||||
|
px={5}
|
||||||
|
cursor={'pointer'}
|
||||||
|
h={'140px'}
|
||||||
|
border={theme.borders.md}
|
||||||
|
boxShadow={'none'}
|
||||||
|
userSelect={'none'}
|
||||||
|
_hover={{
|
||||||
|
boxShadow: 'xl',
|
||||||
|
transform: 'scale(1.03)',
|
||||||
|
borderColor: 'transparent',
|
||||||
|
'& .delete': {
|
||||||
|
display: 'block'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onClick={() => router.push(`/app/detail?appId=${app._id}`)}
|
||||||
|
>
|
||||||
|
<Flex alignItems={'center'} h={'38px'} position={'relative'}>
|
||||||
|
<Avatar src={app.avatar} borderRadius={'md'} w={'28px'} />
|
||||||
|
<Box ml={3}>{app.name}</Box>
|
||||||
|
<IconButton
|
||||||
|
className="delete"
|
||||||
|
position={'absolute'}
|
||||||
|
right={0}
|
||||||
|
size={'sm'}
|
||||||
|
icon={<MyIcon name={'delete'} w={'14px'} />}
|
||||||
|
variant={'base'}
|
||||||
|
borderRadius={'md'}
|
||||||
|
aria-label={'delete'}
|
||||||
|
display={'none'}
|
||||||
|
_hover={{
|
||||||
|
bg: 'myGray.100'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
<Box className={styles.intro} py={2} fontSize={'sm'} color={'myGray.600'}>
|
||||||
|
{app.intro || '这个应用还没写介绍~'}
|
||||||
|
</Box>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Flex, Button, Tooltip, Card } from '@chakra-ui/react';
|
import { Box, Flex, Button, Tooltip, Card } from '@chakra-ui/react';
|
||||||
import type { ShareModelItem } from '@/types/model';
|
import type { ShareAppItem } from '@/types/app';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import MyIcon from '@/components/Icon';
|
import MyIcon from '@/components/Icon';
|
||||||
import styles from '../index.module.scss';
|
import styles from '../index.module.scss';
|
||||||
@ -10,7 +10,7 @@ const ShareModelList = ({
|
|||||||
models = [],
|
models = [],
|
||||||
onclickCollection
|
onclickCollection
|
||||||
}: {
|
}: {
|
||||||
models: ShareModelItem[];
|
models: ShareAppItem[];
|
||||||
onclickCollection: (modelId: string) => void;
|
onclickCollection: (modelId: string) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
7
client/src/pages/appStore/index.module.scss
Normal file
7
client/src/pages/appStore/index.module.scss
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
.intro {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 3;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
@ -3,7 +3,7 @@ import { Box, Flex, Card, Grid, Input } from '@chakra-ui/react';
|
|||||||
import { useLoading } from '@/hooks/useLoading';
|
import { useLoading } from '@/hooks/useLoading';
|
||||||
import { getShareModelList, triggerModelCollection } from '@/api/app';
|
import { getShareModelList, triggerModelCollection } from '@/api/app';
|
||||||
import { usePagination } from '@/hooks/usePagination';
|
import { usePagination } from '@/hooks/usePagination';
|
||||||
import type { ShareModelItem } from '@/types/model';
|
import type { ShareAppItem } from '@/types/app';
|
||||||
import { useUserStore } from '@/store/user';
|
import { useUserStore } from '@/store/user';
|
||||||
import ShareModelList from './components/list';
|
import ShareModelList from './components/list';
|
||||||
import styles from './index.module.scss';
|
import styles from './index.module.scss';
|
||||||
@ -21,7 +21,7 @@ const modelList = () => {
|
|||||||
Pagination,
|
Pagination,
|
||||||
getData,
|
getData,
|
||||||
pageNum
|
pageNum
|
||||||
} = usePagination<ShareModelItem>({
|
} = usePagination<ShareAppItem>({
|
||||||
api: getShareModelList,
|
api: getShareModelList,
|
||||||
pageSize: 24,
|
pageSize: 24,
|
||||||
params: {
|
params: {
|
||||||
@ -1,10 +1,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Flex } from '@chakra-ui/react';
|
import { Box, Flex } from '@chakra-ui/react';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { ModelListItemType } from '@/types/model';
|
import { AppListItemType } from '@/types/app';
|
||||||
import Avatar from '@/components/Avatar';
|
import Avatar from '@/components/Avatar';
|
||||||
|
|
||||||
const ModelList = ({ models, modelId }: { models: ModelListItemType[]; modelId: string }) => {
|
const ModelList = ({ models, modelId }: { models: AppListItemType[]; modelId: string }) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,172 +0,0 @@
|
|||||||
import React, { useCallback, useMemo, useState } from 'react';
|
|
||||||
import { Box, Flex, Input, IconButton, Tooltip, useTheme } from '@chakra-ui/react';
|
|
||||||
import { AddIcon } from '@chakra-ui/icons';
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import MyIcon from '@/components/Icon';
|
|
||||||
import { postCreateModel } from '@/api/app';
|
|
||||||
import { useLoading } from '@/hooks/useLoading';
|
|
||||||
import { useToast } from '@/hooks/useToast';
|
|
||||||
import { useQuery } from '@tanstack/react-query';
|
|
||||||
import { useUserStore } from '@/store/user';
|
|
||||||
import { MyModelsTypeEnum } from '@/constants/user';
|
|
||||||
import dynamic from 'next/dynamic';
|
|
||||||
|
|
||||||
const Avatar = dynamic(() => import('@/components/Avatar'), {
|
|
||||||
ssr: false
|
|
||||||
});
|
|
||||||
const Tabs = dynamic(() => import('@/components/Tabs'), {
|
|
||||||
ssr: false
|
|
||||||
});
|
|
||||||
|
|
||||||
const ModelList = ({ modelId }: { modelId: string }) => {
|
|
||||||
const [currentTab, setCurrentTab] = useState(MyModelsTypeEnum.my);
|
|
||||||
|
|
||||||
const theme = useTheme();
|
|
||||||
const router = useRouter();
|
|
||||||
const { toast } = useToast();
|
|
||||||
const { Loading, setIsLoading } = useLoading();
|
|
||||||
const { myApps, myCollectionApps, loadMyModels, refreshModel } = useUserStore();
|
|
||||||
const [searchText, setSearchText] = useState('');
|
|
||||||
|
|
||||||
/* 加载模型 */
|
|
||||||
const { isFetching } = useQuery(['loadModels'], () => loadMyModels(false));
|
|
||||||
|
|
||||||
const onclickCreateModel = useCallback(async () => {
|
|
||||||
setIsLoading(true);
|
|
||||||
try {
|
|
||||||
const id = await postCreateModel({
|
|
||||||
name: `AI应用${myApps.length + 1}`
|
|
||||||
});
|
|
||||||
toast({
|
|
||||||
title: '创建成功',
|
|
||||||
status: 'success'
|
|
||||||
});
|
|
||||||
refreshModel.freshMyModels();
|
|
||||||
router.push(`/model?modelId=${id}`);
|
|
||||||
} catch (err: any) {
|
|
||||||
toast({
|
|
||||||
title: typeof err === 'string' ? err : err.message || '出现了意外',
|
|
||||||
status: 'error'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setIsLoading(false);
|
|
||||||
}, [myApps.length, refreshModel, router, setIsLoading, toast]);
|
|
||||||
|
|
||||||
const currentModels = useMemo(() => {
|
|
||||||
const map = {
|
|
||||||
[MyModelsTypeEnum.my]: {
|
|
||||||
list: myApps.filter((item) => new RegExp(searchText, 'ig').test(item.name + item.intro)),
|
|
||||||
emptyText: '还没有 AI 应用~\n快来创建一个吧'
|
|
||||||
},
|
|
||||||
[MyModelsTypeEnum.collection]: {
|
|
||||||
list: myCollectionApps.filter((item) =>
|
|
||||||
new RegExp(searchText, 'ig').test(item.name + item.intro)
|
|
||||||
),
|
|
||||||
emptyText: '收藏的 AI 应用为空~\n快去市场找一个吧'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return map[currentTab];
|
|
||||||
}, [currentTab, myCollectionApps, myApps, searchText]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Flex
|
|
||||||
position={'relative'}
|
|
||||||
flexDirection={'column'}
|
|
||||||
w={'100%'}
|
|
||||||
h={'100%'}
|
|
||||||
bg={'white'}
|
|
||||||
borderRight={['', theme.borders.base]}
|
|
||||||
>
|
|
||||||
<Flex w={'90%'} mt={5} mb={3} mx={'auto'}>
|
|
||||||
<Flex flex={1} mr={2} position={'relative'} alignItems={'center'}>
|
|
||||||
<Input
|
|
||||||
h={'32px'}
|
|
||||||
placeholder="根据名字和介绍搜索 AI 应用"
|
|
||||||
value={searchText}
|
|
||||||
onChange={(e) => setSearchText(e.target.value)}
|
|
||||||
/>
|
|
||||||
{searchText && (
|
|
||||||
<MyIcon
|
|
||||||
zIndex={10}
|
|
||||||
position={'absolute'}
|
|
||||||
right={3}
|
|
||||||
name={'closeSolid'}
|
|
||||||
w={'16px'}
|
|
||||||
h={'16px'}
|
|
||||||
color={'myGray.500'}
|
|
||||||
cursor={'pointer'}
|
|
||||||
onClick={() => setSearchText('')}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
<Tooltip label={'新建一个AI应用'}>
|
|
||||||
<IconButton
|
|
||||||
h={'32px'}
|
|
||||||
icon={<AddIcon />}
|
|
||||||
aria-label={''}
|
|
||||||
variant={'base'}
|
|
||||||
onClick={onclickCreateModel}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</Flex>
|
|
||||||
<Flex userSelect={'none'}>
|
|
||||||
<Box flex={1}></Box>
|
|
||||||
<Tabs
|
|
||||||
w={'130px'}
|
|
||||||
list={[
|
|
||||||
{ label: '我的', id: MyModelsTypeEnum.my },
|
|
||||||
{ label: '收藏', id: MyModelsTypeEnum.collection }
|
|
||||||
]}
|
|
||||||
activeId={currentTab}
|
|
||||||
size={'sm'}
|
|
||||||
onChange={(id: any) => setCurrentTab(id)}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
<Box flex={'1 0 0'} h={0} pl={[0, 2]} overflowY={'scroll'} userSelect={'none'}>
|
|
||||||
{currentModels.list.map((item) => (
|
|
||||||
<Flex
|
|
||||||
key={item._id}
|
|
||||||
position={'relative'}
|
|
||||||
alignItems={'center'}
|
|
||||||
p={3}
|
|
||||||
mb={[2, 0]}
|
|
||||||
cursor={'pointer'}
|
|
||||||
transition={'background-color .2s ease-in'}
|
|
||||||
borderRadius={['', 'md']}
|
|
||||||
borderBottom={['1px solid #f4f4f4', 'none']}
|
|
||||||
_hover={{
|
|
||||||
backgroundImage: ['', theme.lgColor.hoverBlueGradient]
|
|
||||||
}}
|
|
||||||
{...(modelId === item._id
|
|
||||||
? {
|
|
||||||
backgroundImage: `${theme.lgColor.activeBlueGradient} !important`
|
|
||||||
}
|
|
||||||
: {})}
|
|
||||||
onClick={() => {
|
|
||||||
if (item._id === modelId) return;
|
|
||||||
router.push(`/model?modelId=${item._id}`);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Avatar src={item.avatar} w={'34px'} h={'34px'} />
|
|
||||||
<Box flex={'1 0 0'} w={0} ml={3}>
|
|
||||||
<Box className="textEllipsis" color={'myGray.1000'}>
|
|
||||||
{item.name}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
))}
|
|
||||||
{!isFetching && currentModels.list.length === 0 && (
|
|
||||||
<Flex h={'100%'} flexDirection={'column'} alignItems={'center'} pt={'30vh'}>
|
|
||||||
<MyIcon name="empty" w={'48px'} h={'48px'} color={'transparent'} />
|
|
||||||
<Box mt={2} color={'myGray.500'}>
|
|
||||||
{currentModels.emptyText}
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
<Loading loading={isFetching} fixed={false} />
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ModelList;
|
|
||||||
@ -1,85 +0,0 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import { Box, Divider, Flex, useTheme, Button, Skeleton, useDisclosure } from '@chakra-ui/react';
|
|
||||||
import { useCopyData } from '@/utils/tools';
|
|
||||||
import dynamic from 'next/dynamic';
|
|
||||||
import MyIcon from '@/components/Icon';
|
|
||||||
|
|
||||||
const APIKeyModal = dynamic(() => import('@/components/APIKeyModal'), {
|
|
||||||
ssr: false
|
|
||||||
});
|
|
||||||
|
|
||||||
const API = ({ modelId }: { modelId: string }) => {
|
|
||||||
const theme = useTheme();
|
|
||||||
const { copyData } = useCopyData();
|
|
||||||
const [baseUrl, setBaseUrl] = useState('https://fastgpt.run/api/openapi');
|
|
||||||
const {
|
|
||||||
isOpen: isOpenAPIModal,
|
|
||||||
onOpen: onOpenAPIModal,
|
|
||||||
onClose: onCloseAPIModal
|
|
||||||
} = useDisclosure();
|
|
||||||
const [isLoaded, setIsLoaded] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setBaseUrl(`${location.origin}/api/openapi`);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Flex flexDirection={'column'} h={'100%'}>
|
|
||||||
<Box display={['none', 'flex']} px={5} alignItems={'center'}>
|
|
||||||
<Box flex={1}>
|
|
||||||
AppId:
|
|
||||||
<Box
|
|
||||||
as={'span'}
|
|
||||||
ml={2}
|
|
||||||
fontWeight={'bold'}
|
|
||||||
cursor={'pointer'}
|
|
||||||
onClick={() => copyData(modelId, '已复制 AppId')}
|
|
||||||
>
|
|
||||||
{modelId}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
<Flex
|
|
||||||
bg={'myWhite.600'}
|
|
||||||
py={2}
|
|
||||||
px={4}
|
|
||||||
borderRadius={'md'}
|
|
||||||
cursor={'pointer'}
|
|
||||||
onClick={() => copyData(baseUrl, '已复制 API 地址')}
|
|
||||||
>
|
|
||||||
<Box border={theme.borders.md} px={2} borderRadius={'md'} fontSize={'sm'}>
|
|
||||||
API服务器
|
|
||||||
</Box>
|
|
||||||
<Box ml={2} color={'myGray.900'} fontSize={['sm', 'md']}>
|
|
||||||
{baseUrl}
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
<Button
|
|
||||||
ml={3}
|
|
||||||
leftIcon={<MyIcon name={'apikey'} w={'16px'} color={''} />}
|
|
||||||
variant={'base'}
|
|
||||||
onClick={onOpenAPIModal}
|
|
||||||
>
|
|
||||||
API 秘钥
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
<Divider mt={3} />
|
|
||||||
<Box flex={1}>
|
|
||||||
<Skeleton h="100%" isLoaded={isLoaded} fadeDuration={2}>
|
|
||||||
<iframe
|
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
height: '100%'
|
|
||||||
}}
|
|
||||||
src="https://kjqvjse66l.feishu.cn/docx/DmLedTWtUoNGX8xui9ocdUEjnNh"
|
|
||||||
frameBorder="0"
|
|
||||||
onLoad={() => setIsLoaded(true)}
|
|
||||||
onError={() => setIsLoaded(true)}
|
|
||||||
/>
|
|
||||||
</Skeleton>
|
|
||||||
</Box>
|
|
||||||
{isOpenAPIModal && <APIKeyModal onClose={onCloseAPIModal} />}
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default API;
|
|
||||||
@ -1,394 +0,0 @@
|
|||||||
import React, { useState, useCallback } from 'react';
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import {
|
|
||||||
Card,
|
|
||||||
Flex,
|
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
useDisclosure,
|
|
||||||
Modal,
|
|
||||||
ModalOverlay,
|
|
||||||
ModalContent,
|
|
||||||
ModalBody,
|
|
||||||
ModalHeader,
|
|
||||||
ModalFooter,
|
|
||||||
ModalCloseButton,
|
|
||||||
Grid,
|
|
||||||
useTheme,
|
|
||||||
IconButton,
|
|
||||||
Tooltip,
|
|
||||||
Textarea
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { useUserStore } from '@/store/user';
|
|
||||||
import { useQuery } from '@tanstack/react-query';
|
|
||||||
import Avatar from '@/components/Avatar';
|
|
||||||
import { AddIcon, DeleteIcon, QuestionOutlineIcon } from '@chakra-ui/icons';
|
|
||||||
import { putAppById } from '@/api/app';
|
|
||||||
import { useToast } from '@/hooks/useToast';
|
|
||||||
import { useLoading } from '@/hooks/useLoading';
|
|
||||||
import { useForm } from 'react-hook-form';
|
|
||||||
import MyIcon from '@/components/Icon';
|
|
||||||
import MySlider from '@/components/Slider';
|
|
||||||
|
|
||||||
const Kb = ({ modelId }: { modelId: string }) => {
|
|
||||||
const theme = useTheme();
|
|
||||||
const router = useRouter();
|
|
||||||
const { toast } = useToast();
|
|
||||||
const { appDetail, loadKbList, loadAppDetail } = useUserStore();
|
|
||||||
const { Loading, setIsLoading } = useLoading();
|
|
||||||
const [selectedIdList, setSelectedIdList] = useState<string[]>([]);
|
|
||||||
const [refresh, setRefresh] = useState(false);
|
|
||||||
const { register, reset, getValues, setValue } = useForm({
|
|
||||||
defaultValues: {
|
|
||||||
searchSimilarity: appDetail.chat.searchSimilarity,
|
|
||||||
searchLimit: appDetail.chat.searchLimit,
|
|
||||||
searchEmptyText: appDetail.chat.searchEmptyText
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const {
|
|
||||||
isOpen: isOpenKbSelect,
|
|
||||||
onOpen: onOpenKbSelect,
|
|
||||||
onClose: onCloseKbSelect
|
|
||||||
} = useDisclosure();
|
|
||||||
const {
|
|
||||||
isOpen: isOpenEditParams,
|
|
||||||
onOpen: onOpenEditParams,
|
|
||||||
onClose: onCloseEditParams
|
|
||||||
} = useDisclosure();
|
|
||||||
|
|
||||||
const onchangeKb = useCallback(
|
|
||||||
async (
|
|
||||||
data: {
|
|
||||||
relatedKbs?: string[];
|
|
||||||
searchSimilarity?: number;
|
|
||||||
searchLimit?: number;
|
|
||||||
searchEmptyText?: string;
|
|
||||||
} = {}
|
|
||||||
) => {
|
|
||||||
setIsLoading(true);
|
|
||||||
try {
|
|
||||||
await putAppById(modelId, {
|
|
||||||
chat: {
|
|
||||||
...appDetail.chat,
|
|
||||||
...data
|
|
||||||
}
|
|
||||||
});
|
|
||||||
loadAppDetail(modelId, true);
|
|
||||||
} catch (err: any) {
|
|
||||||
toast({
|
|
||||||
title: err?.message || '更新失败',
|
|
||||||
status: 'error'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setIsLoading(false);
|
|
||||||
},
|
|
||||||
[setIsLoading, modelId, appDetail.chat, loadAppDetail, toast]
|
|
||||||
);
|
|
||||||
|
|
||||||
// init kb select list
|
|
||||||
const { isLoading, data: kbList = [] } = useQuery(['loadKbList'], () => loadKbList());
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box position={'relative'} px={5} minH={'50vh'}>
|
|
||||||
<Box fontWeight={'bold'}>关联的知识库({appDetail.chat?.relatedKbs.length})</Box>
|
|
||||||
{(() => {
|
|
||||||
const kbs =
|
|
||||||
appDetail.chat?.relatedKbs
|
|
||||||
?.map((id) => kbList.find((kb) => kb._id === id))
|
|
||||||
.filter((item) => item) || [];
|
|
||||||
return (
|
|
||||||
<Grid
|
|
||||||
mt={2}
|
|
||||||
gridTemplateColumns={[
|
|
||||||
'repeat(1,1fr)',
|
|
||||||
'repeat(2,1fr)',
|
|
||||||
'repeat(3,1fr)',
|
|
||||||
'repeat(4,1fr)'
|
|
||||||
]}
|
|
||||||
gridGap={[3, 4]}
|
|
||||||
>
|
|
||||||
<Card
|
|
||||||
p={3}
|
|
||||||
border={theme.borders.base}
|
|
||||||
boxShadow={'sm'}
|
|
||||||
cursor={'pointer'}
|
|
||||||
bg={'myGray.100'}
|
|
||||||
_hover={{
|
|
||||||
bg: 'white',
|
|
||||||
color: 'myBlue.800'
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
reset({
|
|
||||||
searchSimilarity: appDetail.chat.searchSimilarity,
|
|
||||||
searchLimit: appDetail.chat.searchLimit,
|
|
||||||
searchEmptyText: appDetail.chat.searchEmptyText
|
|
||||||
});
|
|
||||||
onOpenEditParams();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Flex alignItems={'center'} h={'38px'} fontWeight={'bold'}>
|
|
||||||
<IconButton
|
|
||||||
mr={2}
|
|
||||||
size={'sm'}
|
|
||||||
borderRadius={'lg'}
|
|
||||||
icon={<MyIcon name={'edit'} w={'14px'} color={'myGray.600'} />}
|
|
||||||
aria-label={''}
|
|
||||||
variant={'base'}
|
|
||||||
/>
|
|
||||||
调整搜索参数
|
|
||||||
</Flex>
|
|
||||||
<Flex mt={3} h={'30px'} color={'myGray.600'} fontSize={'sm'}>
|
|
||||||
相似度: {appDetail.chat.searchSimilarity}, 单次搜索数量:{' '}
|
|
||||||
{appDetail.chat.searchLimit}, 空搜索时拒绝回复:{' '}
|
|
||||||
{appDetail.chat.searchEmptyText !== '' ? 'true' : 'false'}
|
|
||||||
</Flex>
|
|
||||||
</Card>
|
|
||||||
<Card
|
|
||||||
p={3}
|
|
||||||
border={theme.borders.base}
|
|
||||||
boxShadow={'sm'}
|
|
||||||
cursor={'pointer'}
|
|
||||||
bg={'myGray.100'}
|
|
||||||
_hover={{
|
|
||||||
bg: 'white',
|
|
||||||
color: 'myBlue.800'
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
setSelectedIdList(
|
|
||||||
appDetail.chat?.relatedKbs ? [...appDetail.chat?.relatedKbs] : []
|
|
||||||
);
|
|
||||||
onOpenKbSelect();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Flex alignItems={'center'} h={'38px'} fontWeight={'bold'}>
|
|
||||||
<IconButton
|
|
||||||
mr={2}
|
|
||||||
size={'sm'}
|
|
||||||
borderRadius={'lg'}
|
|
||||||
icon={<AddIcon />}
|
|
||||||
aria-label={''}
|
|
||||||
variant={'base'}
|
|
||||||
/>
|
|
||||||
选择关联知识库
|
|
||||||
</Flex>
|
|
||||||
<Flex mt={3} h={'30px'} color={'myGray.600'} fontSize={'sm'}>
|
|
||||||
关联知识库,让 AI 应用回答你的特有内容。
|
|
||||||
</Flex>
|
|
||||||
</Card>
|
|
||||||
{kbs.map((item) =>
|
|
||||||
item ? (
|
|
||||||
<Card
|
|
||||||
key={item._id}
|
|
||||||
p={3}
|
|
||||||
border={theme.borders.base}
|
|
||||||
boxShadow={'sm'}
|
|
||||||
_hover={{
|
|
||||||
boxShadow: 'lg',
|
|
||||||
'& .detailBtn': {
|
|
||||||
display: 'block'
|
|
||||||
},
|
|
||||||
'& .delete': {
|
|
||||||
display: 'block'
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Flex alignItems={'center'} h={'38px'}>
|
|
||||||
<Avatar src={item.avatar} w={['26px', '32px', '38px']}></Avatar>
|
|
||||||
<Box ml={3} fontWeight={'bold'} fontSize={['md', 'lg', 'xl']}>
|
|
||||||
{item.name}
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
<Flex mt={3} alignItems={'flex-end'} justifyContent={'flex-end'} h={'30px'}>
|
|
||||||
<Button
|
|
||||||
mr={3}
|
|
||||||
className="detailBtn"
|
|
||||||
display={['flex', 'none']}
|
|
||||||
variant={'base'}
|
|
||||||
size={'sm'}
|
|
||||||
onClick={() => router.push(`/kb?kbId=${item._id}`)}
|
|
||||||
>
|
|
||||||
查看详情
|
|
||||||
</Button>
|
|
||||||
<IconButton
|
|
||||||
className="delete"
|
|
||||||
display={['flex', 'none']}
|
|
||||||
icon={<DeleteIcon />}
|
|
||||||
variant={'outline'}
|
|
||||||
aria-label={'delete'}
|
|
||||||
size={'sm'}
|
|
||||||
_hover={{ color: 'red.600' }}
|
|
||||||
onClick={() => {
|
|
||||||
const ids = appDetail.chat?.relatedKbs
|
|
||||||
? [...appDetail.chat.relatedKbs]
|
|
||||||
: [];
|
|
||||||
const i = ids.findIndex((id) => id === item._id);
|
|
||||||
ids.splice(i, 1);
|
|
||||||
onchangeKb({ relatedKbs: ids });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Card>
|
|
||||||
) : null
|
|
||||||
)}
|
|
||||||
</Grid>
|
|
||||||
);
|
|
||||||
})()}
|
|
||||||
{/* select kb modal */}
|
|
||||||
<Modal isOpen={isOpenKbSelect} onClose={onCloseKbSelect}>
|
|
||||||
<ModalOverlay />
|
|
||||||
<ModalContent
|
|
||||||
display={'flex'}
|
|
||||||
flexDirection={'column'}
|
|
||||||
w={'800px'}
|
|
||||||
maxW={'90vw'}
|
|
||||||
h={['90vh', 'auto']}
|
|
||||||
>
|
|
||||||
<ModalHeader>关联的知识库({selectedIdList.length})</ModalHeader>
|
|
||||||
<ModalCloseButton />
|
|
||||||
<ModalBody
|
|
||||||
flex={['1 0 0', '0 0 auto']}
|
|
||||||
maxH={'80vh'}
|
|
||||||
overflowY={'auto'}
|
|
||||||
display={'grid'}
|
|
||||||
gridTemplateColumns={['repeat(1,1fr)', 'repeat(2,1fr)', 'repeat(3,1fr)']}
|
|
||||||
gridGap={3}
|
|
||||||
>
|
|
||||||
{kbList.map((item) => (
|
|
||||||
<Card
|
|
||||||
key={item._id}
|
|
||||||
p={3}
|
|
||||||
border={theme.borders.base}
|
|
||||||
boxShadow={'sm'}
|
|
||||||
h={'80px'}
|
|
||||||
cursor={'pointer'}
|
|
||||||
order={appDetail.chat?.relatedKbs?.includes(item._id) ? 0 : 1}
|
|
||||||
_hover={{
|
|
||||||
boxShadow: 'md'
|
|
||||||
}}
|
|
||||||
{...(selectedIdList.includes(item._id)
|
|
||||||
? {
|
|
||||||
bg: 'myBlue.300'
|
|
||||||
}
|
|
||||||
: {})}
|
|
||||||
onClick={() => {
|
|
||||||
let ids = [...selectedIdList];
|
|
||||||
if (!selectedIdList.includes(item._id)) {
|
|
||||||
ids = ids.concat(item._id);
|
|
||||||
} else {
|
|
||||||
const i = ids.findIndex((id) => id === item._id);
|
|
||||||
ids.splice(i, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
ids = ids.filter((id) => kbList.find((item) => item._id === id));
|
|
||||||
setSelectedIdList(ids);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Flex alignItems={'center'} h={'38px'}>
|
|
||||||
<Avatar src={item.avatar} w={['24px', '28px', '32px']}></Avatar>
|
|
||||||
<Box ml={3} fontWeight={'bold'} fontSize={['md', 'lg', 'xl']}>
|
|
||||||
{item.name}
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
</Card>
|
|
||||||
))}
|
|
||||||
</ModalBody>
|
|
||||||
|
|
||||||
<ModalFooter>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
onCloseKbSelect();
|
|
||||||
onchangeKb({ relatedKbs: selectedIdList });
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
完成
|
|
||||||
</Button>
|
|
||||||
</ModalFooter>
|
|
||||||
</ModalContent>
|
|
||||||
</Modal>
|
|
||||||
{/* edit mode */}
|
|
||||||
<Modal isOpen={isOpenEditParams} onClose={onCloseEditParams}>
|
|
||||||
<ModalOverlay />
|
|
||||||
<ModalContent display={'flex'} flexDirection={'column'} w={'600px'} maxW={'90vw'}>
|
|
||||||
<ModalHeader>搜索参数调整</ModalHeader>
|
|
||||||
<ModalCloseButton />
|
|
||||||
<ModalBody>
|
|
||||||
<Flex pt={3} pb={5}>
|
|
||||||
<Box flex={'0 0 100px'}>
|
|
||||||
相似度
|
|
||||||
<Tooltip label={'高相似度推荐0.8及以上。'}>
|
|
||||||
<QuestionOutlineIcon ml={1} />
|
|
||||||
</Tooltip>
|
|
||||||
</Box>
|
|
||||||
<MySlider
|
|
||||||
markList={[
|
|
||||||
{ label: '0', value: 0 },
|
|
||||||
{ label: '1', value: 1 }
|
|
||||||
]}
|
|
||||||
min={0}
|
|
||||||
max={1}
|
|
||||||
step={0.01}
|
|
||||||
value={getValues('searchSimilarity')}
|
|
||||||
onChange={(val) => {
|
|
||||||
setValue('searchSimilarity', val);
|
|
||||||
setRefresh(!refresh);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
<Flex py={8}>
|
|
||||||
<Box flex={'0 0 100px'}>单次搜索数量</Box>
|
|
||||||
<Box flex={1}>
|
|
||||||
<MySlider
|
|
||||||
markList={[
|
|
||||||
{ label: '1', value: 1 },
|
|
||||||
{ label: '20', value: 20 }
|
|
||||||
]}
|
|
||||||
min={1}
|
|
||||||
max={20}
|
|
||||||
value={getValues('searchLimit')}
|
|
||||||
onChange={(val) => {
|
|
||||||
setValue('searchLimit', val);
|
|
||||||
setRefresh(!refresh);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
<Flex pt={3}>
|
|
||||||
<Box flex={'0 0 100px'}>空搜索回复</Box>
|
|
||||||
<Box flex={1}>
|
|
||||||
<Textarea
|
|
||||||
rows={5}
|
|
||||||
maxLength={500}
|
|
||||||
placeholder={
|
|
||||||
'若填写该内容,没有搜索到对应内容时,将直接回复填写的内容。\n为了连贯上下文,FastGpt 会取部分上一个聊天的搜索记录作为补充,因此在连续对话时,该功能可能会失效。'
|
|
||||||
}
|
|
||||||
{...register('searchEmptyText')}
|
|
||||||
></Textarea>
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
</ModalBody>
|
|
||||||
<ModalFooter>
|
|
||||||
<Button variant={'base'} mr={3} onClick={onCloseEditParams}>
|
|
||||||
取消
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
onCloseEditParams();
|
|
||||||
onchangeKb({
|
|
||||||
searchSimilarity: getValues('searchSimilarity'),
|
|
||||||
searchLimit: getValues('searchLimit'),
|
|
||||||
searchEmptyText: getValues('searchEmptyText')
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
完成
|
|
||||||
</Button>
|
|
||||||
</ModalFooter>
|
|
||||||
</ModalContent>
|
|
||||||
</Modal>
|
|
||||||
<Loading loading={isLoading} fixed={false} />
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Kb;
|
|
||||||
@ -1,281 +0,0 @@
|
|||||||
import React, { useCallback, useState } from 'react';
|
|
||||||
import {
|
|
||||||
Flex,
|
|
||||||
Box,
|
|
||||||
Tooltip,
|
|
||||||
Button,
|
|
||||||
TableContainer,
|
|
||||||
Table,
|
|
||||||
Thead,
|
|
||||||
Tr,
|
|
||||||
Th,
|
|
||||||
Td,
|
|
||||||
Tbody,
|
|
||||||
useDisclosure,
|
|
||||||
Modal,
|
|
||||||
ModalOverlay,
|
|
||||||
ModalContent,
|
|
||||||
ModalHeader,
|
|
||||||
ModalFooter,
|
|
||||||
ModalBody,
|
|
||||||
ModalCloseButton,
|
|
||||||
FormControl,
|
|
||||||
Slider,
|
|
||||||
SliderTrack,
|
|
||||||
SliderFilledTrack,
|
|
||||||
SliderThumb,
|
|
||||||
SliderMark,
|
|
||||||
Input
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
|
||||||
import MyIcon from '@/components/Icon';
|
|
||||||
import { useToast } from '@/hooks/useToast';
|
|
||||||
import { useLoading } from '@/hooks/useLoading';
|
|
||||||
import { useQuery } from '@tanstack/react-query';
|
|
||||||
import { getShareChatList, delShareChatById, createShareChat } from '@/api/chat';
|
|
||||||
import { formatTimeToChatTime, useCopyData, getErrText } from '@/utils/tools';
|
|
||||||
import { useForm } from 'react-hook-form';
|
|
||||||
import { defaultShareChat } from '@/constants/model';
|
|
||||||
import type { ShareChatEditType } from '@/types/model';
|
|
||||||
|
|
||||||
const Share = ({ modelId }: { modelId: string }) => {
|
|
||||||
const { toast } = useToast();
|
|
||||||
const { Loading, setIsLoading } = useLoading();
|
|
||||||
const { copyData } = useCopyData();
|
|
||||||
const {
|
|
||||||
isOpen: isOpenCreateShareChat,
|
|
||||||
onOpen: onOpenCreateShareChat,
|
|
||||||
onClose: onCloseCreateShareChat
|
|
||||||
} = useDisclosure();
|
|
||||||
const {
|
|
||||||
register: registerShareChat,
|
|
||||||
getValues: getShareChatValues,
|
|
||||||
setValue: setShareChatValues,
|
|
||||||
handleSubmit: submitShareChat,
|
|
||||||
reset: resetShareChat
|
|
||||||
} = useForm({
|
|
||||||
defaultValues: defaultShareChat
|
|
||||||
});
|
|
||||||
|
|
||||||
const [refresh, setRefresh] = useState(false);
|
|
||||||
|
|
||||||
const {
|
|
||||||
isFetching,
|
|
||||||
data: shareChatList = [],
|
|
||||||
refetch: refetchShareChatList
|
|
||||||
} = useQuery(['initShareChatList', modelId], () => getShareChatList(modelId));
|
|
||||||
|
|
||||||
const onclickCreateShareChat = useCallback(
|
|
||||||
async (e: ShareChatEditType) => {
|
|
||||||
try {
|
|
||||||
setIsLoading(true);
|
|
||||||
const id = await createShareChat({
|
|
||||||
...e,
|
|
||||||
modelId
|
|
||||||
});
|
|
||||||
onCloseCreateShareChat();
|
|
||||||
refetchShareChatList();
|
|
||||||
|
|
||||||
const url = `对话地址为:${location.origin}/chat/share?shareId=${id}
|
|
||||||
${e.password ? `密码为: ${e.password}` : ''}`;
|
|
||||||
copyData(url, '已复制分享地址');
|
|
||||||
|
|
||||||
resetShareChat(defaultShareChat);
|
|
||||||
} catch (err) {
|
|
||||||
toast({
|
|
||||||
title: getErrText(err, '创建分享链接异常'),
|
|
||||||
status: 'warning'
|
|
||||||
});
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
setIsLoading(false);
|
|
||||||
},
|
|
||||||
[
|
|
||||||
copyData,
|
|
||||||
modelId,
|
|
||||||
onCloseCreateShareChat,
|
|
||||||
refetchShareChatList,
|
|
||||||
resetShareChat,
|
|
||||||
setIsLoading,
|
|
||||||
toast
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
// format share used token
|
|
||||||
const formatTokens = (tokens: number) => {
|
|
||||||
if (tokens < 10000) return tokens;
|
|
||||||
return `${(tokens / 10000).toFixed(2)}万`;
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box position={'relative'} px={5} minH={'50vh'}>
|
|
||||||
<Flex justifyContent={'space-between'}>
|
|
||||||
<Box fontWeight={'bold'}>
|
|
||||||
免登录聊天窗口
|
|
||||||
<Tooltip label="可以直接分享该模型给其他用户去进行对话,对方无需登录即可直接进行对话。注意,这个功能会消耗你账号的tokens。请保管好链接和密码。">
|
|
||||||
<QuestionOutlineIcon ml={1} />
|
|
||||||
</Tooltip>
|
|
||||||
</Box>
|
|
||||||
<Button
|
|
||||||
variant={'base'}
|
|
||||||
colorScheme={'myBlue'}
|
|
||||||
size={['sm', 'md']}
|
|
||||||
{...(shareChatList.length >= 10
|
|
||||||
? {
|
|
||||||
isDisabled: true,
|
|
||||||
title: '最多创建10组'
|
|
||||||
}
|
|
||||||
: {})}
|
|
||||||
onClick={onOpenCreateShareChat}
|
|
||||||
>
|
|
||||||
创建新窗口
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
<TableContainer mt={3}>
|
|
||||||
<Table variant={'simple'} w={'100%'} overflowX={'auto'}>
|
|
||||||
<Thead>
|
|
||||||
<Tr>
|
|
||||||
<Th>名称</Th>
|
|
||||||
<Th>密码</Th>
|
|
||||||
<Th>最大上下文</Th>
|
|
||||||
<Th>tokens消耗</Th>
|
|
||||||
<Th>最后使用时间</Th>
|
|
||||||
<Th>操作</Th>
|
|
||||||
</Tr>
|
|
||||||
</Thead>
|
|
||||||
<Tbody>
|
|
||||||
{shareChatList.map((item) => (
|
|
||||||
<Tr key={item._id}>
|
|
||||||
<Td>{item.name}</Td>
|
|
||||||
<Td>{item.password === '1' ? '已开启' : '未使用'}</Td>
|
|
||||||
<Td>{item.maxContext}</Td>
|
|
||||||
<Td>{formatTokens(item.tokens)}</Td>
|
|
||||||
<Td>{item.lastTime ? formatTimeToChatTime(item.lastTime) : '未使用'}</Td>
|
|
||||||
<Td>
|
|
||||||
<Flex>
|
|
||||||
<MyIcon
|
|
||||||
mr={3}
|
|
||||||
name="copy"
|
|
||||||
w={'14px'}
|
|
||||||
cursor={'pointer'}
|
|
||||||
_hover={{ color: 'myBlue.600' }}
|
|
||||||
onClick={() => {
|
|
||||||
const url = `${location.origin}/chat/share?shareId=${item._id}`;
|
|
||||||
copyData(url, '已复制分享地址');
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<MyIcon
|
|
||||||
name="delete"
|
|
||||||
w={'14px'}
|
|
||||||
cursor={'pointer'}
|
|
||||||
_hover={{ color: 'red' }}
|
|
||||||
onClick={async () => {
|
|
||||||
setIsLoading(true);
|
|
||||||
try {
|
|
||||||
await delShareChatById(item._id);
|
|
||||||
refetchShareChatList();
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
setIsLoading(false);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Td>
|
|
||||||
</Tr>
|
|
||||||
))}
|
|
||||||
</Tbody>
|
|
||||||
</Table>
|
|
||||||
</TableContainer>
|
|
||||||
{shareChatList.length === 0 && !isFetching && (
|
|
||||||
<Flex h={'100%'} flexDirection={'column'} alignItems={'center'} pt={'10vh'}>
|
|
||||||
<MyIcon name="empty" w={'48px'} h={'48px'} color={'transparent'} />
|
|
||||||
<Box mt={2} color={'myGray.500'}>
|
|
||||||
没有创建分享链接
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
{/* create shareChat modal */}
|
|
||||||
<Modal isOpen={isOpenCreateShareChat} onClose={onCloseCreateShareChat}>
|
|
||||||
<ModalOverlay />
|
|
||||||
<ModalContent>
|
|
||||||
<ModalHeader>创建免登录窗口</ModalHeader>
|
|
||||||
<ModalCloseButton />
|
|
||||||
<ModalBody>
|
|
||||||
<FormControl>
|
|
||||||
<Flex alignItems={'center'}>
|
|
||||||
<Box flex={'0 0 60px'} w={0}>
|
|
||||||
名称:
|
|
||||||
</Box>
|
|
||||||
<Input
|
|
||||||
placeholder="记录名字,仅用于展示"
|
|
||||||
maxLength={20}
|
|
||||||
{...registerShareChat('name', {
|
|
||||||
required: '记录名称不能为空'
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</FormControl>
|
|
||||||
<FormControl mt={4}>
|
|
||||||
<Flex alignItems={'center'}>
|
|
||||||
<Box flex={'0 0 60px'} w={0}>
|
|
||||||
密码:
|
|
||||||
</Box>
|
|
||||||
<Input placeholder={'不设置密码,可直接访问'} {...registerShareChat('password')} />
|
|
||||||
</Flex>
|
|
||||||
<Box fontSize={'xs'} ml={'60px'} color={'myGray.600'}>
|
|
||||||
密码不会再次展示,请记住你的密码
|
|
||||||
</Box>
|
|
||||||
</FormControl>
|
|
||||||
<FormControl mt={9}>
|
|
||||||
<Flex alignItems={'center'}>
|
|
||||||
<Box flex={'0 0 120px'} w={0}>
|
|
||||||
最长上下文(组)
|
|
||||||
</Box>
|
|
||||||
<Slider
|
|
||||||
aria-label="slider-ex-1"
|
|
||||||
min={1}
|
|
||||||
max={20}
|
|
||||||
step={1}
|
|
||||||
value={getShareChatValues('maxContext')}
|
|
||||||
onChange={(e) => {
|
|
||||||
setShareChatValues('maxContext', e);
|
|
||||||
setRefresh(!refresh);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SliderMark
|
|
||||||
value={getShareChatValues('maxContext')}
|
|
||||||
textAlign="center"
|
|
||||||
bg="myBlue.600"
|
|
||||||
color="white"
|
|
||||||
w={'18px'}
|
|
||||||
h={'18px'}
|
|
||||||
borderRadius={'100px'}
|
|
||||||
fontSize={'xs'}
|
|
||||||
transform={'translate(-50%, -200%)'}
|
|
||||||
>
|
|
||||||
{getShareChatValues('maxContext')}
|
|
||||||
</SliderMark>
|
|
||||||
<SliderTrack>
|
|
||||||
<SliderFilledTrack bg={'myBlue.700'} />
|
|
||||||
</SliderTrack>
|
|
||||||
<SliderThumb />
|
|
||||||
</Slider>
|
|
||||||
</Flex>
|
|
||||||
</FormControl>
|
|
||||||
</ModalBody>
|
|
||||||
|
|
||||||
<ModalFooter>
|
|
||||||
<Button variant={'base'} mr={3} onClick={onCloseCreateShareChat}>
|
|
||||||
取消
|
|
||||||
</Button>
|
|
||||||
<Button onClick={submitShareChat(onclickCreateShareChat)}>确认</Button>
|
|
||||||
</ModalFooter>
|
|
||||||
</ModalContent>
|
|
||||||
</Modal>
|
|
||||||
<Loading loading={isFetching} fixed={false} />
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Share;
|
|
||||||
@ -1,102 +0,0 @@
|
|||||||
import React, { useState, useEffect, useMemo } from 'react';
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import { Box, Flex } from '@chakra-ui/react';
|
|
||||||
import { useUserStore } from '@/store/user';
|
|
||||||
import { useGlobalStore } from '@/store/global';
|
|
||||||
import dynamic from 'next/dynamic';
|
|
||||||
import Tabs from '@/components/Tabs';
|
|
||||||
|
|
||||||
import Settings from './components/Settings';
|
|
||||||
import { defaultApp } from '@/constants/model';
|
|
||||||
|
|
||||||
const Kb = dynamic(() => import('./components/Kb'), {
|
|
||||||
ssr: false
|
|
||||||
});
|
|
||||||
const Share = dynamic(() => import('./components/Share'), {
|
|
||||||
ssr: false
|
|
||||||
});
|
|
||||||
const API = dynamic(() => import('./components/API'), {
|
|
||||||
ssr: false
|
|
||||||
});
|
|
||||||
|
|
||||||
enum TabEnum {
|
|
||||||
'settings' = 'settings',
|
|
||||||
'kb' = 'kb',
|
|
||||||
'share' = 'share',
|
|
||||||
'API' = 'API'
|
|
||||||
}
|
|
||||||
|
|
||||||
const ModelDetail = ({ modelId }: { modelId: string }) => {
|
|
||||||
const router = useRouter();
|
|
||||||
const { isPc } = useGlobalStore();
|
|
||||||
const { appDetail = defaultApp, userInfo } = useUserStore();
|
|
||||||
const [currentTab, setCurrentTab] = useState<`${TabEnum}`>(TabEnum.settings);
|
|
||||||
|
|
||||||
const isOwner = useMemo(
|
|
||||||
() => appDetail.userId === userInfo?._id,
|
|
||||||
[appDetail.userId, userInfo?._id]
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
window.onbeforeunload = (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.returnValue = '内容已修改,确认离开页面吗?';
|
|
||||||
};
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.onbeforeunload = null;
|
|
||||||
};
|
|
||||||
}, [router]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setCurrentTab(TabEnum.settings);
|
|
||||||
}, [modelId]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Flex
|
|
||||||
flexDirection={'column'}
|
|
||||||
h={'100%'}
|
|
||||||
maxW={'100vw'}
|
|
||||||
pt={4}
|
|
||||||
overflow={'overlay'}
|
|
||||||
position={'relative'}
|
|
||||||
bg={'white'}
|
|
||||||
>
|
|
||||||
{/* 头部 */}
|
|
||||||
<Box textAlign={['center', 'left']} px={5} mb={4}>
|
|
||||||
<Box className="textlg" display={['block', 'none']} fontSize={'3xl'} fontWeight={'bold'}>
|
|
||||||
{appDetail.name}
|
|
||||||
</Box>
|
|
||||||
<Tabs
|
|
||||||
mx={['auto', '0']}
|
|
||||||
mt={2}
|
|
||||||
w={['300px', '360px']}
|
|
||||||
list={[
|
|
||||||
{ label: '配置', id: TabEnum.settings },
|
|
||||||
...(isOwner ? [{ label: '知识库', id: TabEnum.kb }] : []),
|
|
||||||
{ label: '分享', id: TabEnum.share },
|
|
||||||
{ label: 'API', id: TabEnum.API },
|
|
||||||
{ label: '立即对话', id: 'startChat' }
|
|
||||||
]}
|
|
||||||
size={isPc ? 'md' : 'sm'}
|
|
||||||
activeId={currentTab}
|
|
||||||
onChange={(e: any) => {
|
|
||||||
if (e === 'startChat') {
|
|
||||||
router.push(`/chat?modelId=${modelId}`);
|
|
||||||
} else {
|
|
||||||
setCurrentTab(e);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
<Box flex={1}>
|
|
||||||
{currentTab === TabEnum.settings && <Settings modelId={modelId} />}
|
|
||||||
{currentTab === TabEnum.kb && <Kb modelId={modelId} />}
|
|
||||||
{currentTab === TabEnum.API && <API modelId={modelId} />}
|
|
||||||
{currentTab === TabEnum.share && <Share modelId={modelId} />}
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ModelDetail;
|
|
||||||
@ -1,44 +0,0 @@
|
|||||||
import React, { useEffect } from 'react';
|
|
||||||
import { Box, Flex } from '@chakra-ui/react';
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import ModelList from './components/ModelList';
|
|
||||||
import dynamic from 'next/dynamic';
|
|
||||||
import { useUserStore } from '@/store/user';
|
|
||||||
import { useGlobalStore } from '@/store/global';
|
|
||||||
import Loading from '@/components/Loading';
|
|
||||||
import SideBar from '@/components/SideBar';
|
|
||||||
|
|
||||||
const ModelDetail = dynamic(() => import('./components/detail/index'), {
|
|
||||||
loading: () => <Loading fixed={false} />,
|
|
||||||
ssr: false
|
|
||||||
});
|
|
||||||
|
|
||||||
const Model = () => {
|
|
||||||
const router = useRouter();
|
|
||||||
const { modelId = '' } = router.query as { modelId: string };
|
|
||||||
const { isPc } = useGlobalStore();
|
|
||||||
const { lastModelId } = useUserStore();
|
|
||||||
|
|
||||||
// redirect modelId
|
|
||||||
useEffect(() => {
|
|
||||||
if (isPc && !modelId && lastModelId) {
|
|
||||||
router.replace(`/model?modelId=${lastModelId}`);
|
|
||||||
}
|
|
||||||
}, [isPc, lastModelId, modelId, router]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Flex h={'100%'} position={'relative'} overflow={'hidden'}>
|
|
||||||
{/* 模型列表 */}
|
|
||||||
{(isPc || !modelId) && (
|
|
||||||
<SideBar w={['100%', '0 0 250px', '0 0 270px', '0 0 290px']}>
|
|
||||||
<ModelList modelId={modelId} />
|
|
||||||
</SideBar>
|
|
||||||
)}
|
|
||||||
<Box flex={1} h={'100%'} position={'relative'}>
|
|
||||||
{modelId && <ModelDetail modelId={modelId} />}
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Model;
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import { Schema, model, models, Model as MongoModel } from 'mongoose';
|
import { Schema, model, models, Model } from 'mongoose';
|
||||||
import { AppSchema as ModelType } from '@/types/mongoSchema';
|
import { AppSchema as AppType } from '@/types/mongoSchema';
|
||||||
import { ChatModelMap, OpenAiChatEnum } from '@/constants/model';
|
import { ChatModelMap, OpenAiChatEnum } from '@/constants/model';
|
||||||
|
|
||||||
const AppSchema = new Schema({
|
const AppSchema = new Schema({
|
||||||
@ -105,4 +105,4 @@ try {
|
|||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Model: MongoModel<ModelType> = models['model'] || model('model', AppSchema);
|
export const App: Model<AppType> = models['model'] || model('model', AppSchema);
|
||||||
18
client/src/service/models/installApp.ts
Normal file
18
client/src/service/models/installApp.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { Schema, model, models, Model } from 'mongoose';
|
||||||
|
import { ChatSchema as ChatType } from '@/types/mongoSchema';
|
||||||
|
import { ChatRoleMap } from '@/constants/chat';
|
||||||
|
|
||||||
|
const InstallAppSchema = new Schema({
|
||||||
|
userId: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'user',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
modelId: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'model',
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const InstallApp: Model<ChatType> = models['installApp'] || model('chat', InstallAppSchema);
|
||||||
@ -55,7 +55,7 @@ export async function connectToDatabase(): Promise<void> {
|
|||||||
|
|
||||||
export * from './models/authCode';
|
export * from './models/authCode';
|
||||||
export * from './models/chat';
|
export * from './models/chat';
|
||||||
export * from './models/model';
|
export * from './models/app';
|
||||||
export * from './models/user';
|
export * from './models/user';
|
||||||
export * from './models/bill';
|
export * from './models/bill';
|
||||||
export * from './models/pay';
|
export * from './models/pay';
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import type { NextApiRequest } from 'next';
|
import type { NextApiRequest } from 'next';
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import Cookie from 'cookie';
|
import Cookie from 'cookie';
|
||||||
import { Chat, Model, OpenApi, User, ShareChat, KB } from '../mongo';
|
import { Chat, App, OpenApi, User, ShareChat, KB } from '../mongo';
|
||||||
import type { AppSchema } from '@/types/mongoSchema';
|
import type { AppSchema } from '@/types/mongoSchema';
|
||||||
import type { ChatItemType } from '@/types/chat';
|
import type { ChatItemType } from '@/types/chat';
|
||||||
import mongoose from 'mongoose';
|
import mongoose from 'mongoose';
|
||||||
@ -218,7 +218,7 @@ export const authApp = async ({
|
|||||||
reserveDetail?: boolean; // focus reserve detail
|
reserveDetail?: boolean; // focus reserve detail
|
||||||
}) => {
|
}) => {
|
||||||
// 获取 model 数据
|
// 获取 model 数据
|
||||||
const app = await Model.findById<AppSchema>(appId);
|
const app = await App.findById<AppSchema>(appId);
|
||||||
if (!app) {
|
if (!app) {
|
||||||
return Promise.reject('模型不存在');
|
return Promise.reject('模型不存在');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { getMyModels, getModelById } from '@/api/app';
|
|||||||
import { formatPrice } from '@/utils/user';
|
import { formatPrice } from '@/utils/user';
|
||||||
import { getTokenLogin } from '@/api/user';
|
import { getTokenLogin } from '@/api/user';
|
||||||
import { defaultApp } from '@/constants/model';
|
import { defaultApp } from '@/constants/model';
|
||||||
import { ModelListItemType } from '@/types/model';
|
import { AppListItemType } from '@/types/app';
|
||||||
import { KbItemType } from '@/types/plugin';
|
import { KbItemType } from '@/types/plugin';
|
||||||
import { getKbList, getKbById } from '@/api/plugins/kb';
|
import { getKbList, getKbById } from '@/api/plugins/kb';
|
||||||
import { defaultKbDetail } from '@/constants/kb';
|
import { defaultKbDetail } from '@/constants/kb';
|
||||||
@ -20,8 +20,8 @@ type State = {
|
|||||||
// model
|
// model
|
||||||
lastModelId: string;
|
lastModelId: string;
|
||||||
setLastModelId: (id: string) => void;
|
setLastModelId: (id: string) => void;
|
||||||
myApps: ModelListItemType[];
|
myApps: AppListItemType[];
|
||||||
myCollectionApps: ModelListItemType[];
|
myCollectionApps: AppListItemType[];
|
||||||
loadMyModels: (init?: boolean) => Promise<null>;
|
loadMyModels: (init?: boolean) => Promise<null>;
|
||||||
appDetail: AppSchema;
|
appDetail: AppSchema;
|
||||||
loadAppDetail: (id: string, init?: boolean) => Promise<AppSchema>;
|
loadAppDetail: (id: string, init?: boolean) => Promise<AppSchema>;
|
||||||
|
|||||||
34
client/src/types/app.d.ts
vendored
34
client/src/types/app.d.ts
vendored
@ -2,6 +2,40 @@ import { FlowModuleTypeEnum } from '@/constants/flow';
|
|||||||
import { XYPosition } from 'reactflow';
|
import { XYPosition } from 'reactflow';
|
||||||
import { AppModuleItemTypeEnum, ModulesInputItemTypeEnum } from '../constants/app';
|
import { AppModuleItemTypeEnum, ModulesInputItemTypeEnum } from '../constants/app';
|
||||||
import type { FlowInputItemType, FlowOutputItemType } from './flow';
|
import type { FlowInputItemType, FlowOutputItemType } from './flow';
|
||||||
|
import type { AppSchema, kbSchema } from './mongoSchema';
|
||||||
|
import { ChatModelType } from '@/constants/model';
|
||||||
|
|
||||||
|
export type AppListItemType = {
|
||||||
|
_id: string;
|
||||||
|
name: string;
|
||||||
|
avatar: string;
|
||||||
|
intro: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface AppUpdateParams {
|
||||||
|
name?: string;
|
||||||
|
avatar?: string;
|
||||||
|
intro?: string;
|
||||||
|
chat?: AppSchema['chat'];
|
||||||
|
share?: AppSchema['share'];
|
||||||
|
modules?: AppSchema['modules'];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ShareAppItem {
|
||||||
|
_id: string;
|
||||||
|
avatar: string;
|
||||||
|
name: string;
|
||||||
|
intro: string;
|
||||||
|
userId: string;
|
||||||
|
share: AppSchema['share'];
|
||||||
|
isCollection: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ShareChatEditType = {
|
||||||
|
name: string;
|
||||||
|
password: string;
|
||||||
|
maxContext: number;
|
||||||
|
};
|
||||||
|
|
||||||
/* agent */
|
/* agent */
|
||||||
/* question classify */
|
/* question classify */
|
||||||
|
|||||||
34
client/src/types/model.d.ts
vendored
34
client/src/types/model.d.ts
vendored
@ -1,34 +0,0 @@
|
|||||||
import type { AppSchema, kbSchema } from './mongoSchema';
|
|
||||||
import { ChatModelType } from '@/constants/model';
|
|
||||||
|
|
||||||
export type ModelListItemType = {
|
|
||||||
_id: string;
|
|
||||||
name: string;
|
|
||||||
avatar: string;
|
|
||||||
intro: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface ModelUpdateParams {
|
|
||||||
name?: string;
|
|
||||||
avatar?: string;
|
|
||||||
intro?: string;
|
|
||||||
chat?: AppSchema['chat'];
|
|
||||||
share?: AppSchema['share'];
|
|
||||||
modules?: AppSchema['modules'];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ShareModelItem {
|
|
||||||
_id: string;
|
|
||||||
avatar: string;
|
|
||||||
name: string;
|
|
||||||
intro: string;
|
|
||||||
userId: string;
|
|
||||||
share: AppSchema['share'];
|
|
||||||
isCollection: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ShareChatEditType = {
|
|
||||||
name: string;
|
|
||||||
password: string;
|
|
||||||
maxContext: number;
|
|
||||||
};
|
|
||||||
6
client/src/types/mongoSchema.d.ts
vendored
6
client/src/types/mongoSchema.d.ts
vendored
@ -57,17 +57,11 @@ export interface AppSchema {
|
|||||||
modules: AppModuleItemType[];
|
modules: AppModuleItemType[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ModelPopulate extends AppSchema {
|
|
||||||
userId: UserModelSchema;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CollectionSchema {
|
export interface CollectionSchema {
|
||||||
modelId: string;
|
modelId: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ModelDataType = 0 | 1;
|
|
||||||
|
|
||||||
export interface TrainingDataSchema {
|
export interface TrainingDataSchema {
|
||||||
_id: string;
|
_id: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user