mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-22 12:20:34 +00:00
v4.1 (#183)
* chat item table * perf: chat item save * docs * limit * docs * docs * perf: node card * docs * docs
This commit is contained in:
29
README.md
29
README.md
@@ -27,14 +27,6 @@ FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开
|
||||
|  |  |
|
||||
|  |  |
|
||||
|
||||
## ⚡快速部署
|
||||
|
||||
> Sealos 的服务器在国外,不需要额外处理网络问题,无需服务器、无需魔法、无需域名,支持高并发 & 动态伸缩。点击以下按钮即可一键部署 👇
|
||||
|
||||
[](https://cloud.sealos.io/?openapp=system-fastdeploy%3FtemplateName%3Dfastgpt)
|
||||
|
||||
由于需要部署数据库,部署完后需要等待 2~4 分钟才能正常访问。默认用了最低配置,首次访问时会有些慢。
|
||||
|
||||
## 💡 功能
|
||||
|
||||
1. 强大的可视化编排,轻松构建 AI 应用
|
||||
@@ -76,12 +68,21 @@ FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开
|
||||
|
||||
项目技术栈: NextJs + TS + ChakraUI + Mongo + Postgres(Vector 插件)
|
||||
|
||||
- [快开始本地开发](https://doc.fastgpt.run/docs/develop/dev)
|
||||
- [部署 FastGPT](https://doc.fastgpt.run/docs/category/deploy)
|
||||
- [系统配置文件说明](https://doc.fastgpt.run/docs/category/data-config)
|
||||
- [多模型配置](https://doc.fastgpt.run/docs/develop/data_config/chat_models)
|
||||
- [V3 升级 V4 初始化](https://doc.fastgpt.run/docs/develop/deploy/v4init)
|
||||
- [API 文档](https://kjqvjse66l.feishu.cn/docx/DmLedTWtUoNGX8xui9ocdUEjnNh?pre_pathname=%2Fdrive%2Fhome%2F)
|
||||
- **⚡ 快速部署**
|
||||
|
||||
> Sealos 的服务器在国外,不需要额外处理网络问题,无需服务器、无需魔法、无需域名,支持高并发 & 动态伸缩。点击以下按钮即可一键部署 👇
|
||||
|
||||
[](https://cloud.sealos.io/?openapp=system-fastdeploy%3FtemplateName%3Dfastgpt)
|
||||
|
||||
由于需要部署数据库,部署完后需要等待 2~4 分钟才能正常访问。默认用了最低配置,首次访问时会有些慢。
|
||||
|
||||
* [快开始本地开发](https://doc.fastgpt.run/docs/develop/dev)
|
||||
* [部署 FastGPT](https://doc.fastgpt.run/docs/category/deploy)
|
||||
* [系统配置文件说明](https://doc.fastgpt.run/docs/category/data-config)
|
||||
* [多模型配置](https://doc.fastgpt.run/docs/develop/data_config/chat_models)
|
||||
* [V3 升级 V4 初始化](https://doc.fastgpt.run/docs/develop/deploy/v4init)
|
||||
* [升级 v4.1 初始化](https://doc.fastgpt.run/docs/develop/deploy/initv4.1)
|
||||
* [API 文档](https://kjqvjse66l.feishu.cn/docx/DmLedTWtUoNGX8xui9ocdUEjnNh?pre_pathname=%2Fdrive%2Fhome%2F)
|
||||
|
||||
## 🏘️ 社区交流群
|
||||
|
||||
|
@@ -11,6 +11,7 @@
|
||||
"Confirm Save App Tip": "The application may be in advanced orchestration mode, and the advanced orchestration configuration will be overwritten after saving, please confirm!",
|
||||
"Connection is invalid": "Connecting is invalid",
|
||||
"Connection type is different": "Connection type is different",
|
||||
"Copy Module Config": "Copy config",
|
||||
"Export Config Successful": "The configuration has been copied. Please check for important data",
|
||||
"Export Configs": "Export Configs",
|
||||
"Import Config": "Import Config",
|
||||
|
@@ -11,6 +11,7 @@
|
||||
"Confirm Save App Tip": "该应用可能为高级编排模式,保存后将会覆盖高级编排配置,请确认!",
|
||||
"Connection is invalid": "连接无效",
|
||||
"Connection type is different": "连接的类型不一致",
|
||||
"Copy Module Config": "复制配置",
|
||||
"Export Config Successful": "已复制配置,请注意检查是否有重要数据",
|
||||
"Export Configs": "导出配置",
|
||||
"Import Config": "导入配置",
|
||||
|
@@ -27,20 +27,10 @@ export const delChatHistoryById = (chatId: string) => DELETE(`/chat/removeHistor
|
||||
*/
|
||||
export const clearChatHistoryByAppId = (appId: string) => DELETE(`/chat/removeHistory`, { appId });
|
||||
|
||||
/**
|
||||
* update history quote status
|
||||
*/
|
||||
export const updateHistoryQuote = (params: {
|
||||
chatId: string;
|
||||
contentId: string;
|
||||
quoteId: string;
|
||||
sourceText: string;
|
||||
}) => PUT(`/chat/history/updateHistoryQuote`, params);
|
||||
|
||||
/**
|
||||
* 删除一句对话
|
||||
*/
|
||||
export const delChatRecordByIndex = (data: { chatId: string; contentId: string }) =>
|
||||
export const delChatRecordById = (data: { chatId: string; contentId: string }) =>
|
||||
DELETE(`/chat/delChatRecordByContentId`, data);
|
||||
|
||||
/**
|
||||
|
@@ -40,6 +40,7 @@ import { useRouter } from 'next/router';
|
||||
import { useGlobalStore } from '@/store/global';
|
||||
import { TaskResponseKeyEnum, getDefaultChatVariables } from '@/constants/chat';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
|
||||
import MyIcon from '@/components/Icon';
|
||||
import Avatar from '@/components/Avatar';
|
||||
@@ -51,6 +52,8 @@ const ResponseTags = dynamic(() => import('./ResponseTags'));
|
||||
|
||||
import styles from './index.module.scss';
|
||||
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 24);
|
||||
|
||||
const textareaMinH = '22px';
|
||||
type generatingMessageProps = { text?: string; name?: string; status?: 'running' | 'finish' };
|
||||
export type StartChatFnProps = {
|
||||
@@ -282,13 +285,13 @@ const ChatBox = (
|
||||
const newChatList: ChatSiteItemType[] = [
|
||||
...chatHistory,
|
||||
{
|
||||
_id: String(new Types.ObjectId()),
|
||||
dataId: nanoid(),
|
||||
obj: 'Human',
|
||||
value: val,
|
||||
status: 'finish'
|
||||
},
|
||||
{
|
||||
_id: String(new Types.ObjectId()),
|
||||
dataId: nanoid(),
|
||||
obj: 'AI',
|
||||
value: '',
|
||||
status: 'loading'
|
||||
@@ -552,7 +555,7 @@ const ChatBox = (
|
||||
{chatHistory.map((item, index) => (
|
||||
<Flex
|
||||
position={'relative'}
|
||||
key={item._id}
|
||||
key={item.dataId}
|
||||
flexDirection={'column'}
|
||||
alignItems={item.obj === 'Human' ? 'flex-end' : 'flex-start'}
|
||||
py={5}
|
||||
@@ -583,10 +586,10 @@ const ChatBox = (
|
||||
_hover={{ color: 'red.600' }}
|
||||
onClick={() => {
|
||||
setChatHistory((state) =>
|
||||
state.filter((chat) => chat._id !== item._id)
|
||||
state.filter((chat) => chat.dataId !== item.dataId)
|
||||
);
|
||||
onDelMessage({
|
||||
contentId: item._id,
|
||||
contentId: item.dataId,
|
||||
index
|
||||
});
|
||||
}}
|
||||
@@ -630,10 +633,10 @@ const ChatBox = (
|
||||
_hover={{ color: 'red.600' }}
|
||||
onClick={() => {
|
||||
setChatHistory((state) =>
|
||||
state.filter((chat) => chat._id !== item._id)
|
||||
state.filter((chat) => chat.dataId !== item.dataId)
|
||||
);
|
||||
onDelMessage({
|
||||
contentId: item._id,
|
||||
contentId: item.dataId,
|
||||
index
|
||||
});
|
||||
}}
|
||||
@@ -682,7 +685,7 @@ const ChatBox = (
|
||||
/>
|
||||
<ResponseTags
|
||||
chatId={chatId}
|
||||
contentId={item._id}
|
||||
contentId={item.dataId}
|
||||
responseData={item.responseData}
|
||||
/>
|
||||
</Card>
|
||||
|
@@ -45,9 +45,7 @@ async function init(limit: number, skip: number) {
|
||||
chatId: { $exists: false }
|
||||
},
|
||||
'_id'
|
||||
)
|
||||
.limit(limit)
|
||||
.skip(skip);
|
||||
).limit(limit);
|
||||
|
||||
await Promise.all(
|
||||
chats.map((chat) =>
|
||||
|
98
client/src/pages/api/admin/initChatItem.ts
Normal file
98
client/src/pages/api/admin/initChatItem.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import { connectToDatabase, Chat, ChatItem } from '@/service/mongo';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 24);
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await authUser({ req, authRoot: true });
|
||||
await connectToDatabase();
|
||||
|
||||
const { limit = 100 } = req.body as { limit: number };
|
||||
let skip = 0;
|
||||
|
||||
const total = await Chat.countDocuments({
|
||||
content: { $exists: true, $not: { $size: 0 } },
|
||||
isInit: { $ne: true }
|
||||
});
|
||||
const totalChat = await Chat.aggregate([
|
||||
{
|
||||
$project: {
|
||||
contentLength: { $size: '$content' }
|
||||
}
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: null,
|
||||
totalLength: { $sum: '$contentLength' }
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
console.log('chatLen:', total, totalChat);
|
||||
|
||||
let promise = Promise.resolve();
|
||||
|
||||
for (let i = 0; i < total; i += limit) {
|
||||
const skipVal = skip;
|
||||
skip += limit;
|
||||
promise = promise
|
||||
.then(() => init(limit))
|
||||
.then(() => {
|
||||
console.log(skipVal);
|
||||
});
|
||||
}
|
||||
|
||||
await promise;
|
||||
|
||||
jsonRes(res, {});
|
||||
} catch (error) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function init(limit: number) {
|
||||
// 遍历 app
|
||||
const chats = await Chat.find(
|
||||
{
|
||||
content: { $exists: true, $not: { $size: 0 } },
|
||||
isInit: { $ne: true }
|
||||
},
|
||||
'_id userId appId chatId content'
|
||||
)
|
||||
.sort({ updateTime: -1 })
|
||||
.limit(limit);
|
||||
|
||||
await Promise.all(
|
||||
chats.map(async (chat) => {
|
||||
const inserts = chat.content
|
||||
.map((item) => ({
|
||||
dataId: nanoid(),
|
||||
chatId: chat.chatId,
|
||||
userId: chat.userId,
|
||||
appId: chat.appId,
|
||||
obj: item.obj,
|
||||
value: item.value,
|
||||
responseData: item.responseData
|
||||
}))
|
||||
.filter((item) => item.chatId && item.userId && item.appId && item.obj && item.value);
|
||||
|
||||
try {
|
||||
await Promise.all(inserts.map((item) => ChatItem.create(item)));
|
||||
await Chat.findByIdAndUpdate(chat._id, {
|
||||
isInit: true
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
await ChatItem.deleteMany({ chatId: chat.chatId });
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
@@ -408,9 +408,7 @@ async function init(limit: number, skip: number) {
|
||||
// userId: '63f9a14228d2a688d8dc9e1b'
|
||||
},
|
||||
'_id chat'
|
||||
)
|
||||
.limit(limit)
|
||||
.skip(skip);
|
||||
).limit(limit);
|
||||
|
||||
return Promise.all(
|
||||
apps.map(async (app) => {
|
||||
|
@@ -1,35 +1,23 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase, Chat } from '@/service/mongo';
|
||||
import { connectToDatabase, ChatItem } from '@/service/mongo';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const { chatId, contentId } = req.query as { chatId: string; contentId: string };
|
||||
|
||||
if (!chatId || !contentId) {
|
||||
throw new Error('缺少参数');
|
||||
}
|
||||
|
||||
await connectToDatabase();
|
||||
|
||||
// 凭证校验
|
||||
const { userId } = await authUser({ req, authToken: true });
|
||||
|
||||
const chatRecord = await Chat.findOne({ chatId });
|
||||
|
||||
if (!chatRecord) {
|
||||
throw new Error('找不到对话');
|
||||
}
|
||||
|
||||
// 删除一条数据库记录
|
||||
await Chat.updateOne(
|
||||
{
|
||||
await ChatItem.deleteOne({
|
||||
dataId: contentId,
|
||||
chatId,
|
||||
userId
|
||||
},
|
||||
{ $pull: { content: { _id: contentId } } }
|
||||
);
|
||||
});
|
||||
|
||||
jsonRes(res);
|
||||
} catch (err) {
|
||||
|
@@ -1,57 +0,0 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase, Chat } from '@/service/mongo';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import { Types } from 'mongoose';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
let {
|
||||
chatId,
|
||||
contentId,
|
||||
quoteId,
|
||||
sourceText = ''
|
||||
} = req.body as {
|
||||
chatId: string;
|
||||
contentId: string;
|
||||
quoteId: string;
|
||||
sourceText: string;
|
||||
};
|
||||
await connectToDatabase();
|
||||
|
||||
const { userId } = await authUser({ req, authToken: true });
|
||||
|
||||
if (!contentId || !chatId || !quoteId) {
|
||||
throw new Error('params is error');
|
||||
}
|
||||
|
||||
await Chat.updateOne(
|
||||
{
|
||||
chatId,
|
||||
userId: new Types.ObjectId(userId),
|
||||
'content._id': new Types.ObjectId(contentId)
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
'content.$.rawSearch.$[quoteElem].source': sourceText
|
||||
}
|
||||
},
|
||||
{
|
||||
arrayFilters: [
|
||||
{
|
||||
'quoteElem.id': quoteId
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
|
||||
jsonRes(res, {
|
||||
data: ''
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
@@ -1,11 +1,10 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase, Chat } from '@/service/mongo';
|
||||
import { Chat, ChatItem } from '@/service/mongo';
|
||||
import type { InitChatResponse } from '@/api/response/chat';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import { ChatItemType } from '@/types/chat';
|
||||
import { authApp } from '@/service/utils/auth';
|
||||
import mongoose from 'mongoose';
|
||||
import type { ChatSchema } from '@/types/mongoSchema';
|
||||
import { getSpecialModule, getChatModelNameList } from '@/components/ChatBox/utils';
|
||||
import { TaskResponseKeyEnum } from '@/constants/chat';
|
||||
@@ -27,8 +26,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
});
|
||||
}
|
||||
|
||||
await connectToDatabase();
|
||||
|
||||
// 校验使用权限
|
||||
const app = (
|
||||
await authApp({
|
||||
@@ -39,49 +36,42 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
})
|
||||
).app;
|
||||
|
||||
// 历史记录
|
||||
// get app and history
|
||||
const { chat, history = [] }: { chat?: ChatSchema; history?: ChatItemType[] } =
|
||||
await (async () => {
|
||||
if (chatId) {
|
||||
// auth chatId
|
||||
const [chat, history] = await Promise.all([
|
||||
Chat.findOne({
|
||||
Chat.findOne(
|
||||
{
|
||||
chatId,
|
||||
userId
|
||||
}),
|
||||
Chat.aggregate([
|
||||
},
|
||||
'title variables'
|
||||
),
|
||||
ChatItem.find(
|
||||
{
|
||||
$match: {
|
||||
chatId,
|
||||
userId: new mongoose.Types.ObjectId(userId)
|
||||
}
|
||||
userId
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
content: {
|
||||
$slice: ['$content', -30] // 返回 content 数组的最后 30 个元素
|
||||
}
|
||||
}
|
||||
},
|
||||
{ $unwind: '$content' },
|
||||
{
|
||||
$project: {
|
||||
_id: '$content._id',
|
||||
obj: '$content.obj',
|
||||
value: '$content.value',
|
||||
[TaskResponseKeyEnum.responseData]: `$content.${TaskResponseKeyEnum.responseData}`
|
||||
}
|
||||
}
|
||||
])
|
||||
`dataId obj value ${TaskResponseKeyEnum.responseData}`
|
||||
)
|
||||
.sort({ _id: -1 })
|
||||
.limit(30)
|
||||
]);
|
||||
if (!chat) {
|
||||
throw new Error('聊天框不存在');
|
||||
}
|
||||
return { history, chat };
|
||||
history.reverse();
|
||||
return { app, history, chat };
|
||||
}
|
||||
return {};
|
||||
})();
|
||||
|
||||
if (!app) {
|
||||
throw new Error('Auth App Error');
|
||||
}
|
||||
|
||||
const isOwner = String(app.userId) === userId;
|
||||
|
||||
jsonRes<InitChatResponse>(res, {
|
||||
@@ -108,3 +98,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const config = {
|
||||
api: {
|
||||
bodyParser: {
|
||||
sizeLimit: '10mb'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase, Chat } from '@/service/mongo';
|
||||
import { connectToDatabase, Chat, ChatItem } from '@/service/mongo';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
|
||||
type Props = {
|
||||
@@ -17,16 +17,28 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
await connectToDatabase();
|
||||
|
||||
if (chatId) {
|
||||
await Chat.findOneAndRemove({
|
||||
await Promise.all([
|
||||
Chat.findOneAndRemove({
|
||||
chatId,
|
||||
userId
|
||||
});
|
||||
}),
|
||||
ChatItem.deleteMany({
|
||||
userId,
|
||||
chatId
|
||||
})
|
||||
]);
|
||||
}
|
||||
if (appId) {
|
||||
await Chat.deleteMany({
|
||||
await Promise.all([
|
||||
Chat.deleteMany({
|
||||
appId,
|
||||
userId
|
||||
});
|
||||
}),
|
||||
ChatItem.deleteMany({
|
||||
userId,
|
||||
appId
|
||||
})
|
||||
]);
|
||||
}
|
||||
|
||||
jsonRes(res);
|
||||
|
@@ -28,7 +28,7 @@ import { BillSourceEnum } from '@/constants/user';
|
||||
import { ChatHistoryItemResType } from '@/types/chat';
|
||||
import { UserModelSchema } from '@/types/mongoSchema';
|
||||
|
||||
export type MessageItemType = ChatCompletionRequestMessage & { _id?: string };
|
||||
export type MessageItemType = ChatCompletionRequestMessage & { dataId?: string };
|
||||
type FastGptWebChatProps = {
|
||||
chatId?: string; // undefined: nonuse history, '': new chat, 'xxxxx': use history
|
||||
appId?: string;
|
||||
@@ -172,7 +172,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
content: [
|
||||
prompt,
|
||||
{
|
||||
_id: messages[messages.length - 1]._id,
|
||||
dataId: messages[messages.length - 1].dataId,
|
||||
obj: ChatRoleEnum.AI,
|
||||
value: answerText,
|
||||
responseData
|
||||
|
@@ -2,10 +2,9 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import { connectToDatabase, Chat } from '@/service/mongo';
|
||||
import { connectToDatabase, ChatItem } from '@/service/mongo';
|
||||
import { Types } from 'mongoose';
|
||||
import type { ChatItemType } from '@/types/chat';
|
||||
import { TaskResponseKeyEnum } from '@/constants/chat';
|
||||
|
||||
export type Props = {
|
||||
chatId?: string;
|
||||
@@ -37,30 +36,37 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
export async function getChatHistory({
|
||||
chatId,
|
||||
userId,
|
||||
limit = 20
|
||||
limit = 30
|
||||
}: Props & { userId: string }): Promise<Response> {
|
||||
if (!chatId) {
|
||||
return { history: [] };
|
||||
}
|
||||
|
||||
const history = await Chat.aggregate([
|
||||
{ $match: { chatId, userId: new Types.ObjectId(userId) } },
|
||||
const history = await ChatItem.aggregate([
|
||||
{
|
||||
$project: {
|
||||
content: {
|
||||
$slice: ['$content', -limit] // 返回 content 数组的最后20个元素
|
||||
}
|
||||
$match: {
|
||||
chatId,
|
||||
userId: new Types.ObjectId(userId)
|
||||
}
|
||||
},
|
||||
{ $unwind: '$content' },
|
||||
{
|
||||
$sort: {
|
||||
_id: -1
|
||||
}
|
||||
},
|
||||
{
|
||||
$limit: limit
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
obj: '$content.obj',
|
||||
value: '$content.value',
|
||||
[TaskResponseKeyEnum.responseData]: `$content.responseData`
|
||||
dataId: 1,
|
||||
obj: 1,
|
||||
value: 1
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
history.reverse();
|
||||
|
||||
return { history };
|
||||
}
|
||||
|
@@ -2,15 +2,13 @@ import React from 'react';
|
||||
import { NodeProps } from 'reactflow';
|
||||
import NodeCard from '../modules/NodeCard';
|
||||
import { FlowModuleItemType } from '@/types/flow';
|
||||
import Divider from '../modules/Divider';
|
||||
import Container from '../modules/Container';
|
||||
import RenderInput from '../render/RenderInput';
|
||||
|
||||
const NodeAnswer = ({
|
||||
data: { moduleId, inputs, outputs, onChangeNode, ...props }
|
||||
}: NodeProps<FlowModuleItemType>) => {
|
||||
const NodeAnswer = ({ data }: NodeProps<FlowModuleItemType>) => {
|
||||
const { moduleId, inputs, outputs, onChangeNode } = data;
|
||||
return (
|
||||
<NodeCard minW={'400px'} moduleId={moduleId} {...props}>
|
||||
<NodeCard minW={'400px'} {...data}>
|
||||
<Container borderTop={'2px solid'} borderTopColor={'myGray.200'}>
|
||||
<RenderInput moduleId={moduleId} onChangeNode={onChangeNode} flowInputList={inputs} />
|
||||
</Container>
|
||||
|
@@ -13,11 +13,10 @@ import MyIcon from '@/components/Icon';
|
||||
import { FlowOutputItemTypeEnum, FlowValueTypeEnum, SpecialInputKeyEnum } from '@/constants/flow';
|
||||
import SourceHandle from '../render/SourceHandle';
|
||||
|
||||
const NodeCQNode = ({
|
||||
data: { moduleId, inputs, outputs, onChangeNode, ...props }
|
||||
}: NodeProps<FlowModuleItemType>) => {
|
||||
const NodeCQNode = ({ data }: NodeProps<FlowModuleItemType>) => {
|
||||
const { moduleId, inputs, outputs, onChangeNode } = data;
|
||||
return (
|
||||
<NodeCard minW={'400px'} moduleId={moduleId} {...props}>
|
||||
<NodeCard minW={'400px'} {...data}>
|
||||
<Divider text="Input" />
|
||||
<Container>
|
||||
<RenderInput
|
||||
|
@@ -13,16 +13,15 @@ import MySlider from '@/components/Slider';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { formatPrice } from '@/utils/user';
|
||||
|
||||
const NodeChat = ({
|
||||
data: { moduleId, inputs, outputs, onChangeNode, ...props }
|
||||
}: NodeProps<FlowModuleItemType>) => {
|
||||
const NodeChat = ({ data }: NodeProps<FlowModuleItemType>) => {
|
||||
const { moduleId, inputs, outputs, onChangeNode } = data;
|
||||
const outputsLen = useMemo(
|
||||
() => outputs.filter((item) => item.type !== FlowOutputItemTypeEnum.hidden).length,
|
||||
[outputs]
|
||||
);
|
||||
|
||||
return (
|
||||
<NodeCard minW={'400px'} moduleId={moduleId} {...props}>
|
||||
<NodeCard minW={'400px'} {...data}>
|
||||
<Divider text="Input" />
|
||||
<Container>
|
||||
<RenderInput
|
||||
|
@@ -3,7 +3,7 @@ import { NodeProps } from 'reactflow';
|
||||
import NodeCard from '../modules/NodeCard';
|
||||
import { FlowModuleItemType } from '@/types/flow';
|
||||
|
||||
const NodeAnswer = ({ data: { ...props } }: NodeProps<FlowModuleItemType>) => {
|
||||
return <NodeCard {...props}></NodeCard>;
|
||||
const NodeAnswer = ({ data }: NodeProps<FlowModuleItemType>) => {
|
||||
return <NodeCard {...data}></NodeCard>;
|
||||
};
|
||||
export default React.memo(NodeAnswer);
|
||||
|
@@ -15,14 +15,13 @@ import ExtractFieldModal from '../modules/ExtractFieldModal';
|
||||
import { ContextExtractEnum } from '@/constants/flow/flowField';
|
||||
import { FlowOutputItemTypeEnum, FlowValueTypeEnum } from '@/constants/flow';
|
||||
|
||||
const NodeExtract = ({
|
||||
data: { inputs, outputs, moduleId, onChangeNode, onDelEdge, ...props }
|
||||
}: NodeProps<FlowModuleItemType>) => {
|
||||
const NodeExtract = ({ data }: NodeProps<FlowModuleItemType>) => {
|
||||
const { inputs, outputs, moduleId, onChangeNode, onDelEdge } = data;
|
||||
const { t } = useTranslation();
|
||||
const [editExtractFiled, setEditExtractField] = useState<ContextExtractAgentItemType>();
|
||||
|
||||
return (
|
||||
<NodeCard minW={'400px'} moduleId={moduleId} {...props}>
|
||||
<NodeCard minW={'400px'} {...data}>
|
||||
<Divider text="Input" />
|
||||
<Container>
|
||||
<RenderInput
|
||||
|
@@ -7,22 +7,17 @@ import Container from '../modules/Container';
|
||||
import RenderInput from '../render/RenderInput';
|
||||
import RenderOutput from '../render/RenderOutput';
|
||||
|
||||
const NodeHistory = ({
|
||||
data: { inputs, outputs, onChangeNode, ...props }
|
||||
}: NodeProps<FlowModuleItemType>) => {
|
||||
const NodeHistory = ({ data }: NodeProps<FlowModuleItemType>) => {
|
||||
const { inputs, outputs, moduleId, onChangeNode } = data;
|
||||
return (
|
||||
<NodeCard minW={'300px'} {...props}>
|
||||
<NodeCard minW={'300px'} {...data}>
|
||||
<Divider text="Input" />
|
||||
<Container>
|
||||
<RenderInput moduleId={props.moduleId} onChangeNode={onChangeNode} flowInputList={inputs} />
|
||||
<RenderInput moduleId={moduleId} onChangeNode={onChangeNode} flowInputList={inputs} />
|
||||
</Container>
|
||||
<Divider text="Output" />
|
||||
<Container>
|
||||
<RenderOutput
|
||||
onChangeNode={onChangeNode}
|
||||
moduleId={props.moduleId}
|
||||
flowOutputList={outputs}
|
||||
/>
|
||||
<RenderOutput onChangeNode={onChangeNode} moduleId={moduleId} flowOutputList={outputs} />
|
||||
</Container>
|
||||
</NodeCard>
|
||||
);
|
||||
|
@@ -13,11 +13,10 @@ import { FlowInputItemTypeEnum, FlowOutputItemTypeEnum, FlowValueTypeEnum } from
|
||||
import { customAlphabet } from 'nanoid';
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6);
|
||||
|
||||
const NodeHttp = ({
|
||||
data: { moduleId, inputs, outputs, onChangeNode, ...props }
|
||||
}: NodeProps<FlowModuleItemType>) => {
|
||||
const NodeHttp = ({ data }: NodeProps<FlowModuleItemType>) => {
|
||||
const { moduleId, inputs, outputs, onChangeNode } = data;
|
||||
return (
|
||||
<NodeCard minW={'350px'} moduleId={moduleId} {...props}>
|
||||
<NodeCard minW={'350px'} {...data}>
|
||||
<Container borderTop={'2px solid'} borderTopColor={'myGray.200'}>
|
||||
<RenderInput moduleId={moduleId} onChangeNode={onChangeNode} flowInputList={inputs} />
|
||||
<Button
|
||||
|
@@ -69,11 +69,10 @@ const KBSelect = ({
|
||||
);
|
||||
};
|
||||
|
||||
const NodeKbSearch = ({
|
||||
data: { moduleId, inputs, outputs, onChangeNode, ...props }
|
||||
}: NodeProps<FlowModuleItemType>) => {
|
||||
const NodeKbSearch = ({ data }: NodeProps<FlowModuleItemType>) => {
|
||||
const { moduleId, inputs, outputs, onChangeNode } = data;
|
||||
return (
|
||||
<NodeCard minW={'400px'} moduleId={moduleId} {...props}>
|
||||
<NodeCard minW={'400px'} {...data}>
|
||||
<Divider text="Input" />
|
||||
<Container>
|
||||
<RenderInput
|
||||
|
@@ -8,11 +8,9 @@ import { SystemInputEnum } from '@/constants/app';
|
||||
import { FlowValueTypeEnum } from '@/constants/flow';
|
||||
import SourceHandle from '../render/SourceHandle';
|
||||
|
||||
const QuestionInputNode = ({
|
||||
data: { inputs, outputs, ...props }
|
||||
}: NodeProps<FlowModuleItemType>) => {
|
||||
const QuestionInputNode = ({ data }: NodeProps<FlowModuleItemType>) => {
|
||||
return (
|
||||
<NodeCard minW={'240px'} {...props}>
|
||||
<NodeCard minW={'240px'} {...data}>
|
||||
<Container borderTop={'2px solid'} borderTopColor={'myGray.200'} textAlign={'end'}>
|
||||
<Box position={'relative'}>
|
||||
用户问题
|
||||
|
@@ -8,9 +8,9 @@ import Divider from '../modules/Divider';
|
||||
import Container from '../modules/Container';
|
||||
import Label from '../modules/Label';
|
||||
|
||||
const NodeTFSwitch = ({ data: { inputs, outputs, ...props } }: NodeProps<FlowModuleItemType>) => {
|
||||
const NodeTFSwitch = ({ data }: NodeProps<FlowModuleItemType>) => {
|
||||
return (
|
||||
<NodeCard minW={'220px'} {...props}>
|
||||
<NodeCard minW={'220px'} {...data}>
|
||||
<Divider text="输入输出" />
|
||||
<Container h={'100px'} py={0} px={0} display={'flex'} alignItems={'center'}>
|
||||
<Box flex={1} pl={'12px'}>
|
||||
|
@@ -10,9 +10,8 @@ import MyIcon from '@/components/Icon';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import { welcomeTextTip } from '@/constants/flow/ModuleTemplate';
|
||||
|
||||
const NodeUserGuide = ({
|
||||
data: { inputs, outputs, onChangeNode, ...props }
|
||||
}: NodeProps<FlowModuleItemType>) => {
|
||||
const NodeUserGuide = ({ data }: NodeProps<FlowModuleItemType>) => {
|
||||
const { inputs, moduleId, onChangeNode } = data;
|
||||
const welcomeText = useMemo(
|
||||
() => inputs.find((item) => item.key === SystemInputEnum.welcomeText),
|
||||
[inputs]
|
||||
@@ -20,7 +19,7 @@ const NodeUserGuide = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<NodeCard minW={'300px'} {...props}>
|
||||
<NodeCard minW={'300px'} {...data}>
|
||||
<Container borderTop={'2px solid'} borderTopColor={'myGray.200'}>
|
||||
<>
|
||||
<Flex mb={1} alignItems={'center'}>
|
||||
@@ -40,7 +39,7 @@ const NodeUserGuide = ({
|
||||
placeholder={welcomeTextTip}
|
||||
onChange={(e) => {
|
||||
onChangeNode({
|
||||
moduleId: props.moduleId,
|
||||
moduleId,
|
||||
key: SystemInputEnum.welcomeText,
|
||||
type: 'inputs',
|
||||
value: {
|
||||
|
@@ -22,9 +22,8 @@ export const defaultVariable: VariableItemType = {
|
||||
enums: [{ value: '' }]
|
||||
};
|
||||
|
||||
const NodeUserGuide = ({
|
||||
data: { inputs, outputs, onChangeNode, ...props }
|
||||
}: NodeProps<FlowModuleItemType>) => {
|
||||
const NodeUserGuide = ({ data }: NodeProps<FlowModuleItemType>) => {
|
||||
const { inputs, moduleId, onChangeNode } = data;
|
||||
const variables = useMemo(
|
||||
() =>
|
||||
(inputs.find((item) => item.key === SystemInputEnum.variables)
|
||||
@@ -37,7 +36,7 @@ const NodeUserGuide = ({
|
||||
const updateVariables = useCallback(
|
||||
(value: VariableItemType[]) => {
|
||||
onChangeNode({
|
||||
moduleId: props.moduleId,
|
||||
moduleId,
|
||||
key: SystemInputEnum.variables,
|
||||
type: 'inputs',
|
||||
value: {
|
||||
@@ -46,7 +45,7 @@ const NodeUserGuide = ({
|
||||
}
|
||||
});
|
||||
},
|
||||
[inputs, onChangeNode, props.moduleId]
|
||||
[inputs, onChangeNode, moduleId]
|
||||
);
|
||||
|
||||
const onclickSubmit = useCallback(
|
||||
@@ -59,7 +58,7 @@ const NodeUserGuide = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<NodeCard minW={'300px'} {...props}>
|
||||
<NodeCard minW={'300px'} {...data}>
|
||||
<Container borderTop={'2px solid'} borderTopColor={'myGray.200'}>
|
||||
<TableContainer>
|
||||
<Table>
|
||||
|
@@ -6,20 +6,15 @@ import type { FlowModuleItemType } from '@/types/flow';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useCopyData } from '@/utils/tools';
|
||||
|
||||
type Props = {
|
||||
type Props = FlowModuleItemType & {
|
||||
children?: React.ReactNode | React.ReactNode[] | string;
|
||||
logo: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
intro: string;
|
||||
minW?: string | number;
|
||||
moduleId: string;
|
||||
onDelNode: FlowModuleItemType['onDelNode'];
|
||||
onCopyNode: FlowModuleItemType['onCopyNode'];
|
||||
};
|
||||
|
||||
const NodeCard = ({
|
||||
const NodeCard = (props: Props) => {
|
||||
const {
|
||||
children,
|
||||
logo = '/icon/logo.svg',
|
||||
name = '未知模块',
|
||||
@@ -28,7 +23,8 @@ const NodeCard = ({
|
||||
onCopyNode,
|
||||
onDelNode,
|
||||
moduleId
|
||||
}: Props) => {
|
||||
} = props;
|
||||
const { copyData } = useCopyData();
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
|
||||
@@ -39,16 +35,22 @@ const NodeCard = ({
|
||||
label: t('common.Copy'),
|
||||
onClick: () => onCopyNode(moduleId)
|
||||
},
|
||||
// {
|
||||
// icon: 'settingLight',
|
||||
// label: t('app.Copy Module Config'),
|
||||
// onClick: () => {
|
||||
// const copyProps = { ...props };
|
||||
// delete copyProps.children;
|
||||
// delete copyProps.children;
|
||||
// console.log(copyProps);
|
||||
// }
|
||||
// },
|
||||
{
|
||||
icon: 'delete',
|
||||
label: t('common.Delete'),
|
||||
onClick: () => onDelNode(moduleId)
|
||||
},
|
||||
// {
|
||||
// icon: 'collectionLight',
|
||||
// label: t('common.Collect'),
|
||||
// onClick: () => {}
|
||||
// },
|
||||
|
||||
{
|
||||
icon: 'back',
|
||||
label: t('common.Cancel'),
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import React, { useCallback, useRef } from 'react';
|
||||
import Head from 'next/head';
|
||||
import { useRouter } from 'next/router';
|
||||
import { getInitChatSiteInfo, delChatRecordByIndex, putChatHistory } from '@/api/chat';
|
||||
import { getInitChatSiteInfo, delChatRecordById, putChatHistory } from '@/api/chat';
|
||||
import {
|
||||
Box,
|
||||
Flex,
|
||||
@@ -128,7 +128,7 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
||||
...state,
|
||||
history: state.history.filter((_, i) => i !== index)
|
||||
}));
|
||||
await delChatRecordByIndex({ chatId, contentId });
|
||||
await delChatRecordById({ chatId, contentId });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
@@ -147,10 +147,6 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
onClick={onclickGit}
|
||||
/>
|
||||
</Flex>
|
||||
<Box mt={3} textAlign={'center'} fontSize={'sm'} color={'myGray.600'}>
|
||||
由于 Git 登录设计缺陷,我们重新设计了 Git 登录,已使用 Git
|
||||
注册的账号将会丢失关联,如果你希望重新绑定原来的账号,可以点击右下角的【联系方式】,我们会手动为你重新绑定。不需要重新关联的,将会注册新的账号。
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</form>
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { Schema, model, models, Model } from 'mongoose';
|
||||
import { ChatSchema as ChatType } from '@/types/mongoSchema';
|
||||
import { ChatRoleMap, TaskResponseKeyEnum } from '@/constants/chat';
|
||||
import { ChatSourceEnum, ChatSourceMap } from '@/constants/chat';
|
||||
import { ChatSourceMap } from '@/constants/chat';
|
||||
|
||||
const ChatSchema = new Schema({
|
||||
chatId: {
|
||||
@@ -45,6 +45,10 @@ const ChatSchema = new Schema({
|
||||
shareId: {
|
||||
type: String
|
||||
},
|
||||
isInit: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
content: {
|
||||
type: [
|
||||
{
|
||||
|
72
client/src/service/models/chatItem.ts
Normal file
72
client/src/service/models/chatItem.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { Schema, model, models, Model } from 'mongoose';
|
||||
import { ChatItemSchema as ChatItemType } from '@/types/mongoSchema';
|
||||
import { ChatRoleMap, TaskResponseKeyEnum } from '@/constants/chat';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 24);
|
||||
|
||||
const ChatItemSchema = new Schema({
|
||||
dataId: {
|
||||
type: String,
|
||||
require: true,
|
||||
default: () => nanoid()
|
||||
},
|
||||
chatId: {
|
||||
type: String,
|
||||
require: true
|
||||
},
|
||||
userId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'user',
|
||||
required: true
|
||||
},
|
||||
appId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'model',
|
||||
required: true
|
||||
},
|
||||
time: {
|
||||
type: Date,
|
||||
default: () => new Date()
|
||||
},
|
||||
obj: {
|
||||
type: String,
|
||||
required: true,
|
||||
enum: Object.keys(ChatRoleMap)
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
[TaskResponseKeyEnum.responseData]: {
|
||||
type: [
|
||||
{
|
||||
moduleName: String,
|
||||
price: String,
|
||||
model: String,
|
||||
tokens: Number,
|
||||
question: String,
|
||||
answer: String,
|
||||
temperature: Number,
|
||||
maxToken: Number,
|
||||
quoteList: Array,
|
||||
completeMessages: Array,
|
||||
similarity: Number,
|
||||
limit: Number,
|
||||
cqList: Array,
|
||||
cqResult: String
|
||||
}
|
||||
],
|
||||
default: []
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
ChatItemSchema.index({ time: -1 });
|
||||
ChatItemSchema.index({ userId: 1 });
|
||||
ChatItemSchema.index({ appId: 1 });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
export const ChatItem: Model<ChatItemType> =
|
||||
models['chatItem'] || model('chatItem', ChatItemSchema);
|
@@ -63,7 +63,6 @@ export const dispatchChatCompletion = async (props: Record<string, any>): Promis
|
||||
}
|
||||
|
||||
const { filterQuoteQA, quotePrompt } = filterQuote({
|
||||
history,
|
||||
quoteQA,
|
||||
model: modelConstantsData
|
||||
});
|
||||
@@ -91,6 +90,7 @@ export const dispatchChatCompletion = async (props: Record<string, any>): Promis
|
||||
maxToken,
|
||||
filterMessages
|
||||
});
|
||||
// console.log(messages);
|
||||
|
||||
// FastGpt temperature range: 1~10
|
||||
temperature = +(modelConstantsData.maxTemperature * (temperature / 10)).toFixed(2);
|
||||
@@ -182,32 +182,23 @@ export const dispatchChatCompletion = async (props: Record<string, any>): Promis
|
||||
};
|
||||
|
||||
function filterQuote({
|
||||
history = [],
|
||||
quoteQA = [],
|
||||
model
|
||||
}: {
|
||||
history: ChatProps['history'];
|
||||
quoteQA: ChatProps['quoteQA'];
|
||||
model: ChatModelItemType;
|
||||
}) {
|
||||
// concat history quote
|
||||
const historyQuote =
|
||||
history[history.length - 1]?.responseData
|
||||
?.find((item) => item.moduleName === ChatModuleEnum.AIChat)
|
||||
?.quoteList?.filter((item) => !quoteQA.find((quote) => quote.id === item.id)) || [];
|
||||
const concatQuote = quoteQA.concat(historyQuote.slice(0, 3));
|
||||
|
||||
const sliceResult = modelToolMap.tokenSlice({
|
||||
model: model.model,
|
||||
maxToken: model.quoteMaxToken,
|
||||
messages: concatQuote.map((item, i) => ({
|
||||
messages: quoteQA.map((item) => ({
|
||||
obj: ChatRoleEnum.System,
|
||||
value: item.a ? `{instruction:${item.q},output:${item.a}}` : `{instruction:${item.q}}`
|
||||
}))
|
||||
});
|
||||
|
||||
// slice filterSearch
|
||||
const filterQuoteQA = concatQuote.slice(0, sliceResult.length);
|
||||
const filterQuoteQA = quoteQA.slice(0, sliceResult.length);
|
||||
|
||||
const quotePrompt =
|
||||
filterQuoteQA.length > 0
|
||||
|
@@ -113,6 +113,7 @@ async function initPg() {
|
||||
}
|
||||
|
||||
export * from './models/chat';
|
||||
export * from './models/chatItem';
|
||||
export * from './models/app';
|
||||
export * from './models/user';
|
||||
export * from './models/bill';
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { ChatItemType } from '@/types/chat';
|
||||
import { Chat, App } from '@/service/mongo';
|
||||
import { Chat, App, ChatItem } from '@/service/mongo';
|
||||
import { ChatSourceEnum } from '@/constants/chat';
|
||||
|
||||
type Props = {
|
||||
@@ -32,26 +32,30 @@ export async function saveChat({
|
||||
'_id'
|
||||
);
|
||||
|
||||
const promise = [];
|
||||
const promise: any[] = [
|
||||
ChatItem.insertMany(
|
||||
content.map((item) => ({
|
||||
chatId,
|
||||
userId,
|
||||
appId,
|
||||
...item
|
||||
}))
|
||||
)
|
||||
];
|
||||
|
||||
if (chatHistory) {
|
||||
promise.push(
|
||||
promise.push([
|
||||
Chat.updateOne(
|
||||
{ chatId, userId },
|
||||
{
|
||||
$push: {
|
||||
content: {
|
||||
$each: content,
|
||||
$slice: -40
|
||||
}
|
||||
},
|
||||
title: content[0].value.slice(0, 20),
|
||||
updateTime: new Date()
|
||||
}
|
||||
)
|
||||
);
|
||||
]);
|
||||
} else {
|
||||
promise.push(
|
||||
...[
|
||||
Chat.create({
|
||||
chatId,
|
||||
userId,
|
||||
@@ -59,9 +63,9 @@ export async function saveChat({
|
||||
variables,
|
||||
title: content[0].value.slice(0, 20),
|
||||
source,
|
||||
shareId,
|
||||
content: content
|
||||
shareId
|
||||
})
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
|
2
client/src/types/chat.d.ts
vendored
2
client/src/types/chat.d.ts
vendored
@@ -6,7 +6,7 @@ import { ClassifyQuestionAgentItemType } from './app';
|
||||
export type ExportChatType = 'md' | 'pdf' | 'html';
|
||||
|
||||
export type ChatItemType = {
|
||||
_id?: string;
|
||||
dataId?: string;
|
||||
obj: `${ChatRoleEnum}`;
|
||||
value: string;
|
||||
[TaskResponseKeyEnum.responseData]?: ChatHistoryItemResType[];
|
||||
|
2
client/src/types/flow.d.ts
vendored
2
client/src/types/flow.d.ts
vendored
@@ -53,9 +53,9 @@ export type FlowModuleTemplateType = {
|
||||
description?: string;
|
||||
intro: string;
|
||||
flowType: `${FlowModuleTypeEnum}`;
|
||||
showStatus?: boolean;
|
||||
inputs: FlowInputItemType[];
|
||||
outputs: FlowOutputItemType[];
|
||||
showStatus?: boolean;
|
||||
};
|
||||
export type FlowModuleItemType = FlowModuleTemplateType & {
|
||||
moduleId: string;
|
||||
|
9
client/src/types/mongoSchema.d.ts
vendored
9
client/src/types/mongoSchema.d.ts
vendored
@@ -92,9 +92,18 @@ export interface ChatSchema {
|
||||
variables: Record<string, any>;
|
||||
source: `${ChatSourceEnum}`;
|
||||
shareId?: string;
|
||||
isInit: boolean;
|
||||
content: ChatItemType[];
|
||||
}
|
||||
|
||||
export interface ChatItemSchema extends ChatItemType {
|
||||
dataId: string;
|
||||
chatId: string;
|
||||
userId: string;
|
||||
appId: string;
|
||||
time: Date;
|
||||
}
|
||||
|
||||
export type BillListItemType = {
|
||||
moduleName: string;
|
||||
amount: number;
|
||||
|
@@ -33,7 +33,7 @@ export const gptMessage2ChatType = (messages: MessageItemType[]): ChatItemType[]
|
||||
};
|
||||
|
||||
return messages.map((item) => ({
|
||||
_id: item._id,
|
||||
dataId: item.dataId,
|
||||
obj: roleMap[item.role],
|
||||
value: item.content || ''
|
||||
}));
|
||||
|
@@ -41,7 +41,7 @@ export const adaptChatItem_openAI = ({
|
||||
[ChatRoleEnum.System]: ChatCompletionRequestMessageRoleEnum.System
|
||||
};
|
||||
return messages.map((item) => ({
|
||||
...(reserveId && { _id: item._id }),
|
||||
...(reserveId && { dataId: item.dataId }),
|
||||
role: map[item.obj] || ChatCompletionRequestMessageRoleEnum.System,
|
||||
content: item.value || ''
|
||||
}));
|
||||
|
@@ -1,7 +1,3 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
# Config Chat Model
|
||||
|
||||
By default, FastGPT is only configured with 3 models of GPT. If you need to integrate other models, you need to do some additional configuration.
|
||||
|
72
docSite/docs/develop/data_config/default.md
Normal file
72
docSite/docs/develop/data_config/default.md
Normal file
@@ -0,0 +1,72 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
# Default Config.json
|
||||
|
||||
```json
|
||||
{
|
||||
"FeConfig": {
|
||||
"show_emptyChat": true,
|
||||
"show_register": false,
|
||||
"show_appStore": false,
|
||||
"show_userDetail": false,
|
||||
"show_git": true,
|
||||
"systemTitle": "FastGPT",
|
||||
"authorText": "Made by FastGPT Team.",
|
||||
"gitLoginKey": "",
|
||||
"scripts": []
|
||||
},
|
||||
"SystemParams": {
|
||||
"gitLoginSecret": "",
|
||||
"vectorMaxProcess": 15,
|
||||
"qaMaxProcess": 15,
|
||||
"pgIvfflatProbe": 20
|
||||
},
|
||||
"plugins": {},
|
||||
"ChatModels": [
|
||||
{
|
||||
"model": "gpt-3.5-turbo",
|
||||
"name": "GPT35-4k",
|
||||
"contextMaxToken": 4000,
|
||||
"quoteMaxToken": 2000,
|
||||
"maxTemperature": 1.2,
|
||||
"price": 0,
|
||||
"defaultSystem": ""
|
||||
},
|
||||
{
|
||||
"model": "gpt-3.5-turbo-16k",
|
||||
"name": "GPT35-16k",
|
||||
"contextMaxToken": 16000,
|
||||
"quoteMaxToken": 8000,
|
||||
"maxTemperature": 1.2,
|
||||
"price": 0,
|
||||
"defaultSystem": ""
|
||||
},
|
||||
{
|
||||
"model": "gpt-4",
|
||||
"name": "GPT4-8k",
|
||||
"contextMaxToken": 8000,
|
||||
"quoteMaxToken": 4000,
|
||||
"maxTemperature": 1.2,
|
||||
"price": 0,
|
||||
"defaultSystem": ""
|
||||
}
|
||||
],
|
||||
"QAModels": [
|
||||
{
|
||||
"model": "gpt-3.5-turbo-16k",
|
||||
"name": "GPT35-16k",
|
||||
"maxToken": 16000,
|
||||
"price": 0
|
||||
}
|
||||
],
|
||||
"VectorModels": [
|
||||
{
|
||||
"model": "text-embedding-ada-002",
|
||||
"name": "Embedding-2",
|
||||
"price": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
@@ -10,6 +10,8 @@ In the development environment, you need to make a copy of `config.json` as `con
|
||||
|
||||
This configuration file contains customization of the frontend page, system-level parameters, and AI dialogue models, etc.
|
||||
|
||||
**Note: The configuration instructions below are only a partial introduction. You need to mount the entire config.json file and not just a part of it. You can directly modify the provided config.json file based on the instructions below.**
|
||||
|
||||
## Brief Explanation of Basic Fields
|
||||
|
||||
Here are some basic configuration fields.
|
||||
|
@@ -70,7 +70,7 @@ services:
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
# image: c121914yu/fast-gpt:latest # docker hub
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:latest # 阿里云
|
||||
image: ghcr.io/labring/fastgpt:latest # 阿里云
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
@@ -89,7 +89,8 @@ services:
|
||||
- TOKEN_KEY=any
|
||||
- ROOT_KEY=root_key
|
||||
# mongo 配置,不需要改
|
||||
- MONGODB_URI=mongodb://username:password@mongo:27017/?authSource=admin
|
||||
- MONGODB_URI=mongodb://username:password@mongo:27017
|
||||
# - MONGODB_URI=mongodb://username:password@mongo:27017/?authSource=admin
|
||||
- MONGODB_NAME=fastgpt
|
||||
# pg配置.
|
||||
- PG_HOST=pg
|
||||
@@ -105,42 +106,38 @@ networks:
|
||||
# host 版本, 不推荐。
|
||||
version: '3.3'
|
||||
services:
|
||||
pg:
|
||||
image: ankane/pgvector:v0.4.2 # dockerhub
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:v0.4.2 # 阿里云
|
||||
container_name: pg
|
||||
restart: always
|
||||
ports: # 生产环境建议不要暴露
|
||||
pg:
|
||||
image: ankane/pgvector:v0.4.2 # dockerhub
|
||||
container_name: pg
|
||||
restart: always
|
||||
ports: # 生产环境建议不要暴露
|
||||
- 5432:5432
|
||||
environment:
|
||||
environment:
|
||||
# 这里的配置只有首次运行生效。修改后,重启镜像是不会生效的。需要把持久化数据删除再重启,才有效果
|
||||
- POSTGRES_USER=username
|
||||
- POSTGRES_PASSWORD=password
|
||||
- POSTGRES_DB=postgres
|
||||
volumes:
|
||||
volumes:
|
||||
- ./pg/data:/var/lib/postgresql/data
|
||||
mongo:
|
||||
image: mongo:5.0.18
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/mongo:5.0.18 # 阿里云
|
||||
container_name: mongo
|
||||
restart: always
|
||||
ports: # 生产环境建议不要暴露
|
||||
mongo:
|
||||
image: mongo:5.0.18
|
||||
container_name: mongo
|
||||
restart: always
|
||||
ports: # 生产环境建议不要暴露
|
||||
- 27017:27017
|
||||
environment:
|
||||
environment:
|
||||
# 这里的配置只有首次运行生效。修改后,重启镜像是不会生效的。需要把持久化数据删除再重启,才有效果
|
||||
- MONGO_INITDB_ROOT_USERNAME=username
|
||||
- MONGO_INITDB_ROOT_PASSWORD=password
|
||||
volumes:
|
||||
volumes:
|
||||
- ./mongo/data:/data/db
|
||||
- ./mongo/logs:/var/log/mongodb
|
||||
fastgpt:
|
||||
# image: ghcr.io/c121914yu/fastgpt:latest # github
|
||||
# image: c121914yu/fast-gpt:latest # docker hub
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:latest # 阿里云
|
||||
network_mode: host
|
||||
restart: always
|
||||
container_name: fastgpt
|
||||
environment:
|
||||
fastgpt:
|
||||
image: ghcr.io/c121914yu/fastgpt:latest # github
|
||||
network_mode: host
|
||||
restart: always
|
||||
container_name: fastgpt
|
||||
environment:
|
||||
# root 密码,用户名为: root
|
||||
- DEFAULT_ROOT_PSW=1234
|
||||
# 中转地址,如果是用官方号,不需要管
|
||||
@@ -152,7 +149,8 @@ environment:
|
||||
# root key, 最高权限,可以内部接口互相调用
|
||||
- ROOT_KEY=root_key
|
||||
# mongo 配置,不需要改
|
||||
- MONGODB_URI=mongodb://username:password@0.0.0.0:27017/?authSource=admin
|
||||
- MONGODB_URI=mongodb://username:password@0.0.0.0:27017
|
||||
# - MONGODB_URI=mongodb://username:password@0.0.0.0:27017/?authSource=admin
|
||||
- MONGODB_NAME=fastgpt
|
||||
# pg 配置
|
||||
- PG_HOST=0.0.0.0
|
||||
|
9
docSite/docs/develop/deploy/initv4.1.md
Normal file
9
docSite/docs/develop/deploy/initv4.1.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# V4.1 版本初始化
|
||||
|
||||
新版重新设置了对话存储结构,需要初始化原来的存储内容
|
||||
|
||||
## 执行初始化 API
|
||||
|
||||
部署新版项目,并发起 3 个 HTTP 请求(记得携带 headers.rootkey,这个值是环境变量里的)
|
||||
|
||||
https://xxxxx/api/admin/initChatItem
|
@@ -1,7 +1,3 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
# 配置其他对话模型
|
||||
|
||||
默认情况下,FastGPT 只配置了 GPT 的 3 个模型,如果你需要接入其他模型,需要进行一些额外配置。
|
||||
|
@@ -0,0 +1,72 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
# 默认配置文件
|
||||
|
||||
```json
|
||||
{
|
||||
"FeConfig": {
|
||||
"show_emptyChat": true,
|
||||
"show_register": false,
|
||||
"show_appStore": false,
|
||||
"show_userDetail": false,
|
||||
"show_git": true,
|
||||
"systemTitle": "FastGPT",
|
||||
"authorText": "Made by FastGPT Team.",
|
||||
"gitLoginKey": "",
|
||||
"scripts": []
|
||||
},
|
||||
"SystemParams": {
|
||||
"gitLoginSecret": "",
|
||||
"vectorMaxProcess": 15,
|
||||
"qaMaxProcess": 15,
|
||||
"pgIvfflatProbe": 20
|
||||
},
|
||||
"plugins": {},
|
||||
"ChatModels": [
|
||||
{
|
||||
"model": "gpt-3.5-turbo",
|
||||
"name": "GPT35-4k",
|
||||
"contextMaxToken": 4000,
|
||||
"quoteMaxToken": 2000,
|
||||
"maxTemperature": 1.2,
|
||||
"price": 0,
|
||||
"defaultSystem": ""
|
||||
},
|
||||
{
|
||||
"model": "gpt-3.5-turbo-16k",
|
||||
"name": "GPT35-16k",
|
||||
"contextMaxToken": 16000,
|
||||
"quoteMaxToken": 8000,
|
||||
"maxTemperature": 1.2,
|
||||
"price": 0,
|
||||
"defaultSystem": ""
|
||||
},
|
||||
{
|
||||
"model": "gpt-4",
|
||||
"name": "GPT4-8k",
|
||||
"contextMaxToken": 8000,
|
||||
"quoteMaxToken": 4000,
|
||||
"maxTemperature": 1.2,
|
||||
"price": 0,
|
||||
"defaultSystem": ""
|
||||
}
|
||||
],
|
||||
"QAModels": [
|
||||
{
|
||||
"model": "gpt-3.5-turbo-16k",
|
||||
"name": "GPT35-16k",
|
||||
"maxToken": 16000,
|
||||
"price": 0
|
||||
}
|
||||
],
|
||||
"VectorModels": [
|
||||
{
|
||||
"model": "text-embedding-ada-002",
|
||||
"name": "Embedding-2",
|
||||
"price": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
@@ -1,5 +1,5 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
# 快速介绍
|
||||
@@ -10,6 +10,8 @@ sidebar_position: 1
|
||||
|
||||
这个配置文件中包含了前端页面定制、系统级参数、AI 对话的模型等……
|
||||
|
||||
**注意:下面的配置介绍仅是局部介绍,你需要完整挂载整个 config.jso ,不能仅挂载一部分。你可以直接在给的 config.json 基础上根据下面的介绍进行修改。**
|
||||
|
||||
## 基础字段粗略说明
|
||||
|
||||
这里会介绍一些基础的配置字段。
|
||||
|
@@ -37,7 +37,7 @@ docker-compose -v
|
||||
version: '3.3'
|
||||
services:
|
||||
pg:
|
||||
image: ankane/pgvector:v0.4.2 # git
|
||||
image: ankane/pgvector:v0.4.2 # docker
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:v0.4.2 # 阿里云
|
||||
container_name: pg
|
||||
restart: always
|
||||
@@ -70,7 +70,7 @@ services:
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
# image: c121914yu/fast-gpt:latest # docker hub
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:latest # 阿里云
|
||||
image: ghcr.io/labring/fastgpt:latest # 阿里云
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
@@ -89,7 +89,8 @@ services:
|
||||
- TOKEN_KEY=any
|
||||
- ROOT_KEY=root_key
|
||||
# mongo 配置,不需要改
|
||||
- MONGODB_URI=mongodb://username:password@mongo:27017/?authSource=admin
|
||||
- MONGODB_URI=mongodb://username:password@mongo:27017 # 如果这个连不上,尝试下面的
|
||||
# - MONGODB_URI=mongodb://username:password@mongo:27017/?authSource=admin
|
||||
- MONGODB_NAME=fastgpt
|
||||
# pg配置.
|
||||
- PG_HOST=pg
|
||||
@@ -136,7 +137,7 @@ volumes:
|
||||
fastgpt:
|
||||
# image: ghcr.io/c121914yu/fastgpt:latest # github
|
||||
# image: c121914yu/fast-gpt:latest # docker hub
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:latest # 阿里云
|
||||
image: ghcr.io/labring/fastgpt:latest # 阿里云
|
||||
network_mode: host
|
||||
restart: always
|
||||
container_name: fastgpt
|
||||
@@ -152,7 +153,8 @@ environment:
|
||||
# root key, 最高权限,可以内部接口互相调用
|
||||
- ROOT_KEY=root_key
|
||||
# mongo 配置,不需要改
|
||||
- MONGODB_URI=mongodb://username:password@0.0.0.0:27017/?authSource=admin
|
||||
- MONGODB_URI=mongodb://username:password@0.0.0.0:27017
|
||||
# - MONGODB_URI=mongodb://username:password@0.0.0.0:27017/?authSource=admin
|
||||
- MONGODB_NAME=fastgpt
|
||||
# pg 配置
|
||||
- PG_HOST=0.0.0.0
|
||||
|
@@ -0,0 +1,9 @@
|
||||
# V4.1 版本初始化
|
||||
|
||||
新版重新设置了对话存储结构,需要初始化原来的存储内容
|
||||
|
||||
## 执行初始化 API
|
||||
|
||||
部署新版项目,并发起 3 个 HTTP 请求(记得携带 headers.rootkey,这个值是环境变量里的)
|
||||
|
||||
https://xxxxx/api/admin/initChatItem
|
Reference in New Issue
Block a user