* chat item table

* perf: chat item save

* docs

* limit

* docs

* docs

* perf: node card

* docs

* docs
This commit is contained in:
Archer
2023-08-17 16:57:22 +08:00
committed by GitHub
parent ce61ac3fac
commit 324e4a0e75
49 changed files with 617 additions and 359 deletions

View File

@@ -27,14 +27,6 @@ FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开
| ![Demo](./.github/imgs/intro1.png) | ![Demo](./.github/imgs/intro2.png) | | ![Demo](./.github/imgs/intro1.png) | ![Demo](./.github/imgs/intro2.png) |
| ![Demo](./.github/imgs/intro3.png) | ![Demo](./.github/imgs/intro4.png) | | ![Demo](./.github/imgs/intro3.png) | ![Demo](./.github/imgs/intro4.png) |
## ⚡快速部署
> Sealos 的服务器在国外,不需要额外处理网络问题,无需服务器、无需魔法、无需域名,支持高并发 & 动态伸缩。点击以下按钮即可一键部署 👇
[![](https://cdn.jsdelivr.us/gh/labring-actions/templates@main/Deploy-on-Sealos.svg)](https://cloud.sealos.io/?openapp=system-fastdeploy%3FtemplateName%3Dfastgpt)
由于需要部署数据库,部署完后需要等待 2~4 分钟才能正常访问。默认用了最低配置,首次访问时会有些慢。
## 💡 功能 ## 💡 功能
1. 强大的可视化编排,轻松构建 AI 应用 1. 强大的可视化编排,轻松构建 AI 应用
@@ -76,12 +68,21 @@ FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开
项目技术栈: NextJs + TS + ChakraUI + Mongo + PostgresVector 插件) 项目技术栈: NextJs + TS + ChakraUI + Mongo + PostgresVector 插件)
- [快开始本地开发](https://doc.fastgpt.run/docs/develop/dev) - **⚡ 快速部署**
- [部署 FastGPT](https://doc.fastgpt.run/docs/category/deploy)
- [系统配置文件说明](https://doc.fastgpt.run/docs/category/data-config) > Sealos 的服务器在国外,不需要额外处理网络问题,无需服务器、无需魔法、无需域名,支持高并发 & 动态伸缩。点击以下按钮即可一键部署 👇
- [多模型配置](https://doc.fastgpt.run/docs/develop/data_config/chat_models)
- [V3 升级 V4 初始化](https://doc.fastgpt.run/docs/develop/deploy/v4init) [![](https://cdn.jsdelivr.us/gh/labring-actions/templates@main/Deploy-on-Sealos.svg)](https://cloud.sealos.io/?openapp=system-fastdeploy%3FtemplateName%3Dfastgpt)
- [API 文档](https://kjqvjse66l.feishu.cn/docx/DmLedTWtUoNGX8xui9ocdUEjnNh?pre_pathname=%2Fdrive%2Fhome%2F)
由于需要部署数据库,部署完后需要等待 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)
## 🏘️ 社区交流群 ## 🏘️ 社区交流群

View File

@@ -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!", "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 is invalid": "Connecting is invalid",
"Connection type is different": "Connection type is different", "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 Config Successful": "The configuration has been copied. Please check for important data",
"Export Configs": "Export Configs", "Export Configs": "Export Configs",
"Import Config": "Import Config", "Import Config": "Import Config",

View File

@@ -11,6 +11,7 @@
"Confirm Save App Tip": "该应用可能为高级编排模式,保存后将会覆盖高级编排配置,请确认!", "Confirm Save App Tip": "该应用可能为高级编排模式,保存后将会覆盖高级编排配置,请确认!",
"Connection is invalid": "连接无效", "Connection is invalid": "连接无效",
"Connection type is different": "连接的类型不一致", "Connection type is different": "连接的类型不一致",
"Copy Module Config": "复制配置",
"Export Config Successful": "已复制配置,请注意检查是否有重要数据", "Export Config Successful": "已复制配置,请注意检查是否有重要数据",
"Export Configs": "导出配置", "Export Configs": "导出配置",
"Import Config": "导入配置", "Import Config": "导入配置",

View File

@@ -27,20 +27,10 @@ export const delChatHistoryById = (chatId: string) => DELETE(`/chat/removeHistor
*/ */
export const clearChatHistoryByAppId = (appId: string) => DELETE(`/chat/removeHistory`, { appId }); 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); DELETE(`/chat/delChatRecordByContentId`, data);
/** /**

View File

@@ -40,6 +40,7 @@ import { useRouter } from 'next/router';
import { useGlobalStore } from '@/store/global'; import { useGlobalStore } from '@/store/global';
import { TaskResponseKeyEnum, getDefaultChatVariables } from '@/constants/chat'; import { TaskResponseKeyEnum, getDefaultChatVariables } from '@/constants/chat';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { customAlphabet } from 'nanoid';
import MyIcon from '@/components/Icon'; import MyIcon from '@/components/Icon';
import Avatar from '@/components/Avatar'; import Avatar from '@/components/Avatar';
@@ -51,6 +52,8 @@ const ResponseTags = dynamic(() => import('./ResponseTags'));
import styles from './index.module.scss'; import styles from './index.module.scss';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 24);
const textareaMinH = '22px'; const textareaMinH = '22px';
type generatingMessageProps = { text?: string; name?: string; status?: 'running' | 'finish' }; type generatingMessageProps = { text?: string; name?: string; status?: 'running' | 'finish' };
export type StartChatFnProps = { export type StartChatFnProps = {
@@ -282,13 +285,13 @@ const ChatBox = (
const newChatList: ChatSiteItemType[] = [ const newChatList: ChatSiteItemType[] = [
...chatHistory, ...chatHistory,
{ {
_id: String(new Types.ObjectId()), dataId: nanoid(),
obj: 'Human', obj: 'Human',
value: val, value: val,
status: 'finish' status: 'finish'
}, },
{ {
_id: String(new Types.ObjectId()), dataId: nanoid(),
obj: 'AI', obj: 'AI',
value: '', value: '',
status: 'loading' status: 'loading'
@@ -552,7 +555,7 @@ const ChatBox = (
{chatHistory.map((item, index) => ( {chatHistory.map((item, index) => (
<Flex <Flex
position={'relative'} position={'relative'}
key={item._id} key={item.dataId}
flexDirection={'column'} flexDirection={'column'}
alignItems={item.obj === 'Human' ? 'flex-end' : 'flex-start'} alignItems={item.obj === 'Human' ? 'flex-end' : 'flex-start'}
py={5} py={5}
@@ -583,10 +586,10 @@ const ChatBox = (
_hover={{ color: 'red.600' }} _hover={{ color: 'red.600' }}
onClick={() => { onClick={() => {
setChatHistory((state) => setChatHistory((state) =>
state.filter((chat) => chat._id !== item._id) state.filter((chat) => chat.dataId !== item.dataId)
); );
onDelMessage({ onDelMessage({
contentId: item._id, contentId: item.dataId,
index index
}); });
}} }}
@@ -630,10 +633,10 @@ const ChatBox = (
_hover={{ color: 'red.600' }} _hover={{ color: 'red.600' }}
onClick={() => { onClick={() => {
setChatHistory((state) => setChatHistory((state) =>
state.filter((chat) => chat._id !== item._id) state.filter((chat) => chat.dataId !== item.dataId)
); );
onDelMessage({ onDelMessage({
contentId: item._id, contentId: item.dataId,
index index
}); });
}} }}
@@ -682,7 +685,7 @@ const ChatBox = (
/> />
<ResponseTags <ResponseTags
chatId={chatId} chatId={chatId}
contentId={item._id} contentId={item.dataId}
responseData={item.responseData} responseData={item.responseData}
/> />
</Card> </Card>

View File

@@ -45,9 +45,7 @@ async function init(limit: number, skip: number) {
chatId: { $exists: false } chatId: { $exists: false }
}, },
'_id' '_id'
) ).limit(limit);
.limit(limit)
.skip(skip);
await Promise.all( await Promise.all(
chats.map((chat) => chats.map((chat) =>

View 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 });
}
})
);
}

View File

@@ -408,9 +408,7 @@ async function init(limit: number, skip: number) {
// userId: '63f9a14228d2a688d8dc9e1b' // userId: '63f9a14228d2a688d8dc9e1b'
}, },
'_id chat' '_id chat'
) ).limit(limit);
.limit(limit)
.skip(skip);
return Promise.all( return Promise.all(
apps.map(async (app) => { apps.map(async (app) => {

View File

@@ -1,35 +1,23 @@
import type { NextApiRequest, NextApiResponse } from 'next'; import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response'; import { jsonRes } from '@/service/response';
import { connectToDatabase, Chat } from '@/service/mongo'; import { connectToDatabase, ChatItem } from '@/service/mongo';
import { authUser } from '@/service/utils/auth'; import { authUser } from '@/service/utils/auth';
export default async function handler(req: NextApiRequest, res: NextApiResponse) { export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try { try {
const { chatId, contentId } = req.query as { chatId: string; contentId: string }; const { chatId, contentId } = req.query as { chatId: string; contentId: string };
if (!chatId || !contentId) {
throw new Error('缺少参数');
}
await connectToDatabase(); await connectToDatabase();
// 凭证校验 // 凭证校验
const { userId } = await authUser({ req, authToken: true }); 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, chatId,
userId userId
}, });
{ $pull: { content: { _id: contentId } } }
);
jsonRes(res); jsonRes(res);
} catch (err) { } catch (err) {

View File

@@ -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
});
}
}

View File

@@ -1,11 +1,10 @@
import type { NextApiRequest, NextApiResponse } from 'next'; import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response'; import { jsonRes } from '@/service/response';
import { connectToDatabase, Chat } from '@/service/mongo'; import { Chat, ChatItem } from '@/service/mongo';
import type { InitChatResponse } from '@/api/response/chat'; import type { InitChatResponse } from '@/api/response/chat';
import { authUser } from '@/service/utils/auth'; import { authUser } from '@/service/utils/auth';
import { ChatItemType } from '@/types/chat'; import { ChatItemType } from '@/types/chat';
import { authApp } from '@/service/utils/auth'; import { authApp } from '@/service/utils/auth';
import mongoose from 'mongoose';
import type { ChatSchema } from '@/types/mongoSchema'; import type { ChatSchema } from '@/types/mongoSchema';
import { getSpecialModule, getChatModelNameList } from '@/components/ChatBox/utils'; import { getSpecialModule, getChatModelNameList } from '@/components/ChatBox/utils';
import { TaskResponseKeyEnum } from '@/constants/chat'; import { TaskResponseKeyEnum } from '@/constants/chat';
@@ -27,8 +26,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
}); });
} }
await connectToDatabase();
// 校验使用权限 // 校验使用权限
const app = ( const app = (
await authApp({ await authApp({
@@ -39,49 +36,42 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
}) })
).app; ).app;
// 历史记录 // get app and history
const { chat, history = [] }: { chat?: ChatSchema; history?: ChatItemType[] } = const { chat, history = [] }: { chat?: ChatSchema; history?: ChatItemType[] } =
await (async () => { await (async () => {
if (chatId) { if (chatId) {
// auth chatId // auth chatId
const [chat, history] = await Promise.all([ const [chat, history] = await Promise.all([
Chat.findOne({ Chat.findOne(
chatId,
userId
}),
Chat.aggregate([
{ {
$match: { chatId,
chatId, userId
userId: new mongoose.Types.ObjectId(userId)
}
}, },
'title variables'
),
ChatItem.find(
{ {
$project: { chatId,
content: { userId
$slice: ['$content', -30] // 返回 content 数组的最后 30 个元素
}
}
}, },
{ $unwind: '$content' }, `dataId obj value ${TaskResponseKeyEnum.responseData}`
{ )
$project: { .sort({ _id: -1 })
_id: '$content._id', .limit(30)
obj: '$content.obj',
value: '$content.value',
[TaskResponseKeyEnum.responseData]: `$content.${TaskResponseKeyEnum.responseData}`
}
}
])
]); ]);
if (!chat) { if (!chat) {
throw new Error('聊天框不存在'); throw new Error('聊天框不存在');
} }
return { history, chat }; history.reverse();
return { app, history, chat };
} }
return {}; return {};
})(); })();
if (!app) {
throw new Error('Auth App Error');
}
const isOwner = String(app.userId) === userId; const isOwner = String(app.userId) === userId;
jsonRes<InitChatResponse>(res, { jsonRes<InitChatResponse>(res, {
@@ -108,3 +98,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
}); });
} }
} }
export const config = {
api: {
bodyParser: {
sizeLimit: '10mb'
}
}
};

View File

@@ -1,6 +1,6 @@
import type { NextApiRequest, NextApiResponse } from 'next'; import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response'; import { jsonRes } from '@/service/response';
import { connectToDatabase, Chat } from '@/service/mongo'; import { connectToDatabase, Chat, ChatItem } from '@/service/mongo';
import { authUser } from '@/service/utils/auth'; import { authUser } from '@/service/utils/auth';
type Props = { type Props = {
@@ -17,16 +17,28 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
await connectToDatabase(); await connectToDatabase();
if (chatId) { if (chatId) {
await Chat.findOneAndRemove({ await Promise.all([
chatId, Chat.findOneAndRemove({
userId chatId,
}); userId
}),
ChatItem.deleteMany({
userId,
chatId
})
]);
} }
if (appId) { if (appId) {
await Chat.deleteMany({ await Promise.all([
appId, Chat.deleteMany({
userId appId,
}); userId
}),
ChatItem.deleteMany({
userId,
appId
})
]);
} }
jsonRes(res); jsonRes(res);

View File

@@ -28,7 +28,7 @@ import { BillSourceEnum } from '@/constants/user';
import { ChatHistoryItemResType } from '@/types/chat'; import { ChatHistoryItemResType } from '@/types/chat';
import { UserModelSchema } from '@/types/mongoSchema'; import { UserModelSchema } from '@/types/mongoSchema';
export type MessageItemType = ChatCompletionRequestMessage & { _id?: string }; export type MessageItemType = ChatCompletionRequestMessage & { dataId?: string };
type FastGptWebChatProps = { type FastGptWebChatProps = {
chatId?: string; // undefined: nonuse history, '': new chat, 'xxxxx': use history chatId?: string; // undefined: nonuse history, '': new chat, 'xxxxx': use history
appId?: string; appId?: string;
@@ -172,7 +172,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
content: [ content: [
prompt, prompt,
{ {
_id: messages[messages.length - 1]._id, dataId: messages[messages.length - 1].dataId,
obj: ChatRoleEnum.AI, obj: ChatRoleEnum.AI,
value: answerText, value: answerText,
responseData responseData

View File

@@ -2,10 +2,9 @@
import type { NextApiRequest, NextApiResponse } from 'next'; import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response'; import { jsonRes } from '@/service/response';
import { authUser } from '@/service/utils/auth'; import { authUser } from '@/service/utils/auth';
import { connectToDatabase, Chat } from '@/service/mongo'; import { connectToDatabase, ChatItem } from '@/service/mongo';
import { Types } from 'mongoose'; import { Types } from 'mongoose';
import type { ChatItemType } from '@/types/chat'; import type { ChatItemType } from '@/types/chat';
import { TaskResponseKeyEnum } from '@/constants/chat';
export type Props = { export type Props = {
chatId?: string; chatId?: string;
@@ -37,30 +36,37 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
export async function getChatHistory({ export async function getChatHistory({
chatId, chatId,
userId, userId,
limit = 20 limit = 30
}: Props & { userId: string }): Promise<Response> { }: Props & { userId: string }): Promise<Response> {
if (!chatId) { if (!chatId) {
return { history: [] }; return { history: [] };
} }
const history = await Chat.aggregate([ const history = await ChatItem.aggregate([
{ $match: { chatId, userId: new Types.ObjectId(userId) } },
{ {
$project: { $match: {
content: { chatId,
$slice: ['$content', -limit] // 返回 content 数组的最后20个元素 userId: new Types.ObjectId(userId)
}
} }
}, },
{ $unwind: '$content' }, {
$sort: {
_id: -1
}
},
{
$limit: limit
},
{ {
$project: { $project: {
obj: '$content.obj', dataId: 1,
value: '$content.value', obj: 1,
[TaskResponseKeyEnum.responseData]: `$content.responseData` value: 1
} }
} }
]); ]);
history.reverse();
return { history }; return { history };
} }

View File

@@ -2,15 +2,13 @@ import React from 'react';
import { NodeProps } from 'reactflow'; import { NodeProps } from 'reactflow';
import NodeCard from '../modules/NodeCard'; import NodeCard from '../modules/NodeCard';
import { FlowModuleItemType } from '@/types/flow'; import { FlowModuleItemType } from '@/types/flow';
import Divider from '../modules/Divider';
import Container from '../modules/Container'; import Container from '../modules/Container';
import RenderInput from '../render/RenderInput'; import RenderInput from '../render/RenderInput';
const NodeAnswer = ({ const NodeAnswer = ({ data }: NodeProps<FlowModuleItemType>) => {
data: { moduleId, inputs, outputs, onChangeNode, ...props } const { moduleId, inputs, outputs, onChangeNode } = data;
}: NodeProps<FlowModuleItemType>) => {
return ( return (
<NodeCard minW={'400px'} moduleId={moduleId} {...props}> <NodeCard minW={'400px'} {...data}>
<Container borderTop={'2px solid'} borderTopColor={'myGray.200'}> <Container borderTop={'2px solid'} borderTopColor={'myGray.200'}>
<RenderInput moduleId={moduleId} onChangeNode={onChangeNode} flowInputList={inputs} /> <RenderInput moduleId={moduleId} onChangeNode={onChangeNode} flowInputList={inputs} />
</Container> </Container>

View File

@@ -13,11 +13,10 @@ import MyIcon from '@/components/Icon';
import { FlowOutputItemTypeEnum, FlowValueTypeEnum, SpecialInputKeyEnum } from '@/constants/flow'; import { FlowOutputItemTypeEnum, FlowValueTypeEnum, SpecialInputKeyEnum } from '@/constants/flow';
import SourceHandle from '../render/SourceHandle'; import SourceHandle from '../render/SourceHandle';
const NodeCQNode = ({ const NodeCQNode = ({ data }: NodeProps<FlowModuleItemType>) => {
data: { moduleId, inputs, outputs, onChangeNode, ...props } const { moduleId, inputs, outputs, onChangeNode } = data;
}: NodeProps<FlowModuleItemType>) => {
return ( return (
<NodeCard minW={'400px'} moduleId={moduleId} {...props}> <NodeCard minW={'400px'} {...data}>
<Divider text="Input" /> <Divider text="Input" />
<Container> <Container>
<RenderInput <RenderInput

View File

@@ -13,16 +13,15 @@ import MySlider from '@/components/Slider';
import { Box } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import { formatPrice } from '@/utils/user'; import { formatPrice } from '@/utils/user';
const NodeChat = ({ const NodeChat = ({ data }: NodeProps<FlowModuleItemType>) => {
data: { moduleId, inputs, outputs, onChangeNode, ...props } const { moduleId, inputs, outputs, onChangeNode } = data;
}: NodeProps<FlowModuleItemType>) => {
const outputsLen = useMemo( const outputsLen = useMemo(
() => outputs.filter((item) => item.type !== FlowOutputItemTypeEnum.hidden).length, () => outputs.filter((item) => item.type !== FlowOutputItemTypeEnum.hidden).length,
[outputs] [outputs]
); );
return ( return (
<NodeCard minW={'400px'} moduleId={moduleId} {...props}> <NodeCard minW={'400px'} {...data}>
<Divider text="Input" /> <Divider text="Input" />
<Container> <Container>
<RenderInput <RenderInput

View File

@@ -3,7 +3,7 @@ import { NodeProps } from 'reactflow';
import NodeCard from '../modules/NodeCard'; import NodeCard from '../modules/NodeCard';
import { FlowModuleItemType } from '@/types/flow'; import { FlowModuleItemType } from '@/types/flow';
const NodeAnswer = ({ data: { ...props } }: NodeProps<FlowModuleItemType>) => { const NodeAnswer = ({ data }: NodeProps<FlowModuleItemType>) => {
return <NodeCard {...props}></NodeCard>; return <NodeCard {...data}></NodeCard>;
}; };
export default React.memo(NodeAnswer); export default React.memo(NodeAnswer);

View File

@@ -15,14 +15,13 @@ import ExtractFieldModal from '../modules/ExtractFieldModal';
import { ContextExtractEnum } from '@/constants/flow/flowField'; import { ContextExtractEnum } from '@/constants/flow/flowField';
import { FlowOutputItemTypeEnum, FlowValueTypeEnum } from '@/constants/flow'; import { FlowOutputItemTypeEnum, FlowValueTypeEnum } from '@/constants/flow';
const NodeExtract = ({ const NodeExtract = ({ data }: NodeProps<FlowModuleItemType>) => {
data: { inputs, outputs, moduleId, onChangeNode, onDelEdge, ...props } const { inputs, outputs, moduleId, onChangeNode, onDelEdge } = data;
}: NodeProps<FlowModuleItemType>) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [editExtractFiled, setEditExtractField] = useState<ContextExtractAgentItemType>(); const [editExtractFiled, setEditExtractField] = useState<ContextExtractAgentItemType>();
return ( return (
<NodeCard minW={'400px'} moduleId={moduleId} {...props}> <NodeCard minW={'400px'} {...data}>
<Divider text="Input" /> <Divider text="Input" />
<Container> <Container>
<RenderInput <RenderInput

View File

@@ -7,22 +7,17 @@ import Container from '../modules/Container';
import RenderInput from '../render/RenderInput'; import RenderInput from '../render/RenderInput';
import RenderOutput from '../render/RenderOutput'; import RenderOutput from '../render/RenderOutput';
const NodeHistory = ({ const NodeHistory = ({ data }: NodeProps<FlowModuleItemType>) => {
data: { inputs, outputs, onChangeNode, ...props } const { inputs, outputs, moduleId, onChangeNode } = data;
}: NodeProps<FlowModuleItemType>) => {
return ( return (
<NodeCard minW={'300px'} {...props}> <NodeCard minW={'300px'} {...data}>
<Divider text="Input" /> <Divider text="Input" />
<Container> <Container>
<RenderInput moduleId={props.moduleId} onChangeNode={onChangeNode} flowInputList={inputs} /> <RenderInput moduleId={moduleId} onChangeNode={onChangeNode} flowInputList={inputs} />
</Container> </Container>
<Divider text="Output" /> <Divider text="Output" />
<Container> <Container>
<RenderOutput <RenderOutput onChangeNode={onChangeNode} moduleId={moduleId} flowOutputList={outputs} />
onChangeNode={onChangeNode}
moduleId={props.moduleId}
flowOutputList={outputs}
/>
</Container> </Container>
</NodeCard> </NodeCard>
); );

View File

@@ -13,11 +13,10 @@ import { FlowInputItemTypeEnum, FlowOutputItemTypeEnum, FlowValueTypeEnum } from
import { customAlphabet } from 'nanoid'; import { customAlphabet } from 'nanoid';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6); const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6);
const NodeHttp = ({ const NodeHttp = ({ data }: NodeProps<FlowModuleItemType>) => {
data: { moduleId, inputs, outputs, onChangeNode, ...props } const { moduleId, inputs, outputs, onChangeNode } = data;
}: NodeProps<FlowModuleItemType>) => {
return ( return (
<NodeCard minW={'350px'} moduleId={moduleId} {...props}> <NodeCard minW={'350px'} {...data}>
<Container borderTop={'2px solid'} borderTopColor={'myGray.200'}> <Container borderTop={'2px solid'} borderTopColor={'myGray.200'}>
<RenderInput moduleId={moduleId} onChangeNode={onChangeNode} flowInputList={inputs} /> <RenderInput moduleId={moduleId} onChangeNode={onChangeNode} flowInputList={inputs} />
<Button <Button

View File

@@ -69,11 +69,10 @@ const KBSelect = ({
); );
}; };
const NodeKbSearch = ({ const NodeKbSearch = ({ data }: NodeProps<FlowModuleItemType>) => {
data: { moduleId, inputs, outputs, onChangeNode, ...props } const { moduleId, inputs, outputs, onChangeNode } = data;
}: NodeProps<FlowModuleItemType>) => {
return ( return (
<NodeCard minW={'400px'} moduleId={moduleId} {...props}> <NodeCard minW={'400px'} {...data}>
<Divider text="Input" /> <Divider text="Input" />
<Container> <Container>
<RenderInput <RenderInput

View File

@@ -8,11 +8,9 @@ import { SystemInputEnum } from '@/constants/app';
import { FlowValueTypeEnum } from '@/constants/flow'; import { FlowValueTypeEnum } from '@/constants/flow';
import SourceHandle from '../render/SourceHandle'; import SourceHandle from '../render/SourceHandle';
const QuestionInputNode = ({ const QuestionInputNode = ({ data }: NodeProps<FlowModuleItemType>) => {
data: { inputs, outputs, ...props }
}: NodeProps<FlowModuleItemType>) => {
return ( return (
<NodeCard minW={'240px'} {...props}> <NodeCard minW={'240px'} {...data}>
<Container borderTop={'2px solid'} borderTopColor={'myGray.200'} textAlign={'end'}> <Container borderTop={'2px solid'} borderTopColor={'myGray.200'} textAlign={'end'}>
<Box position={'relative'}> <Box position={'relative'}>

View File

@@ -8,9 +8,9 @@ import Divider from '../modules/Divider';
import Container from '../modules/Container'; import Container from '../modules/Container';
import Label from '../modules/Label'; import Label from '../modules/Label';
const NodeTFSwitch = ({ data: { inputs, outputs, ...props } }: NodeProps<FlowModuleItemType>) => { const NodeTFSwitch = ({ data }: NodeProps<FlowModuleItemType>) => {
return ( return (
<NodeCard minW={'220px'} {...props}> <NodeCard minW={'220px'} {...data}>
<Divider text="输入输出" /> <Divider text="输入输出" />
<Container h={'100px'} py={0} px={0} display={'flex'} alignItems={'center'}> <Container h={'100px'} py={0} px={0} display={'flex'} alignItems={'center'}>
<Box flex={1} pl={'12px'}> <Box flex={1} pl={'12px'}>

View File

@@ -10,9 +10,8 @@ import MyIcon from '@/components/Icon';
import MyTooltip from '@/components/MyTooltip'; import MyTooltip from '@/components/MyTooltip';
import { welcomeTextTip } from '@/constants/flow/ModuleTemplate'; import { welcomeTextTip } from '@/constants/flow/ModuleTemplate';
const NodeUserGuide = ({ const NodeUserGuide = ({ data }: NodeProps<FlowModuleItemType>) => {
data: { inputs, outputs, onChangeNode, ...props } const { inputs, moduleId, onChangeNode } = data;
}: NodeProps<FlowModuleItemType>) => {
const welcomeText = useMemo( const welcomeText = useMemo(
() => inputs.find((item) => item.key === SystemInputEnum.welcomeText), () => inputs.find((item) => item.key === SystemInputEnum.welcomeText),
[inputs] [inputs]
@@ -20,7 +19,7 @@ const NodeUserGuide = ({
return ( return (
<> <>
<NodeCard minW={'300px'} {...props}> <NodeCard minW={'300px'} {...data}>
<Container borderTop={'2px solid'} borderTopColor={'myGray.200'}> <Container borderTop={'2px solid'} borderTopColor={'myGray.200'}>
<> <>
<Flex mb={1} alignItems={'center'}> <Flex mb={1} alignItems={'center'}>
@@ -40,7 +39,7 @@ const NodeUserGuide = ({
placeholder={welcomeTextTip} placeholder={welcomeTextTip}
onChange={(e) => { onChange={(e) => {
onChangeNode({ onChangeNode({
moduleId: props.moduleId, moduleId,
key: SystemInputEnum.welcomeText, key: SystemInputEnum.welcomeText,
type: 'inputs', type: 'inputs',
value: { value: {

View File

@@ -22,9 +22,8 @@ export const defaultVariable: VariableItemType = {
enums: [{ value: '' }] enums: [{ value: '' }]
}; };
const NodeUserGuide = ({ const NodeUserGuide = ({ data }: NodeProps<FlowModuleItemType>) => {
data: { inputs, outputs, onChangeNode, ...props } const { inputs, moduleId, onChangeNode } = data;
}: NodeProps<FlowModuleItemType>) => {
const variables = useMemo( const variables = useMemo(
() => () =>
(inputs.find((item) => item.key === SystemInputEnum.variables) (inputs.find((item) => item.key === SystemInputEnum.variables)
@@ -37,7 +36,7 @@ const NodeUserGuide = ({
const updateVariables = useCallback( const updateVariables = useCallback(
(value: VariableItemType[]) => { (value: VariableItemType[]) => {
onChangeNode({ onChangeNode({
moduleId: props.moduleId, moduleId,
key: SystemInputEnum.variables, key: SystemInputEnum.variables,
type: 'inputs', type: 'inputs',
value: { value: {
@@ -46,7 +45,7 @@ const NodeUserGuide = ({
} }
}); });
}, },
[inputs, onChangeNode, props.moduleId] [inputs, onChangeNode, moduleId]
); );
const onclickSubmit = useCallback( const onclickSubmit = useCallback(
@@ -59,7 +58,7 @@ const NodeUserGuide = ({
return ( return (
<> <>
<NodeCard minW={'300px'} {...props}> <NodeCard minW={'300px'} {...data}>
<Container borderTop={'2px solid'} borderTopColor={'myGray.200'}> <Container borderTop={'2px solid'} borderTopColor={'myGray.200'}>
<TableContainer> <TableContainer>
<Table> <Table>

View File

@@ -6,29 +6,25 @@ import type { FlowModuleItemType } from '@/types/flow';
import MyTooltip from '@/components/MyTooltip'; import MyTooltip from '@/components/MyTooltip';
import { QuestionOutlineIcon } from '@chakra-ui/icons'; import { QuestionOutlineIcon } from '@chakra-ui/icons';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useCopyData } from '@/utils/tools';
type Props = { type Props = FlowModuleItemType & {
children?: React.ReactNode | React.ReactNode[] | string; children?: React.ReactNode | React.ReactNode[] | string;
logo: string;
name: string;
description?: string;
intro: string;
minW?: string | number; minW?: string | number;
moduleId: string;
onDelNode: FlowModuleItemType['onDelNode'];
onCopyNode: FlowModuleItemType['onCopyNode'];
}; };
const NodeCard = ({ const NodeCard = (props: Props) => {
children, const {
logo = '/icon/logo.svg', children,
name = '未知模块', logo = '/icon/logo.svg',
description, name = '未知模块',
minW = '300px', description,
onCopyNode, minW = '300px',
onDelNode, onCopyNode,
moduleId onDelNode,
}: Props) => { moduleId
} = props;
const { copyData } = useCopyData();
const { t } = useTranslation(); const { t } = useTranslation();
const theme = useTheme(); const theme = useTheme();
@@ -39,16 +35,22 @@ const NodeCard = ({
label: t('common.Copy'), label: t('common.Copy'),
onClick: () => onCopyNode(moduleId) 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', icon: 'delete',
label: t('common.Delete'), label: t('common.Delete'),
onClick: () => onDelNode(moduleId) onClick: () => onDelNode(moduleId)
}, },
// {
// icon: 'collectionLight',
// label: t('common.Collect'),
// onClick: () => {}
// },
{ {
icon: 'back', icon: 'back',
label: t('common.Cancel'), label: t('common.Cancel'),

View File

@@ -1,7 +1,7 @@
import React, { useCallback, useRef } from 'react'; import React, { useCallback, useRef } from 'react';
import Head from 'next/head'; import Head from 'next/head';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { getInitChatSiteInfo, delChatRecordByIndex, putChatHistory } from '@/api/chat'; import { getInitChatSiteInfo, delChatRecordById, putChatHistory } from '@/api/chat';
import { import {
Box, Box,
Flex, Flex,
@@ -128,7 +128,7 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
...state, ...state,
history: state.history.filter((_, i) => i !== index) history: state.history.filter((_, i) => i !== index)
})); }));
await delChatRecordByIndex({ chatId, contentId }); await delChatRecordById({ chatId, contentId });
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }

View File

@@ -147,10 +147,6 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
onClick={onclickGit} onClick={onclickGit}
/> />
</Flex> </Flex>
<Box mt={3} textAlign={'center'} fontSize={'sm'} color={'myGray.600'}>
Git Git 使 Git
</Box>
</> </>
)} )}
</form> </form>

View File

@@ -1,7 +1,7 @@
import { Schema, model, models, Model } from 'mongoose'; import { Schema, model, models, Model } from 'mongoose';
import { ChatSchema as ChatType } from '@/types/mongoSchema'; import { ChatSchema as ChatType } from '@/types/mongoSchema';
import { ChatRoleMap, TaskResponseKeyEnum } from '@/constants/chat'; import { ChatRoleMap, TaskResponseKeyEnum } from '@/constants/chat';
import { ChatSourceEnum, ChatSourceMap } from '@/constants/chat'; import { ChatSourceMap } from '@/constants/chat';
const ChatSchema = new Schema({ const ChatSchema = new Schema({
chatId: { chatId: {
@@ -45,6 +45,10 @@ const ChatSchema = new Schema({
shareId: { shareId: {
type: String type: String
}, },
isInit: {
type: Boolean,
default: false
},
content: { content: {
type: [ type: [
{ {

View 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);

View File

@@ -63,7 +63,6 @@ export const dispatchChatCompletion = async (props: Record<string, any>): Promis
} }
const { filterQuoteQA, quotePrompt } = filterQuote({ const { filterQuoteQA, quotePrompt } = filterQuote({
history,
quoteQA, quoteQA,
model: modelConstantsData model: modelConstantsData
}); });
@@ -91,6 +90,7 @@ export const dispatchChatCompletion = async (props: Record<string, any>): Promis
maxToken, maxToken,
filterMessages filterMessages
}); });
// console.log(messages);
// FastGpt temperature range: 1~10 // FastGpt temperature range: 1~10
temperature = +(modelConstantsData.maxTemperature * (temperature / 10)).toFixed(2); temperature = +(modelConstantsData.maxTemperature * (temperature / 10)).toFixed(2);
@@ -182,32 +182,23 @@ export const dispatchChatCompletion = async (props: Record<string, any>): Promis
}; };
function filterQuote({ function filterQuote({
history = [],
quoteQA = [], quoteQA = [],
model model
}: { }: {
history: ChatProps['history'];
quoteQA: ChatProps['quoteQA']; quoteQA: ChatProps['quoteQA'];
model: ChatModelItemType; 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({ const sliceResult = modelToolMap.tokenSlice({
model: model.model, model: model.model,
maxToken: model.quoteMaxToken, maxToken: model.quoteMaxToken,
messages: concatQuote.map((item, i) => ({ messages: quoteQA.map((item) => ({
obj: ChatRoleEnum.System, obj: ChatRoleEnum.System,
value: item.a ? `{instruction:${item.q},output:${item.a}}` : `{instruction:${item.q}}` value: item.a ? `{instruction:${item.q},output:${item.a}}` : `{instruction:${item.q}}`
})) }))
}); });
// slice filterSearch // slice filterSearch
const filterQuoteQA = concatQuote.slice(0, sliceResult.length); const filterQuoteQA = quoteQA.slice(0, sliceResult.length);
const quotePrompt = const quotePrompt =
filterQuoteQA.length > 0 filterQuoteQA.length > 0

View File

@@ -113,6 +113,7 @@ async function initPg() {
} }
export * from './models/chat'; export * from './models/chat';
export * from './models/chatItem';
export * from './models/app'; export * from './models/app';
export * from './models/user'; export * from './models/user';
export * from './models/bill'; export * from './models/bill';

View File

@@ -1,5 +1,5 @@
import { ChatItemType } from '@/types/chat'; import { ChatItemType } from '@/types/chat';
import { Chat, App } from '@/service/mongo'; import { Chat, App, ChatItem } from '@/service/mongo';
import { ChatSourceEnum } from '@/constants/chat'; import { ChatSourceEnum } from '@/constants/chat';
type Props = { type Props = {
@@ -32,36 +32,40 @@ export async function saveChat({
'_id' '_id'
); );
const promise = []; const promise: any[] = [
ChatItem.insertMany(
content.map((item) => ({
chatId,
userId,
appId,
...item
}))
)
];
if (chatHistory) { if (chatHistory) {
promise.push( promise.push([
Chat.updateOne( Chat.updateOne(
{ chatId, userId }, { chatId, userId },
{ {
$push: {
content: {
$each: content,
$slice: -40
}
},
title: content[0].value.slice(0, 20), title: content[0].value.slice(0, 20),
updateTime: new Date() updateTime: new Date()
} }
) )
); ]);
} else { } else {
promise.push( promise.push(
Chat.create({ ...[
chatId, Chat.create({
userId, chatId,
appId, userId,
variables, appId,
title: content[0].value.slice(0, 20), variables,
source, title: content[0].value.slice(0, 20),
shareId, source,
content: content shareId
}) })
]
); );
} }

View File

@@ -6,7 +6,7 @@ import { ClassifyQuestionAgentItemType } from './app';
export type ExportChatType = 'md' | 'pdf' | 'html'; export type ExportChatType = 'md' | 'pdf' | 'html';
export type ChatItemType = { export type ChatItemType = {
_id?: string; dataId?: string;
obj: `${ChatRoleEnum}`; obj: `${ChatRoleEnum}`;
value: string; value: string;
[TaskResponseKeyEnum.responseData]?: ChatHistoryItemResType[]; [TaskResponseKeyEnum.responseData]?: ChatHistoryItemResType[];

View File

@@ -53,9 +53,9 @@ export type FlowModuleTemplateType = {
description?: string; description?: string;
intro: string; intro: string;
flowType: `${FlowModuleTypeEnum}`; flowType: `${FlowModuleTypeEnum}`;
showStatus?: boolean;
inputs: FlowInputItemType[]; inputs: FlowInputItemType[];
outputs: FlowOutputItemType[]; outputs: FlowOutputItemType[];
showStatus?: boolean;
}; };
export type FlowModuleItemType = FlowModuleTemplateType & { export type FlowModuleItemType = FlowModuleTemplateType & {
moduleId: string; moduleId: string;

View File

@@ -92,9 +92,18 @@ export interface ChatSchema {
variables: Record<string, any>; variables: Record<string, any>;
source: `${ChatSourceEnum}`; source: `${ChatSourceEnum}`;
shareId?: string; shareId?: string;
isInit: boolean;
content: ChatItemType[]; content: ChatItemType[];
} }
export interface ChatItemSchema extends ChatItemType {
dataId: string;
chatId: string;
userId: string;
appId: string;
time: Date;
}
export type BillListItemType = { export type BillListItemType = {
moduleName: string; moduleName: string;
amount: number; amount: number;

View File

@@ -33,7 +33,7 @@ export const gptMessage2ChatType = (messages: MessageItemType[]): ChatItemType[]
}; };
return messages.map((item) => ({ return messages.map((item) => ({
_id: item._id, dataId: item.dataId,
obj: roleMap[item.role], obj: roleMap[item.role],
value: item.content || '' value: item.content || ''
})); }));

View File

@@ -41,7 +41,7 @@ export const adaptChatItem_openAI = ({
[ChatRoleEnum.System]: ChatCompletionRequestMessageRoleEnum.System [ChatRoleEnum.System]: ChatCompletionRequestMessageRoleEnum.System
}; };
return messages.map((item) => ({ return messages.map((item) => ({
...(reserveId && { _id: item._id }), ...(reserveId && { dataId: item.dataId }),
role: map[item.obj] || ChatCompletionRequestMessageRoleEnum.System, role: map[item.obj] || ChatCompletionRequestMessageRoleEnum.System,
content: item.value || '' content: item.value || ''
})); }));

View File

@@ -1,7 +1,3 @@
---
sidebar_position: 2
---
# Config Chat Model # 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. 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.

View 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
}
]
}
```

View File

@@ -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. 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 ## Brief Explanation of Basic Fields
Here are some basic configuration fields. Here are some basic configuration fields.

View File

@@ -70,7 +70,7 @@ services:
fastgpt: fastgpt:
container_name: fastgpt container_name: fastgpt
# image: c121914yu/fast-gpt:latest # docker hub # image: c121914yu/fast-gpt:latest # docker hub
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:latest # 阿里云 image: ghcr.io/labring/fastgpt:latest # 阿里云
ports: ports:
- 3000:3000 - 3000:3000
networks: networks:
@@ -89,7 +89,8 @@ services:
- TOKEN_KEY=any - TOKEN_KEY=any
- ROOT_KEY=root_key - ROOT_KEY=root_key
# mongo 配置,不需要改 # 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 - MONGODB_NAME=fastgpt
# pg配置. # pg配置.
- PG_HOST=pg - PG_HOST=pg
@@ -105,61 +106,58 @@ networks:
# host 版本, 不推荐。 # host 版本, 不推荐。
version: '3.3' version: '3.3'
services: services:
pg: pg:
image: ankane/pgvector:v0.4.2 # dockerhub image: ankane/pgvector:v0.4.2 # dockerhub
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:v0.4.2 # 阿里云 container_name: pg
container_name: pg restart: always
restart: always ports: # 生产环境建议不要暴露
ports: # 生产环境建议不要暴露 - 5432:5432
- 5432:5432 environment:
environment: # 这里的配置只有首次运行生效。修改后,重启镜像是不会生效的。需要把持久化数据删除再重启,才有效果
# 这里的配置只有首次运行生效。修改后,重启镜像是不会生效的。需要把持久化数据删除再重启,才有效果 - POSTGRES_USER=username
- POSTGRES_USER=username - POSTGRES_PASSWORD=password
- POSTGRES_PASSWORD=password - POSTGRES_DB=postgres
- POSTGRES_DB=postgres volumes:
volumes: - ./pg/data:/var/lib/postgresql/data
- ./pg/data:/var/lib/postgresql/data mongo:
mongo: image: mongo:5.0.18
image: mongo:5.0.18 container_name: mongo
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/mongo:5.0.18 # 阿里云 restart: always
container_name: mongo ports: # 生产环境建议不要暴露
restart: always - 27017:27017
ports: # 生产环境建议不要暴露 environment:
- 27017:27017 # 这里的配置只有首次运行生效。修改后,重启镜像是不会生效的。需要把持久化数据删除再重启,才有效果
environment: - MONGO_INITDB_ROOT_USERNAME=username
# 这里的配置只有首次运行生效。修改后,重启镜像是不会生效的。需要把持久化数据删除再重启,才有效果 - MONGO_INITDB_ROOT_PASSWORD=password
- MONGO_INITDB_ROOT_USERNAME=username volumes:
- MONGO_INITDB_ROOT_PASSWORD=password - ./mongo/data:/data/db
volumes: - ./mongo/logs:/var/log/mongodb
- ./mongo/data:/data/db fastgpt:
- ./mongo/logs:/var/log/mongodb image: ghcr.io/c121914yu/fastgpt:latest # github
fastgpt: network_mode: host
# image: ghcr.io/c121914yu/fastgpt:latest # github restart: always
# image: c121914yu/fast-gpt:latest # docker hub container_name: fastgpt
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:latest # 阿里云 environment:
network_mode: host # root 密码,用户名为: root
restart: always - DEFAULT_ROOT_PSW=1234
container_name: fastgpt # 中转地址,如果是用官方号,不需要管
environment: - OPENAI_BASE_URL=https://api.openai.com/v1
# root 密码,用户名为: root - CHAT_API_KEY=sk-xxxx
- DEFAULT_ROOT_PSW=1234 - DB_MAX_LINK=5 # database max link
# 中转地址,如果是用官方号,不需要管 # token加密凭证随便填作为登录凭证
- OPENAI_BASE_URL=https://api.openai.com/v1 - TOKEN_KEY=any
- CHAT_API_KEY=sk-xxxx # root key, 最高权限,可以内部接口互相调用
- DB_MAX_LINK=5 # database max link - ROOT_KEY=root_key
# token加密凭证随便填作为登录凭证 # mongo 配置,不需要改
- TOKEN_KEY=any - MONGODB_URI=mongodb://username:password@0.0.0.0:27017
# root key, 最高权限,可以内部接口互相调用 # - MONGODB_URI=mongodb://username:password@0.0.0.0:27017/?authSource=admin
- ROOT_KEY=root_key - MONGODB_NAME=fastgpt
# mongo 配置,不需要改 # pg 配置
- MONGODB_URI=mongodb://username:password@0.0.0.0:27017/?authSource=admin - PG_HOST=0.0.0.0
- MONGODB_NAME=fastgpt - PG_PORT=5432
# pg 配置 - PG_USER=username
- PG_HOST=0.0.0.0 - PG_PASSWORD=password
- PG_PORT=5432 - PG_DB_NAME=postgres
- PG_USER=username
- PG_PASSWORD=password
- PG_DB_NAME=postgres
``` ```
## 四、运行 docker-compose ## 四、运行 docker-compose

View File

@@ -0,0 +1,9 @@
# V4.1 版本初始化
新版重新设置了对话存储结构,需要初始化原来的存储内容
## 执行初始化 API
部署新版项目,并发起 3 个 HTTP 请求(记得携带 headers.rootkey这个值是环境变量里的
https://xxxxx/api/admin/initChatItem

View File

@@ -1,7 +1,3 @@
---
sidebar_position: 2
---
# 配置其他对话模型 # 配置其他对话模型
默认情况下FastGPT 只配置了 GPT 的 3 个模型,如果你需要接入其他模型,需要进行一些额外配置。 默认情况下FastGPT 只配置了 GPT 的 3 个模型,如果你需要接入其他模型,需要进行一些额外配置。

View File

@@ -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
}
]
}
```

View File

@@ -1,5 +1,5 @@
--- ---
sidebar_position: 1 sidebar_position: 2
--- ---
# 快速介绍 # 快速介绍
@@ -10,6 +10,8 @@ sidebar_position: 1
这个配置文件中包含了前端页面定制、系统级参数、AI 对话的模型等…… 这个配置文件中包含了前端页面定制、系统级参数、AI 对话的模型等……
**注意:下面的配置介绍仅是局部介绍,你需要完整挂载整个 config.jso ,不能仅挂载一部分。你可以直接在给的 config.json 基础上根据下面的介绍进行修改。**
## 基础字段粗略说明 ## 基础字段粗略说明
这里会介绍一些基础的配置字段。 这里会介绍一些基础的配置字段。

View File

@@ -37,7 +37,7 @@ docker-compose -v
version: '3.3' version: '3.3'
services: services:
pg: 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 # 阿里云 # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:v0.4.2 # 阿里云
container_name: pg container_name: pg
restart: always restart: always
@@ -70,7 +70,7 @@ services:
fastgpt: fastgpt:
container_name: fastgpt container_name: fastgpt
# image: c121914yu/fast-gpt:latest # docker hub # image: c121914yu/fast-gpt:latest # docker hub
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:latest # 阿里云 image: ghcr.io/labring/fastgpt:latest # 阿里云
ports: ports:
- 3000:3000 - 3000:3000
networks: networks:
@@ -89,7 +89,8 @@ services:
- TOKEN_KEY=any - TOKEN_KEY=any
- ROOT_KEY=root_key - ROOT_KEY=root_key
# mongo 配置,不需要改 # 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 - MONGODB_NAME=fastgpt
# pg配置. # pg配置.
- PG_HOST=pg - PG_HOST=pg
@@ -136,7 +137,7 @@ volumes:
fastgpt: fastgpt:
# image: ghcr.io/c121914yu/fastgpt:latest # github # image: ghcr.io/c121914yu/fastgpt:latest # github
# image: c121914yu/fast-gpt:latest # docker hub # 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 network_mode: host
restart: always restart: always
container_name: fastgpt container_name: fastgpt
@@ -152,7 +153,8 @@ environment:
# root key, 最高权限,可以内部接口互相调用 # root key, 最高权限,可以内部接口互相调用
- ROOT_KEY=root_key - ROOT_KEY=root_key
# mongo 配置,不需要改 # 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 - MONGODB_NAME=fastgpt
# pg 配置 # pg 配置
- PG_HOST=0.0.0.0 - PG_HOST=0.0.0.0

View File

@@ -0,0 +1,9 @@
# V4.1 版本初始化
新版重新设置了对话存储结构,需要初始化原来的存储内容
## 执行初始化 API
部署新版项目,并发起 3 个 HTTP 请求(记得携带 headers.rootkey这个值是环境变量里的
https://xxxxx/api/admin/initChatItem