diff --git a/docSite/content/docs/installation/upgrading/40.md b/docSite/content/docs/installation/upgrading/40.md index 172e894ddf0d..785a8fe94340 100644 --- a/docSite/content/docs/installation/upgrading/40.md +++ b/docSite/content/docs/installation/upgrading/40.md @@ -4,7 +4,7 @@ description: 'FastGPT 从旧版本升级到 V4.0 操作指南' icon: 'upgrade' draft: false toc: true -weight: 1000 +weight: 850 --- 如果您是**从旧版本升级到 V4**,由于新版 MongoDB 表变更比较大,需要按照本文档的说明执行一些初始化脚本。 diff --git a/docSite/content/docs/installation/upgrading/41.md b/docSite/content/docs/installation/upgrading/41.md index 4d121d4d85e3..7d2f056fdf30 100644 --- a/docSite/content/docs/installation/upgrading/41.md +++ b/docSite/content/docs/installation/upgrading/41.md @@ -4,7 +4,7 @@ description: 'FastGPT 从旧版本升级到 V4.1 操作指南' icon: 'upgrade' draft: false toc: true -weight: 999 +weight: 849 --- 如果您是**从旧版本升级到 V4.1**,由于新版重新设置了对话存储结构,需要初始化原来的存储内容。 diff --git a/docSite/content/docs/installation/upgrading/42.md b/docSite/content/docs/installation/upgrading/42.md index c1e4a1d866ed..ece48521c61b 100644 --- a/docSite/content/docs/installation/upgrading/42.md +++ b/docSite/content/docs/installation/upgrading/42.md @@ -4,7 +4,7 @@ description: 'FastGPT 从旧版本升级到 V4.2 操作指南' icon: 'upgrade' draft: false toc: true -weight: 998 +weight: 848 --- 99.9%用户不影响,升级 4.2 主要是修改了配置文件中 QAModel 的格式。从原先的数组改成对象: diff --git a/docSite/content/docs/installation/upgrading/421.md b/docSite/content/docs/installation/upgrading/421.md index cf8b184ce9e9..ea8a12e2709e 100644 --- a/docSite/content/docs/installation/upgrading/421.md +++ b/docSite/content/docs/installation/upgrading/421.md @@ -4,7 +4,7 @@ description: 'FastGPT 从旧版本升级到 V4.2.1 操作指南' icon: 'upgrade' draft: false toc: true -weight: 997 +weight: 847 --- 私有部署,如果添加了配置文件,需要在配置文件中修改 `VectorModels` 字段。增加 defaultToken 和 maxToken,分别对应直接分段时的默认 token 数量和该模型支持的 token 上限(通常不建议超过 3000) diff --git a/docSite/content/docs/installation/upgrading/43.md b/docSite/content/docs/installation/upgrading/43.md index 8a62a5e19e08..1c101244c74d 100644 --- a/docSite/content/docs/installation/upgrading/43.md +++ b/docSite/content/docs/installation/upgrading/43.md @@ -4,7 +4,7 @@ description: 'FastGPT 从旧版本升级到 V4.3 操作指南' icon: 'upgrade' draft: false toc: true -weight: 996 +weight: 846 --- ## 执行初始化 API diff --git a/docSite/content/docs/installation/upgrading/44.md b/docSite/content/docs/installation/upgrading/44.md index 6fff148d24fd..ee87879adde8 100644 --- a/docSite/content/docs/installation/upgrading/44.md +++ b/docSite/content/docs/installation/upgrading/44.md @@ -4,7 +4,7 @@ description: 'FastGPT 从旧版本升级到 V4.4 操作指南' icon: 'upgrade' draft: false toc: true -weight: 995 +weight: 845 --- ## 执行初始化 API diff --git a/docSite/content/docs/installation/upgrading/441.md b/docSite/content/docs/installation/upgrading/441.md index 5a6309747634..0721627f297e 100644 --- a/docSite/content/docs/installation/upgrading/441.md +++ b/docSite/content/docs/installation/upgrading/441.md @@ -4,7 +4,7 @@ description: 'FastGPT 从旧版本升级到 V4.4.1 操作指南' icon: 'upgrade' draft: false toc: true -weight: 994 +weight: 844 --- ## 执行初始化 API diff --git a/docSite/content/docs/installation/upgrading/442.md b/docSite/content/docs/installation/upgrading/442.md index 4dd6ffdc3532..21958a493b76 100644 --- a/docSite/content/docs/installation/upgrading/442.md +++ b/docSite/content/docs/installation/upgrading/442.md @@ -4,7 +4,7 @@ description: 'FastGPT 从旧版本升级到 V4.4.2 操作指南' icon: 'upgrade' draft: false toc: true -weight: 993 +weight: 843 --- ## 执行初始化 API diff --git a/docSite/content/docs/installation/upgrading/445.md b/docSite/content/docs/installation/upgrading/445.md index 1792db9ab42d..8dae7384c9bb 100644 --- a/docSite/content/docs/installation/upgrading/445.md +++ b/docSite/content/docs/installation/upgrading/445.md @@ -4,7 +4,7 @@ description: 'FastGPT V4.4.5 更新(需执行升级脚本)' icon: 'upgrade' draft: false toc: true -weight: 992 +weight: 842 --- ## 执行初始化 API diff --git a/docSite/content/docs/installation/upgrading/446.md b/docSite/content/docs/installation/upgrading/446.md new file mode 100644 index 000000000000..a6e3fe784706 --- /dev/null +++ b/docSite/content/docs/installation/upgrading/446.md @@ -0,0 +1,14 @@ +--- +title: 'V4.4.6' +description: 'FastGPT V4.4.6 更新' +icon: 'upgrade' +draft: false +toc: true +weight: 841 +--- + +## 功能介绍 + +1. 高级编排新增模块 - 应用调用,可调用其他应用。 +2. 新增 - 必要连接校验 +3. 修复 - 下一步指引在免登录中身份问题。 \ No newline at end of file diff --git a/packages/core/aiApi/config.ts b/packages/core/ai/config.ts similarity index 100% rename from packages/core/aiApi/config.ts rename to packages/core/ai/config.ts diff --git a/packages/core/aiApi/constant.ts b/packages/core/ai/constant.ts similarity index 100% rename from packages/core/aiApi/constant.ts rename to packages/core/ai/constant.ts diff --git a/packages/core/ai/functions/createQuestionGuide.ts b/packages/core/ai/functions/createQuestionGuide.ts new file mode 100644 index 000000000000..d7a667756bf2 --- /dev/null +++ b/packages/core/ai/functions/createQuestionGuide.ts @@ -0,0 +1,57 @@ +import { ChatCompletionRequestMessage } from '../type'; +import { getAIChatApi } from '../config'; + +export const Prompt_QuestionGuide = `我不太清楚问你什么问题,请帮我生成 3 个问题,引导我继续提问。问题的长度应小于20个字符,按 JSON 格式返回: ["问题1", "问题2", "问题3"]`; + +export async function createQuestionGuide({ + messages, + model +}: { + messages: ChatCompletionRequestMessage[]; + model: string; +}) { + const chatAPI = getAIChatApi(); + const { data } = await chatAPI.createChatCompletion({ + model: model, + temperature: 0, + max_tokens: 200, + messages: [ + ...messages, + { + role: 'user', + content: Prompt_QuestionGuide + } + ], + stream: false + }); + + const answer = data.choices?.[0].message?.content || ''; + const totalTokens = data.usage?.total_tokens || 0; + + const start = answer.indexOf('['); + const end = answer.lastIndexOf(']'); + + if (start === -1 || end === -1) { + return { + result: [], + tokens: totalTokens + }; + } + + const jsonStr = answer + .substring(start, end + 1) + .replace(/(\\n|\\)/g, '') + .replace(/ /g, ''); + + try { + return { + result: JSON.parse(jsonStr), + tokens: totalTokens + }; + } catch (error) { + return { + result: [], + tokens: totalTokens + }; + } +} diff --git a/packages/core/aiApi/type.d.ts b/packages/core/ai/type.d.ts similarity index 100% rename from packages/core/aiApi/type.d.ts rename to packages/core/ai/type.d.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 36347558a541..d480176cbf8a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.0' +lockfileVersion: '6.1' settings: autoInstallPeers: true diff --git a/projects/app/package.json b/projects/app/package.json index af6c0ddf3b99..ea26e9cdd0d9 100644 --- a/projects/app/package.json +++ b/projects/app/package.json @@ -1,6 +1,6 @@ { "name": "app", - "version": "4.4.5", + "version": "4.4.6", "private": false, "scripts": { "dev": "next dev", diff --git a/projects/app/public/docs/versionIntro.md b/projects/app/public/docs/versionIntro.md index 4b314b8655a5..aacab4f75d47 100644 --- a/projects/app/public/docs/versionIntro.md +++ b/projects/app/public/docs/versionIntro.md @@ -1,10 +1,9 @@ -### Fast GPT V4.4.5 +### Fast GPT V4.4.6 -1. 新增 - 下一步指引选项,可以通过模型生成 3 个预测问题。 -2. 新增 - 分享链接 hook 身份校验。 -3. 新增 - Api Key 使用。增加别名、额度限制和过期时间。自带 appId,无需额外连接。 -4. 去除 - 限定词。目前旧应用仍生效,9/25 后全面去除,请及时替换。 -5. 新增 - 引用模板/引用提示词设置,可以 DIY 引用内容的格式,从而更好的适配场景。[参考文档](https://doc.fastgpt.run/docs/use-cases/prompt/) -6. [使用文档](https://doc.fastgpt.run/docs/intro/) -7. [点击查看高级编排介绍文档](https://doc.fastgpt.run/docs/workflow) -8. [点击查看商业版](https://doc.fastgpt.run/docs/commercial/) +1. 高级编排新增模块 - 应用调用 +2. 新增 - 必要连接校验 +3. 新增 - 下一步指引选项,可以通过模型生成 3 个预测问题。 +4. 新增 - 分享链接 hook 身份校验。 +5. [使用文档](https://doc.fastgpt.run/docs/intro/) +6. [点击查看高级编排介绍文档](https://doc.fastgpt.run/docs/workflow) +7. [点击查看商业版](https://doc.fastgpt.run/docs/commercial/) diff --git a/projects/app/public/imgs/module/app.png b/projects/app/public/imgs/module/app.png new file mode 100644 index 000000000000..a21dafeb901c Binary files /dev/null and b/projects/app/public/imgs/module/app.png differ diff --git a/projects/app/public/locales/en/common.json b/projects/app/public/locales/en/common.json index 209286968b40..02667863533c 100644 --- a/projects/app/public/locales/en/common.json +++ b/projects/app/public/locales/en/common.json @@ -96,6 +96,7 @@ "module question": "Question", "module quoteList": "Quotes", "module runningTime": "Time", + "module search response": "Search Result", "module similarity": "Similarity", "module temperature": "Temperature", "module time": "Running Time", diff --git a/projects/app/public/locales/zh/common.json b/projects/app/public/locales/zh/common.json index e8c7111dc08a..102ac7a585a4 100644 --- a/projects/app/public/locales/zh/common.json +++ b/projects/app/public/locales/zh/common.json @@ -96,6 +96,7 @@ "module question": "问题", "module quoteList": "引用内容", "module runningTime": "运行时长", + "module search response": "搜索结果", "module similarity": "相似度", "module temperature": "温度", "module time": "运行时长", diff --git a/projects/app/src/api/core/ai/agent/type.d.ts b/projects/app/src/api/core/ai/agent/type.d.ts index e2d93013fa8f..99189b71c9c5 100644 --- a/projects/app/src/api/core/ai/agent/type.d.ts +++ b/projects/app/src/api/core/ai/agent/type.d.ts @@ -1,5 +1,6 @@ -import { ChatCompletionRequestMessage } from '@fastgpt/core/aiApi/type'; +import { ChatCompletionRequestMessage } from '@fastgpt/core/ai/type'; export type CreateQuestionGuideProps = { messages: ChatCompletionRequestMessage[]; + shareId?: string; }; diff --git a/projects/app/src/components/ChatBox/ResponseTags.tsx b/projects/app/src/components/ChatBox/ResponseTags.tsx index 82b8b6741e06..8f2f3194b771 100644 --- a/projects/app/src/components/ChatBox/ResponseTags.tsx +++ b/projects/app/src/components/ChatBox/ResponseTags.tsx @@ -12,15 +12,7 @@ const QuoteModal = dynamic(() => import('./QuoteModal'), { ssr: false }); const ContextModal = dynamic(() => import('./ContextModal'), { ssr: false }); const WholeResponseModal = dynamic(() => import('./WholeResponseModal'), { ssr: false }); -const ResponseTags = ({ - chatId, - contentId, - responseData = [] -}: { - chatId?: string; - contentId?: string; - responseData?: ChatHistoryItemResType[]; -}) => { +const ResponseTags = ({ responseData = [] }: { responseData?: ChatHistoryItemResType[] }) => { const { isPc } = useGlobalStore(); const { t } = useTranslation(); const [quoteModalData, setQuoteModalData] = useState(); @@ -41,9 +33,12 @@ const ResponseTags = ({ return { chatAccount: responseData.filter((item) => item.moduleType === FlowModuleTypeEnum.chatNode) .length, - quoteList: chatData?.quoteList, + quoteList: responseData + .filter((item) => item.moduleType === FlowModuleTypeEnum.chatNode) + .map((item) => item.quoteList) + .flat(), historyPreview: chatData?.historyPreview, - runningTime: responseData.reduce((sum, item) => sum + (item.runningTime || 0), 0).toFixed(2) + runningTime: +responseData.reduce((sum, item) => sum + (item.runningTime || 0), 0).toFixed(2) }; }, [responseData]); @@ -56,20 +51,20 @@ const ResponseTags = ({ return responseData.length === 0 ? null : ( + {quoteList.length > 0 && ( + + setQuoteModalData(quoteList)} + > + {quoteList.length}条引用 + + + )} {chatAccount === 1 && ( <> - {quoteList.length > 0 && ( - - setQuoteModalData(quoteList)} - > - {quoteList.length}条引用 - - - )} {historyPreview.length > 0 && ( ChatSiteItemType[]; resetVariables: (data?: Record) => void; - resetHistory: (chatId: ChatSiteItemType[]) => void; + resetHistory: (history: ChatSiteItemType[]) => void; scrollToBottom: (behavior?: 'smooth' | 'auto') => void; }; @@ -96,7 +96,6 @@ type Props = { showMarkIcon?: boolean; // admin mark dataset showVoiceIcon?: boolean; showEmptyIntro?: boolean; - chatId?: string; appAvatar?: string; userAvatar?: string; userGuideModule?: AppModuleItemType; @@ -116,7 +115,6 @@ const ChatBox = ( showMarkIcon = false, showVoiceIcon = true, showEmptyIntro = false, - chatId, appAvatar, userAvatar, userGuideModule, @@ -265,7 +263,8 @@ const ChatBox = ( const result = await postQuestionGuide( { - messages: adaptChat2GptMessages({ messages: history, reserveId: false }).slice(-6) + messages: adaptChat2GptMessages({ messages: history, reserveId: false }).slice(-6), + shareId: router.query.shareId as string }, abortSignal ); @@ -277,7 +276,7 @@ const ChatBox = ( } } catch (error) {} }, - [questionGuide, scrollToBottom] + [questionGuide, scrollToBottom, router.query.shareId] ); /** @@ -743,11 +742,7 @@ const ChatBox = ( source={item.value} isChatting={index === chatHistory.length - 1 && isChatting} /> - + {/* question guide */} {index === chatHistory.length - 1 && !isChatting && diff --git a/projects/app/src/components/Markdown/chat/Guide.tsx b/projects/app/src/components/Markdown/chat/Guide.tsx index 01ac3fb1b3cb..938de7b70403 100644 --- a/projects/app/src/components/Markdown/chat/Guide.tsx +++ b/projects/app/src/components/Markdown/chat/Guide.tsx @@ -20,19 +20,17 @@ function MyLink(e: any) { {text} ) : ( - - - { - event.emit('guideClick', { text }); - }} - > - {text} - + + { + event.emit('guideClick', { text }); + }} + > + {text} ); @@ -40,9 +38,10 @@ function MyLink(e: any) { const Guide = ({ text }: { text: string }) => { const formatText = useMemo( - () => text.replace(/\[(.*?)\]($|\n)/g, '[$1]()\n').replace(/\\n/g, '\n '), + () => text.replace(/\[(.*?)\]($|\n)/g, '[$1]()').replace(/\\n/g, '\n '), [text] ); + return ( ) @@ -262,3 +271,54 @@ const RenderInput = ({ }; export default React.memo(RenderInput); + +function RenderSelectApp({ app, moduleId }: { app: FlowInputItemType; moduleId: string }) { + const { onChangeNode, appId } = useFlowStore(); + const theme = useTheme(); + + const { + isOpen: isOpenSelectApp, + onOpen: onOpenSelectApp, + onClose: onCloseSelectApp + } = useDisclosure(); + + const value = app.value as SelectAppItemType | undefined; + + return ( + <> + + {!value ? ( + + ) : ( + + + + {value?.name} + + + )} + + + {isOpenSelectApp && ( + { + onChangeNode({ + moduleId, + type: 'inputs', + key: 'app', + value: { + ...app, + value: e[0] + } + }); + }} + /> + )} + + ); +} diff --git a/projects/app/src/pages/app/detail/components/AdEdit/index.tsx b/projects/app/src/pages/app/detail/components/AdEdit/index.tsx index 17b68e371712..ceeb78ad8933 100644 --- a/projects/app/src/pages/app/detail/components/AdEdit/index.tsx +++ b/projects/app/src/pages/app/detail/components/AdEdit/index.tsx @@ -38,6 +38,7 @@ const NodeVariable = dynamic(() => import('./components/Nodes/NodeVariable')); const NodeUserGuide = dynamic(() => import('./components/Nodes/NodeUserGuide')); const NodeExtract = dynamic(() => import('./components/Nodes/NodeExtract')); const NodeHttp = dynamic(() => import('./components/Nodes/NodeHttp')); +const NodeAPP = dynamic(() => import('./components/Nodes/NodeAPP')); import 'reactflow/dist/style.css'; @@ -52,7 +53,8 @@ const nodeTypes = { [FlowModuleTypeEnum.answerNode]: NodeAnswer, [FlowModuleTypeEnum.classifyQuestion]: NodeCQNode, [FlowModuleTypeEnum.contentExtract]: NodeExtract, - [FlowModuleTypeEnum.httpRequest]: NodeHttp + [FlowModuleTypeEnum.httpRequest]: NodeHttp, + [FlowModuleTypeEnum.app]: NodeAPP // [FlowModuleTypeEnum.empty]: EmptyModule }; const edgeTypes = { @@ -116,8 +118,17 @@ function FlowHeader({ app, onCloseSettings }: Props & {}) { const { mutate: onclickSave, isLoading } = useRequest({ mutationFn: () => { + const modules = flow2AppModules(); + // check required connect + for (let i = 0; i < modules.length; i++) { + const item = modules[i]; + if (item.inputs.find((input) => input.required && !input.connected)) { + return Promise.reject(`【${item.name}】存在未连接的必填输入`); + } + } + return updateAppDetail(app._id, { - modules: flow2AppModules(), + modules: modules, type: AppTypeEnum.advanced }); }, @@ -314,7 +325,7 @@ const Flow = (data: Props) => { return ( - + {!!data.app._id && } diff --git a/projects/app/src/pages/app/detail/components/DatasetSelectModal.tsx b/projects/app/src/pages/app/detail/components/DatasetSelectModal.tsx index b064ae02ad54..cba1a2e7c2b1 100644 --- a/projects/app/src/pages/app/detail/components/DatasetSelectModal.tsx +++ b/projects/app/src/pages/app/detail/components/DatasetSelectModal.tsx @@ -69,7 +69,12 @@ export const DatasetSelectModal = ({ tips={'仅能选择同一个索引模型的知识库'} onClose={onClose} > - + {filterKbList.selected.map((item) => (() => { diff --git a/projects/app/src/pages/app/detail/components/Logs.tsx b/projects/app/src/pages/app/detail/components/Logs.tsx index 41c2d6c17d29..5461fb258b8d 100644 --- a/projects/app/src/pages/app/detail/components/Logs.tsx +++ b/projects/app/src/pages/app/detail/components/Logs.tsx @@ -287,7 +287,6 @@ function DetailLogsModal({ void; + onSuccess: (e: SelectAppItemType[]) => void; +}) => { + const { t } = useTranslation(); + const { Loading } = useLoading(); + const theme = useTheme(); + const [selectedApps, setSelectedApps] = React.useState(defaultApps); + /* 加载模型 */ + const { data = [], isLoading } = useQuery(['loadMyApos'], () => getMyModels()); + + const apps = useMemo( + () => data.filter((app) => !filterApps.includes(app._id)), + [data, filterApps] + ); + + return ( + 1 ? `(${selectedApps.length}/${max})` : ''}`} + onClose={onClose} + w={'700px'} + position={'relative'} + > + + {apps.map((app) => ( + { + setSelectedApps(selectedApps.filter((e) => e !== app._id)); + } + } + : { + onClick: () => { + if (max === 1) { + setSelectedApps([app._id]); + } else if (selectedApps.length < max) { + setSelectedApps([...selectedApps, app._id]); + } + } + })} + > + + + {app.name} + + + ))} + + + + + + + + ); +}; + +export default React.memo(SelectAppModal); diff --git a/projects/app/src/pages/app/list/index.tsx b/projects/app/src/pages/app/list/index.tsx index 2bde0ddb87d4..329f9c899200 100644 --- a/projects/app/src/pages/app/list/index.tsx +++ b/projects/app/src/pages/app/list/index.tsx @@ -64,7 +64,7 @@ const MyApps = () => { ); /* 加载模型 */ - useQuery(['loadModels'], () => loadMyApps(true), { + useQuery(['loadApps'], () => loadMyApps(true), { refetchOnMount: true }); diff --git a/projects/app/src/pages/chat/components/ToolMenu.tsx b/projects/app/src/pages/chat/components/ToolMenu.tsx index d74bd50cecec..667652748db0 100644 --- a/projects/app/src/pages/chat/components/ToolMenu.tsx +++ b/projects/app/src/pages/chat/components/ToolMenu.tsx @@ -8,7 +8,6 @@ import { useRouter } from 'next/router'; const ToolMenu = ({ history }: { history: ChatItemType[] }) => { const { onExportChat } = useChatBox(); const router = useRouter(); - const { appId, shareId } = router.query; const menuList = useMemo( () => [ @@ -18,8 +17,8 @@ const ToolMenu = ({ history }: { history: ChatItemType[] }) => { onClick: () => { router.replace({ query: { - appId, - shareId + ...router.query, + chatId: '' } }); } @@ -36,7 +35,7 @@ const ToolMenu = ({ history }: { history: ChatItemType[] }) => { }, { icon: 'pdf', label: 'PDF导出', onClick: () => onExportChat({ type: 'pdf', history }) } ], - [appId, history, onExportChat, router, shareId] + [history, onExportChat, router] ); return history.length > 0 ? ( diff --git a/projects/app/src/pages/chat/index.tsx b/projects/app/src/pages/chat/index.tsx index 0368161dff61..d1c89c34f5ce 100644 --- a/projects/app/src/pages/chat/index.tsx +++ b/projects/app/src/pages/chat/index.tsx @@ -358,7 +358,6 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => { { const router = useRouter(); @@ -89,9 +91,8 @@ const OutLink = ({ forbidRefresh.current = true; router.replace({ query: { - shareId, - chatId: completionChatId, - authToken + ...router.query, + chatId: completionChatId } }); } @@ -174,59 +175,58 @@ const OutLink = ({ {shareChatData.app.name} - {((children: React.ReactNode) => { - return isPc ? ( - {children} - ) : ( - - - - {children} - - - ); - })( - ({ - id: item.chatId, - title: item.title - }))} - onClose={onCloseSlider} - onChangeChat={(chatId) => { - console.log(chatId); - - router.replace({ - query: { - chatId: chatId || '', - shareId, - authToken - } - }); - if (!isPc) { - onCloseSlider(); - } - }} - onDelHistory={delOneShareHistoryByChatId} - onClearHistory={() => { - delManyShareChatHistoryByShareId(shareId); - router.replace({ - query: { - shareId, - authToken - } - }); - }} - /> - )} + {showHistory === '1' + ? ((children: React.ReactNode) => { + return isPc ? ( + {children} + ) : ( + + + + {children} + + + ); + })( + ({ + id: item.chatId, + title: item.title + }))} + onClose={onCloseSlider} + onChangeChat={(chatId) => { + router.replace({ + query: { + ...router.query, + chatId: chatId || '' + } + }); + if (!isPc) { + onCloseSlider(); + } + }} + onDelHistory={delOneShareHistoryByChatId} + onClearHistory={() => { + delManyShareChatHistoryByShareId(shareId); + router.replace({ + query: { + ...router.query, + chatId: '' + } + }); + }} + /> + ) + : null} {/* chat container */} => { const { moduleName, - userOpenaiAccount, + user, inputs: { agents, userChatInput } } = props as Props; @@ -53,7 +53,7 @@ export const dispatchClassifyQuestion = async (props: Props): Promise { const { moduleName, - userOpenaiAccount, + user, inputs: { content, description, extractKeys } } = props; @@ -73,7 +73,7 @@ export async function dispatchContentExtract(props: Props): Promise { [TaskResponseKeyEnum.responseData]: { moduleType: FlowModuleTypeEnum.contentExtract, moduleName, - price: userOpenaiAccount?.key ? 0 : extractModel.price * tokens, + price: user.openaiAccount?.key ? 0 : extractModel.price * tokens, model: extractModel.name || '', tokens, extractDescription: description, @@ -83,7 +83,7 @@ export async function dispatchContentExtract(props: Props): Promise { } async function functionCall({ - userOpenaiAccount, + user, inputs: { history = [], content, extractKeys, description } }: Props) { const extractModel = global.extractModel; @@ -126,7 +126,7 @@ async function functionCall({ } }; - const chatAPI = getAIChatApi(userOpenaiAccount); + const chatAPI = getAIChatApi(user.openaiAccount); const response = await chatAPI.createChatCompletion( { @@ -137,7 +137,7 @@ async function functionCall({ functions: [agentFunction] }, { - ...axiosConfig(userOpenaiAccount) + ...axiosConfig(user.openaiAccount) } ); @@ -157,7 +157,7 @@ async function functionCall({ } async function completions({ - userOpenaiAccount, + user, inputs: { history = [], content, extractKeys, description } }: Props) { const extractModel = global.extractModel; @@ -181,7 +181,7 @@ Human: ${content}` } ]; - const chatAPI = getAIChatApi(userOpenaiAccount); + const chatAPI = getAIChatApi(user.openaiAccount); const { data } = await chatAPI.createChatCompletion( { @@ -192,7 +192,7 @@ Human: ${content}` }, { timeout: 480000, - ...axiosConfig(userOpenaiAccount) + ...axiosConfig(user.openaiAccount) } ); const answer = data.choices?.[0].message?.content || ''; diff --git a/projects/app/src/service/moduleDispatch/chat/oneapi.ts b/projects/app/src/service/moduleDispatch/chat/oneapi.ts index 7bbde5f26550..50e0d8c22a0b 100644 --- a/projects/app/src/service/moduleDispatch/chat/oneapi.ts +++ b/projects/app/src/service/moduleDispatch/chat/oneapi.ts @@ -5,13 +5,13 @@ import type { ChatHistoryItemResType } from '@/types/chat'; import { ChatRoleEnum, sseResponseEventEnum } from '@/constants/chat'; import { SSEParseData, parseStreamChunk } from '@/utils/sse'; import { textAdaptGptResponse } from '@/utils/adapt'; -import { getAIChatApi, axiosConfig } from '@fastgpt/core/aiApi/config'; +import { getAIChatApi, axiosConfig } from '@fastgpt/core/ai/config'; import { TaskResponseKeyEnum } from '@/constants/chat'; import { getChatModel } from '@/service/utils/data'; import { countModelPrice } from '@/service/common/bill/push'; import { ChatModelItemType } from '@/types/model'; import { textCensor } from '@/api/service/plugins'; -import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/core/aiApi/constant'; +import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/core/ai/constant'; import { AppModuleItemType } from '@/types/app'; import { countMessagesTokens, sliceMessagesTB } from '@/utils/common/tiktoken'; import { adaptChat2GptMessages } from '@/utils/common/adapt/message'; @@ -35,6 +35,7 @@ export type ChatProps = ModuleDispatchProps< export type ChatResponse = { [TaskResponseKeyEnum.answerText]: string; [TaskResponseKeyEnum.responseData]: ChatHistoryItemResType; + [TaskResponseKeyEnum.history]: ChatItemType[]; finish: boolean; }; @@ -45,7 +46,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise; export type AnswerResponse = { - [TaskResponseKeyEnum.answerText]: string; finish: boolean; }; @@ -29,7 +28,6 @@ export const dispatchAnswer = (props: Record): AnswerResponse => { } return { - [TaskResponseKeyEnum.answerText]: text, finish: true }; }; diff --git a/projects/app/src/service/moduleDispatch/tools/runApp.ts b/projects/app/src/service/moduleDispatch/tools/runApp.ts new file mode 100644 index 000000000000..6f8eb699178a --- /dev/null +++ b/projects/app/src/service/moduleDispatch/tools/runApp.ts @@ -0,0 +1,80 @@ +import { ChatHistoryItemResType, ChatItemType } from '@/types/chat'; +import type { ModuleDispatchProps } from '@/types/core/chat/type'; +import { SelectAppItemType } from '@/types/core/app/flow'; +import { dispatchModules } from '@/pages/api/v1/chat/completions'; +import { App } from '@/service/mongo'; +import { responseWrite } from '@/service/common/stream'; +import { ChatRoleEnum, TaskResponseKeyEnum, sseResponseEventEnum } from '@/constants/chat'; +import { textAdaptGptResponse } from '@/utils/adapt'; + +type Props = ModuleDispatchProps<{ + userChatInput: string; + history?: ChatItemType[]; + app: SelectAppItemType; +}>; +type Response = { + finish: boolean; + [TaskResponseKeyEnum.responseData]: ChatHistoryItemResType[]; + [TaskResponseKeyEnum.answerText]: string; + [TaskResponseKeyEnum.history]: ChatItemType[]; +}; + +export const dispatchAppRequest = async (props: Record): Promise => { + const { + res, + variables, + user, + stream, + detail, + inputs: { userChatInput, history = [], app } + } = props as Props; + + if (!userChatInput) { + return Promise.reject('Input is empty'); + } + + const appData = await App.findById(app.id); + + if (!appData) { + return Promise.reject('App not found'); + } + + responseWrite({ + res, + event: detail ? sseResponseEventEnum.answer : undefined, + data: textAdaptGptResponse({ + text: '\n' + }) + }); + + const { responseData, answerText } = await dispatchModules({ + res, + modules: appData.modules, + user, + variables, + params: { + history, + userChatInput + }, + stream, + detail + }); + + const completeMessages = history.concat([ + { + obj: ChatRoleEnum.Human, + value: userChatInput + }, + { + obj: ChatRoleEnum.AI, + value: answerText + } + ]); + + return { + finish: true, + responseData, + [TaskResponseKeyEnum.answerText]: answerText, + [TaskResponseKeyEnum.history]: completeMessages + }; +}; diff --git a/projects/app/src/service/support/outLink/auth.ts b/projects/app/src/service/support/outLink/auth.ts index 0601acb747c7..ab31a5bf2f6e 100644 --- a/projects/app/src/service/support/outLink/auth.ts +++ b/projects/app/src/service/support/outLink/auth.ts @@ -102,8 +102,23 @@ export async function authOutLinkLimit({ await authShareStart({ authToken, tokenUrl: outLink.limit.hookUrl, question }); } +export async function authOutLinkId({ id }: { id: string }) { + const outLink = await OutLink.findOne({ + shareId: id + }); + + if (!outLink) { + return Promise.reject('分享链接无效'); + } + + return { + userId: String(outLink.userId) + }; +} + type TokenAuthResponseType = { success: boolean; + msg?: string; message?: string; }; @@ -119,7 +134,7 @@ export const authShareChatInit = async (authToken?: string, tokenUrl?: string) = } }); if (data?.success !== true) { - return Promise.reject(data?.message || '身份校验失败'); + return Promise.reject(data?.message || data?.msg || '身份校验失败'); } } catch (error) { return Promise.reject('身份校验失败'); @@ -148,7 +163,7 @@ export const authShareStart = async ({ }); if (data?.success !== true) { - return Promise.reject(data?.message || '身份校验失败'); + return Promise.reject(data?.message || data?.msg || '身份校验失败'); } } catch (error) { return Promise.reject('身份校验失败'); diff --git a/projects/app/src/service/utils/auth.ts b/projects/app/src/service/utils/auth.ts index 257942bb6022..1625b3937479 100644 --- a/projects/app/src/service/utils/auth.ts +++ b/projects/app/src/service/utils/auth.ts @@ -1,15 +1,17 @@ import type { NextApiRequest } from 'next'; import Cookie from 'cookie'; -import { App, OpenApi, User, KB } from '../mongo'; +import { App, User, KB } from '../mongo'; import type { AppSchema, UserModelSchema } from '@/types/mongoSchema'; import { ERROR_ENUM } from '../errorCode'; import { authJWT } from './tools'; import { authOpenApiKey } from '../support/openapi/auth'; +import { authOutLinkId } from '../support/outLink/auth'; export enum AuthUserTypeEnum { token = 'token', root = 'root', - apikey = 'apikey' + apikey = 'apikey', + outLink = 'outLink' } /* auth balance */ @@ -34,13 +36,15 @@ export const authUser = async ({ authToken = false, authRoot = false, authApiKey = false, - authBalance = false + authBalance = false, + authOutLink }: { req: NextApiRequest; authToken?: boolean; authRoot?: boolean; authApiKey?: boolean; authBalance?: boolean; + authOutLink?: boolean; }) => { const authCookieToken = async (cookie?: string, token?: string): Promise => { // 获取 cookie @@ -107,13 +111,18 @@ export const authUser = async ({ userid?: string; authorization?: string; }; + const { shareId } = (req?.body || {}) as { shareId?: string }; let uid = ''; let appId = ''; let openApiKey = apikey; let authType: `${AuthUserTypeEnum}` = AuthUserTypeEnum.token; - if (authToken && (cookie || token)) { + if (authOutLink && shareId) { + const res = await authOutLinkId({ id: shareId }); + uid = res.userId; + authType = AuthUserTypeEnum.outLink; + } else if (authToken && (cookie || token)) { // user token(from fastgpt web) uid = await authCookieToken(cookie, token); authType = AuthUserTypeEnum.token; diff --git a/projects/app/src/types/core/app/flow.d.ts b/projects/app/src/types/core/app/flow.d.ts index 7a67f03c44e1..8ff172e72247 100644 --- a/projects/app/src/types/core/app/flow.d.ts +++ b/projects/app/src/types/core/app/flow.d.ts @@ -60,3 +60,9 @@ export type FlowModuleTemplateType = { export type FlowModuleItemType = FlowModuleTemplateType & { moduleId: string; }; + +export type SelectAppItemType = { + id: string; + name: string; + logo: string; +}; diff --git a/projects/app/src/types/core/chat/type.d.ts b/projects/app/src/types/core/chat/type.d.ts index 9ee395d22759..5f1e702ef237 100644 --- a/projects/app/src/types/core/chat/type.d.ts +++ b/projects/app/src/types/core/chat/type.d.ts @@ -1,4 +1,4 @@ -import type { ChatCompletionRequestMessage } from '@fastgpt/core/aiApi/type'; +import type { ChatCompletionRequestMessage } from '@fastgpt/core/ai/type'; import type { NextApiResponse } from 'next'; import { RunningModuleItemType } from '@/types/app'; import { UserModelSchema } from '@/types/mongoSchema'; @@ -13,6 +13,6 @@ export type ModuleDispatchProps = { detail: boolean; variables: Record; outputs: RunningModuleItemType['outputs']; - userOpenaiAccount?: UserModelSchema['openaiAccount']; + user: UserModelSchema; inputs: T; }; diff --git a/projects/app/src/utils/adapt.ts b/projects/app/src/utils/adapt.ts index ba5f600d18cb..433424918545 100644 --- a/projects/app/src/utils/adapt.ts +++ b/projects/app/src/utils/adapt.ts @@ -2,7 +2,7 @@ import { formatPrice } from '@fastgpt/common/bill/index'; import type { BillSchema } from '@/types/common/bill'; import type { UserBillType } from '@/types/user'; import { ChatItemType } from '@/types/chat'; -import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/core/aiApi/constant'; +import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/core/ai/constant'; import { ChatRoleEnum } from '@/constants/chat'; import type { MessageItemType } from '@/types/core/chat/type'; import type { AppModuleItemType } from '@/types/app'; diff --git a/projects/app/src/utils/common/adapt/message.ts b/projects/app/src/utils/common/adapt/message.ts index 936de5d79d5b..8dd2257527e6 100644 --- a/projects/app/src/utils/common/adapt/message.ts +++ b/projects/app/src/utils/common/adapt/message.ts @@ -1,6 +1,6 @@ import type { ChatItemType } from '@/types/chat'; import { ChatRoleEnum } from '@/constants/chat'; -import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/core/aiApi/constant'; +import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/core/ai/constant'; import type { MessageItemType } from '@/types/core/chat/type'; const chat2Message = { diff --git a/projects/app/src/utils/common/tiktoken/index.ts b/projects/app/src/utils/common/tiktoken/index.ts index 83435dd5888a..8c785fbb7697 100644 --- a/projects/app/src/utils/common/tiktoken/index.ts +++ b/projects/app/src/utils/common/tiktoken/index.ts @@ -2,7 +2,7 @@ import { ChatItemType } from '@/types/chat'; import { Tiktoken } from 'js-tiktoken/lite'; import { adaptChat2GptMessages } from '../adapt/message'; -import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/core/aiApi/constant'; +import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/core/ai/constant'; import encodingJson from './cl100k_base.json'; /* init tikToken obj */