mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 13:03:50 +00:00
myapps
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Model, Kb } from '../schema.js';
|
||||
import { App, Kb } from '../schema.js';
|
||||
import { auth } from './system.js';
|
||||
|
||||
export const useAppRoute = (app) => {
|
||||
@@ -17,7 +17,7 @@ export const useAppRoute = (app) => {
|
||||
...(id && { _id: id })
|
||||
};
|
||||
|
||||
const modelsRaw = await Model.find(where)
|
||||
const modelsRaw = await App.find(where)
|
||||
.skip(start)
|
||||
.limit(end - start)
|
||||
.sort({ [sort]: order, 'share.isShare': -1, 'share.collection': -1 });
|
||||
@@ -50,7 +50,7 @@ export const useAppRoute = (app) => {
|
||||
|
||||
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('X-Total-Count', totalCount);
|
||||
res.json(models);
|
||||
@@ -70,7 +70,7 @@ export const useAppRoute = (app) => {
|
||||
intro
|
||||
} = req.body;
|
||||
|
||||
await Model.findByIdAndUpdate(_id, {
|
||||
await App.findByIdAndUpdate(_id, {
|
||||
$set: {
|
||||
intro: intro,
|
||||
'share.topNum': Number(topNum),
|
||||
|
@@ -56,7 +56,7 @@ const kbSchema = new mongoose.Schema({
|
||||
__v: Number
|
||||
});
|
||||
|
||||
const modelSchema = new mongoose.Schema({
|
||||
const appSchema = new mongoose.Schema({
|
||||
userId: mongoose.Schema.Types.ObjectId,
|
||||
name: 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 User = mongoose.models['user'] || mongoose.model('user', userSchema);
|
||||
export const Pay = mongoose.models['pay'] || mongoose.model('pay', paySchema);
|
||||
|
@@ -1,44 +1,44 @@
|
||||
import { GET, POST, DELETE, PUT } from './request';
|
||||
import type { AppSchema } from '@/types/mongoSchema';
|
||||
import type { ModelUpdateParams } from '@/types/model';
|
||||
import type { AppUpdateParams } from '@/types/app';
|
||||
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 删除模型
|
||||
*/
|
||||
export const delModelById = (id: string) => DELETE(`/model/del?modelId=${id}`);
|
||||
export const delModelById = (id: string) => DELETE(`/app/del?modelId=${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 更新模型
|
||||
*/
|
||||
export const putAppById = (id: string, data: ModelUpdateParams) =>
|
||||
PUT(`/model/update?appId=${id}`, data);
|
||||
export const putAppById = (id: string, data: AppUpdateParams) =>
|
||||
PUT(`/app/update?appId=${id}`, data);
|
||||
|
||||
/* 共享市场 */
|
||||
/**
|
||||
* 获取共享市场模型
|
||||
*/
|
||||
export const getShareModelList = (data: { searchText?: string } & RequestPaging) =>
|
||||
POST(`/model/share/getModels`, data);
|
||||
POST(`/app/share/getModels`, data);
|
||||
|
||||
/**
|
||||
* 收藏/取消收藏模型
|
||||
*/
|
||||
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 { RequestPaging } from '../types/index';
|
||||
import type { ShareChatSchema } from '@/types/mongoSchema';
|
||||
import type { ShareChatEditType } from '@/types/model';
|
||||
import type { ShareChatEditType } from '@/types/app';
|
||||
import { Obj2Query } from '@/utils/tools';
|
||||
import type { QuoteItemType } from '@/pages/api/openapi/kb/appKbSearch';
|
||||
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 } = {
|
||||
'/': true,
|
||||
'/login': true,
|
||||
'/model/share': true,
|
||||
'/appStore': true,
|
||||
'/chat/share': true
|
||||
};
|
||||
|
||||
|
@@ -32,12 +32,6 @@ const Navbar = ({ unread }: { unread: number }) => {
|
||||
link: `/app/list`,
|
||||
activeLink: ['/app/list']
|
||||
},
|
||||
{
|
||||
label: '旧应用',
|
||||
icon: 'model',
|
||||
link: `/model?modelId=${lastModelId}`,
|
||||
activeLink: ['/model']
|
||||
},
|
||||
{
|
||||
label: '知识库',
|
||||
icon: 'kb',
|
||||
@@ -47,8 +41,8 @@ const Navbar = ({ unread }: { unread: number }) => {
|
||||
{
|
||||
label: '市场',
|
||||
icon: 'appStore',
|
||||
link: '/model/share',
|
||||
activeLink: ['/model/share']
|
||||
link: '/appStore',
|
||||
activeLink: ['/appStore']
|
||||
},
|
||||
{
|
||||
label: '账号',
|
||||
@@ -57,7 +51,7 @@ const Navbar = ({ unread }: { unread: number }) => {
|
||||
activeLink: ['/number']
|
||||
}
|
||||
],
|
||||
[lastChatId, lastChatModelId, lastModelId]
|
||||
[lastChatId, lastChatModelId]
|
||||
);
|
||||
|
||||
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';
|
||||
|
||||
export const embeddingModel = 'text-embedding-ada-002';
|
||||
|
@@ -274,7 +274,7 @@ export const theme = extendTheme({
|
||||
borders: {
|
||||
sm: '1px solid #EFF0F1',
|
||||
base: '1px solid #DEE0E2',
|
||||
md: '1px solid #BDC1C5'
|
||||
md: '1px solid #DAE0E2'
|
||||
},
|
||||
breakpoints: {
|
||||
sm: '900px',
|
||||
|
@@ -3,7 +3,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
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>) {
|
||||
try {
|
||||
@@ -21,7 +21,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
await connectToDatabase();
|
||||
|
||||
// 上限校验
|
||||
const authCount = await Model.countDocuments({
|
||||
const authCount = await App.countDocuments({
|
||||
userId
|
||||
});
|
||||
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,
|
||||
userId
|
||||
});
|
@@ -1,6 +1,6 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
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 { 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,
|
||||
userId
|
||||
});
|
@@ -1,8 +1,8 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
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 type { ModelListResponse } from '@/api/response/model';
|
||||
import type { AppListResponse } from '@/api/response/app';
|
||||
|
||||
/* 获取模型列表 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
@@ -14,7 +14,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
|
||||
// 根据 userId 获取模型信息
|
||||
const [myApps, myCollections] = await Promise.all([
|
||||
Model.find(
|
||||
App.find(
|
||||
{
|
||||
userId
|
||||
},
|
||||
@@ -31,7 +31,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
.then((res) => res.filter((item) => item.modelId))
|
||||
]);
|
||||
|
||||
jsonRes<ModelListResponse>(res, {
|
||||
jsonRes<AppListResponse>(res, {
|
||||
data: {
|
||||
myApps: myApps.map((item) => ({
|
||||
_id: item._id,
|
@@ -1,6 +1,6 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
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';
|
||||
|
||||
/* 模型收藏切换 */
|
||||
@@ -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 })
|
||||
});
|
||||
|
@@ -1,8 +1,8 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase, Model } from '@/service/mongo';
|
||||
import { connectToDatabase, App } from '@/service/mongo';
|
||||
import type { PagingData } from '@/types';
|
||||
import type { ShareModelItem } from '@/types/model';
|
||||
import type { ShareAppItem } from '@/types/app';
|
||||
import { parseCookie } from '@/service/utils/auth';
|
||||
import { Types } from 'mongoose';
|
||||
|
||||
@@ -91,11 +91,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
// 获取被分享的模型
|
||||
const [models, total] = await Promise.all([
|
||||
// @ts-ignore
|
||||
Model.aggregate(pipeline),
|
||||
Model.countDocuments(where)
|
||||
App.aggregate(pipeline),
|
||||
App.countDocuments(where)
|
||||
]);
|
||||
|
||||
jsonRes<PagingData<ShareModelItem>>(res, {
|
||||
jsonRes<PagingData<ShareAppItem>>(res, {
|
||||
data: {
|
||||
pageNum,
|
||||
pageSize,
|
@@ -2,14 +2,14 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import { Model } from '@/service/models/model';
|
||||
import type { ModelUpdateParams } from '@/types/model';
|
||||
import { App } from '@/service/models/model';
|
||||
import type { AppUpdateParams } from '@/types/app';
|
||||
import { authApp } from '@/service/utils/auth';
|
||||
|
||||
/* 获取我的模型 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
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 };
|
||||
|
||||
if (!appId) {
|
||||
@@ -27,7 +27,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
});
|
||||
|
||||
// 更新模型
|
||||
await Model.updateOne(
|
||||
await App.updateOne(
|
||||
{
|
||||
_id: appId,
|
||||
userId
|
@@ -1,6 +1,6 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
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 { authUser } from '@/service/utils/auth';
|
||||
import { ChatItemType } from '@/types/chat';
|
||||
@@ -23,13 +23,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
// 没有 modelId 时,直接获取用户的第一个id
|
||||
const app = await (async () => {
|
||||
if (!modelId) {
|
||||
const myModel = await Model.findOne({ userId });
|
||||
const myModel = await App.findOne({ userId });
|
||||
if (!myModel) {
|
||||
const { _id } = await Model.create({
|
||||
const { _id } = await App.create({
|
||||
name: '应用1',
|
||||
userId
|
||||
});
|
||||
return (await Model.findById(_id)) as AppSchema;
|
||||
return (await App.findById(_id)) as AppSchema;
|
||||
} else {
|
||||
return myModel;
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
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 { authUser } from '@/service/utils/auth';
|
||||
import { Types } from 'mongoose';
|
||||
@@ -60,7 +60,7 @@ export async function saveChat({
|
||||
}));
|
||||
|
||||
if (String(app.userId) === userId) {
|
||||
await Model.findByIdAndUpdate(modelId, {
|
||||
await App.findByIdAndUpdate(modelId, {
|
||||
updateTime: new Date()
|
||||
});
|
||||
}
|
||||
@@ -96,7 +96,7 @@ export async function saveChat({
|
||||
// update app
|
||||
...(String(app.userId) === userId
|
||||
? [
|
||||
Model.findByIdAndUpdate(modelId, {
|
||||
App.findByIdAndUpdate(modelId, {
|
||||
updateTime: new Date()
|
||||
})
|
||||
]
|
||||
|
@@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase, ShareChat } from '@/service/mongo';
|
||||
import { authApp, authUser } from '@/service/utils/auth';
|
||||
import type { ShareChatEditType } from '@/types/model';
|
||||
import type { ShareChatEditType } from '@/types/app';
|
||||
|
||||
/* create a shareChat */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
|
@@ -117,6 +117,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
},
|
||||
stream
|
||||
});
|
||||
console.log(responseData, answerText);
|
||||
|
||||
// save chat
|
||||
if (typeof chatId === 'string') {
|
||||
@@ -282,14 +283,17 @@ async function dispatchModules({
|
||||
if (res.closed) return Promise.resolve();
|
||||
console.log('run=========', module.type, module.url);
|
||||
|
||||
// direct answer
|
||||
if (module.type === AppModuleItemTypeEnum.answer) {
|
||||
const text =
|
||||
module.inputs.find((item) => item.key === SpecificInputEnum.answerText)?.value || '';
|
||||
pushStore({
|
||||
answer: module.inputs.find((item) => item.key === SpecificInputEnum.answerText)?.value || ''
|
||||
answer: text
|
||||
});
|
||||
return StreamAnswer({
|
||||
res,
|
||||
stream,
|
||||
text: module.inputs.find((item) => item.key === SpecificInputEnum.answerText)?.value
|
||||
text: text
|
||||
});
|
||||
}
|
||||
|
||||
@@ -365,9 +369,9 @@ function StreamAnswer({
|
||||
}: {
|
||||
res: NextApiResponse;
|
||||
stream?: boolean;
|
||||
text?: '';
|
||||
text?: string;
|
||||
}) {
|
||||
if (stream) {
|
||||
if (stream && text) {
|
||||
return sseResponse({
|
||||
res,
|
||||
event: sseResponseEventEnum.answer,
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
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 { PgClient } from '@/service/pg';
|
||||
import { Types } from 'mongoose';
|
||||
@@ -32,7 +32,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
});
|
||||
|
||||
// delete related model
|
||||
await Model.updateMany(
|
||||
await App.updateMany(
|
||||
{
|
||||
userId
|
||||
},
|
||||
|
@@ -36,7 +36,7 @@ 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';
|
||||
import type { ShareChatEditType } from '@/types/app';
|
||||
|
||||
const Share = ({ modelId }: { modelId: string }) => {
|
||||
const { toast } = useToast();
|
||||
|
@@ -4,8 +4,8 @@ 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 Tabs from '@/components/Tabs';
|
||||
import Settings from './components/Settings';
|
||||
import { defaultApp } from '@/constants/model';
|
||||
|
||||
|
@@ -1,24 +1,81 @@
|
||||
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 { 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 theme = useTheme();
|
||||
const router = useRouter();
|
||||
const { myApps, loadMyModels } = useUserStore();
|
||||
|
||||
/* 加载模型 */
|
||||
useQuery(['loadModels'], () => loadMyModels(false));
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box
|
||||
className="textlg"
|
||||
borderBottom={theme.borders.base}
|
||||
letterSpacing={1}
|
||||
py={3}
|
||||
px={5}
|
||||
fontSize={'24px'}
|
||||
fontWeight={'bold'}
|
||||
onClick={() => router.push(`/app/detail?appId=642adec15f01d67d4613efdb`)}
|
||||
<Flex py={3} px={5} borderBottom={theme.borders.base} alignItems={'center'}>
|
||||
<Box flex={1} className="textlg" letterSpacing={1} fontSize={'24px'} fontWeight={'bold'}>
|
||||
我的应用
|
||||
</Box>
|
||||
<Button leftIcon={<AddIcon />} variant={'base'}>
|
||||
新建
|
||||
</Button>
|
||||
</Flex>
|
||||
<Grid
|
||||
p={5}
|
||||
gridTemplateColumns={['1fr', 'repeat(3,1fr)', 'repeat(4,1fr)', 'repeat(5,1fr)']}
|
||||
gridGap={5}
|
||||
>
|
||||
我的应用
|
||||
</Box>
|
||||
{myApps.map((app) => (
|
||||
<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>
|
||||
);
|
||||
};
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import React from '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 MyIcon from '@/components/Icon';
|
||||
import styles from '../index.module.scss';
|
||||
@@ -10,7 +10,7 @@ const ShareModelList = ({
|
||||
models = [],
|
||||
onclickCollection
|
||||
}: {
|
||||
models: ShareModelItem[];
|
||||
models: ShareAppItem[];
|
||||
onclickCollection: (modelId: string) => void;
|
||||
}) => {
|
||||
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 { getShareModelList, triggerModelCollection } from '@/api/app';
|
||||
import { usePagination } from '@/hooks/usePagination';
|
||||
import type { ShareModelItem } from '@/types/model';
|
||||
import type { ShareAppItem } from '@/types/app';
|
||||
import { useUserStore } from '@/store/user';
|
||||
import ShareModelList from './components/list';
|
||||
import styles from './index.module.scss';
|
||||
@@ -21,7 +21,7 @@ const modelList = () => {
|
||||
Pagination,
|
||||
getData,
|
||||
pageNum
|
||||
} = usePagination<ShareModelItem>({
|
||||
} = usePagination<ShareAppItem>({
|
||||
api: getShareModelList,
|
||||
pageSize: 24,
|
||||
params: {
|
@@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { ModelListItemType } from '@/types/model';
|
||||
import { AppListItemType } from '@/types/app';
|
||||
import Avatar from '@/components/Avatar';
|
||||
|
||||
const ModelList = ({ models, modelId }: { models: ModelListItemType[]; modelId: string }) => {
|
||||
const ModelList = ({ models, modelId }: { models: AppListItemType[]; modelId: string }) => {
|
||||
const router = useRouter();
|
||||
|
||||
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 { AppSchema as ModelType } from '@/types/mongoSchema';
|
||||
import { Schema, model, models, Model } from 'mongoose';
|
||||
import { AppSchema as AppType } from '@/types/mongoSchema';
|
||||
import { ChatModelMap, OpenAiChatEnum } from '@/constants/model';
|
||||
|
||||
const AppSchema = new Schema({
|
||||
@@ -105,4 +105,4 @@ try {
|
||||
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/chat';
|
||||
export * from './models/model';
|
||||
export * from './models/app';
|
||||
export * from './models/user';
|
||||
export * from './models/bill';
|
||||
export * from './models/pay';
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import type { NextApiRequest } from 'next';
|
||||
import jwt from 'jsonwebtoken';
|
||||
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 { ChatItemType } from '@/types/chat';
|
||||
import mongoose from 'mongoose';
|
||||
@@ -218,7 +218,7 @@ export const authApp = async ({
|
||||
reserveDetail?: boolean; // focus reserve detail
|
||||
}) => {
|
||||
// 获取 model 数据
|
||||
const app = await Model.findById<AppSchema>(appId);
|
||||
const app = await App.findById<AppSchema>(appId);
|
||||
if (!app) {
|
||||
return Promise.reject('模型不存在');
|
||||
}
|
||||
|
@@ -6,7 +6,7 @@ import { getMyModels, getModelById } from '@/api/app';
|
||||
import { formatPrice } from '@/utils/user';
|
||||
import { getTokenLogin } from '@/api/user';
|
||||
import { defaultApp } from '@/constants/model';
|
||||
import { ModelListItemType } from '@/types/model';
|
||||
import { AppListItemType } from '@/types/app';
|
||||
import { KbItemType } from '@/types/plugin';
|
||||
import { getKbList, getKbById } from '@/api/plugins/kb';
|
||||
import { defaultKbDetail } from '@/constants/kb';
|
||||
@@ -20,8 +20,8 @@ type State = {
|
||||
// model
|
||||
lastModelId: string;
|
||||
setLastModelId: (id: string) => void;
|
||||
myApps: ModelListItemType[];
|
||||
myCollectionApps: ModelListItemType[];
|
||||
myApps: AppListItemType[];
|
||||
myCollectionApps: AppListItemType[];
|
||||
loadMyModels: (init?: boolean) => Promise<null>;
|
||||
appDetail: 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 { AppModuleItemTypeEnum, ModulesInputItemTypeEnum } from '../constants/app';
|
||||
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 */
|
||||
/* 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[];
|
||||
}
|
||||
|
||||
export interface ModelPopulate extends AppSchema {
|
||||
userId: UserModelSchema;
|
||||
}
|
||||
|
||||
export interface CollectionSchema {
|
||||
modelId: string;
|
||||
userId: string;
|
||||
}
|
||||
|
||||
export type ModelDataType = 0 | 1;
|
||||
|
||||
export interface TrainingDataSchema {
|
||||
_id: string;
|
||||
userId: string;
|
||||
|
Reference in New Issue
Block a user