From 3fd837c22150ab2b327f4e1c7b024e82ebceb720 Mon Sep 17 00:00:00 2001 From: YeYuheng <57035043+YYH211@users.noreply.github.com> Date: Mon, 8 Dec 2025 12:59:43 +0800 Subject: [PATCH] Complete agent parent (#6049) * add role and tools filling * add: file-upload --------- Co-authored-by: xxyyh <2289112474@qq> --- .../global/core/chat/helperBot/adaptor.ts | 4 +- packages/global/core/chat/helperBot/type.ts | 13 +- .../global/core/workflow/runtime/constants.ts | 4 +- .../chat/HelperBot/dispatch/topAgent/index.ts | 208 ++++++++++++++++-- .../HelperBot/dispatch/topAgent/prompt.ts | 8 +- .../core/chat/HelperBot/dispatch/type.ts | 14 +- packages/service/core/chat/HelperBot/utils.ts | 2 +- packages/web/i18n/zh-CN/app.json | 2 + .../components/core/chat/HelperBot/index.tsx | 35 +-- .../app/detail/Edit/ChatAgent/ChatTest.tsx | 120 +++++++++- .../app/detail/Edit/ChatAgent/Edit.tsx | 1 + .../app/detail/Edit/ChatAgent/utils.ts | 23 +- .../api/core/chat/helperBot/completions.ts | 13 +- projects/app/src/web/common/api/fetch.ts | 9 + 14 files changed, 396 insertions(+), 60 deletions(-) diff --git a/packages/global/core/chat/helperBot/adaptor.ts b/packages/global/core/chat/helperBot/adaptor.ts index e32785215..bd8089e6a 100644 --- a/packages/global/core/chat/helperBot/adaptor.ts +++ b/packages/global/core/chat/helperBot/adaptor.ts @@ -1,4 +1,4 @@ -import { ChatCompletionRequestMessageRoleEnum } from 'core/ai/constants'; +import { ChatCompletionRequestMessageRoleEnum } from '../../ai/constants'; import type { ChatCompletionContentPart, ChatCompletionFunctionMessageParam, @@ -96,7 +96,7 @@ export const helperChats2GPTMessages = ({ tool_calls }); aiResults.push(...toolResponse); - } else if ('text' in value && value.text?.content === 'string') { + } else if ('text' in value && typeof value.text?.content === 'string') { if (!value.text.content && item.value.length > 1) { return; } diff --git a/packages/global/core/chat/helperBot/type.ts b/packages/global/core/chat/helperBot/type.ts index 5ff7d40ce..55d798f55 100644 --- a/packages/global/core/chat/helperBot/type.ts +++ b/packages/global/core/chat/helperBot/type.ts @@ -78,11 +78,22 @@ export type HelperBotChatItemSiteType = z.infer; + export const topAgentParamsSchema = z.object({ role: z.string().nullish(), taskObject: z.string().nullish(), selectedTools: z.array(z.string()).nullish(), selectedDatasets: z.array(z.string()).nullish(), - fileUpload: z.boolean().nullish() + fileUpload: z.boolean().nullish(), + // AI 模型配置 + modelConfig: AIModelConfigSchema.nullish() }); export type TopAgentParamsType = z.infer; diff --git a/packages/global/core/workflow/runtime/constants.ts b/packages/global/core/workflow/runtime/constants.ts index 9149dcfb5..d74e44fe8 100644 --- a/packages/global/core/workflow/runtime/constants.ts +++ b/packages/global/core/workflow/runtime/constants.ts @@ -17,7 +17,9 @@ export enum SseResponseEventEnum { interactive = 'interactive', - agentPlan = 'agentPlan' // agent plan + agentPlan = 'agentPlan', // agent plan + + formData = 'formData' // form data for TopAgent } export enum DispatchNodeResponseKeyEnum { diff --git a/packages/service/core/chat/HelperBot/dispatch/topAgent/index.ts b/packages/service/core/chat/HelperBot/dispatch/topAgent/index.ts index ab3beccb5..415855ddd 100644 --- a/packages/service/core/chat/HelperBot/dispatch/topAgent/index.ts +++ b/packages/service/core/chat/HelperBot/dispatch/topAgent/index.ts @@ -1,35 +1,207 @@ import type { HelperBotDispatchParamsType, HelperBotDispatchResponseType } from '../type'; import { helperChats2GPTMessages } from '@fastgpt/global/core/chat/helperBot/adaptor'; +import { getPrompt } from './prompt'; +import { createLLMResponse } from '../../../../ai/llm/request'; +import { getLLMModel } from '../../../../ai/model'; +import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants'; +import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils'; +import type { AIChatItemValueItemType } from '@fastgpt/global/core/chat/helperBot/type'; +import { getSystemToolsWithInstalled } from '../../../../app/tool/controller'; export const dispatchTopAgent = async ( props: HelperBotDispatchParamsType ): Promise => { - const { query, files, metadata, histories } = props; - const messages = helperChats2GPTMessages({ + const { query, files, metadata, histories, workflowResponseWrite, teamId, userId } = props; + + const modelConfig = metadata.data?.modelConfig; + + const modelName = modelConfig?.model || global.systemDefaultModel?.llm?.model; + if (!modelName) { + throw new Error('未配置 LLM 模型,请在前端选择模型或在系统中配置默认模型'); + } + const modelData = getLLMModel(modelName); + if (!modelData) { + throw new Error(`模型 ${modelName} 未找到`); + } + + const temperature = modelConfig?.temperature ?? 0.7; + const maxToken = modelConfig?.maxToken ?? 4000; + const stream = modelConfig?.stream ?? true; + + const resourceList = await generateResourceList({ + teamId, + userId + }); + + const historyMessages = helperChats2GPTMessages({ messages: histories, reserveTool: false }); - // 拿工具资源参考 FastGPT/projects/app/src/pages/api/core/app/tool/getSystemToolTemplates.ts - /* - 流输出 - onReasoning({ text }) { - if (!aiChatReasoning) return; - workflowStreamResponse?.({ + const systemPrompt = getPrompt({ resourceList }); + const conversationMessages = [ + { role: 'system' as const, content: systemPrompt }, + ...historyMessages, + { role: 'user' as const, content: query } + ]; + + // console.log('📝 TopAgent 阶段 1: 信息收集'); + // console.log('conversationMessages:', conversationMessages); + + const llmResponse = await createLLMResponse({ + body: { + messages: conversationMessages, + model: modelName, + temperature, + stream, + max_tokens: maxToken + }, + onStreaming: ({ text }) => { + workflowResponseWrite?.({ + event: SseResponseEventEnum.answer, + data: textAdaptGptResponse({ text }) + }); + }, + onReasoning: ({ text }) => { + workflowResponseWrite?.({ + event: SseResponseEventEnum.answer, + data: textAdaptGptResponse({ reasoning_content: text }) + }); + } + }); + + const firstPhaseAnswer = llmResponse.answerText; + const firstPhaseReasoning = llmResponse.reasoningText; + + // 尝试解析信息收集阶段的 JSON 响应 + let parsedResponse: { reasoning?: string; question?: string } | null = null; + try { + parsedResponse = JSON.parse(firstPhaseAnswer); + } catch (e) { + // 如果解析失败,说明不是 JSON 格式,可能是普通文本 + parsedResponse = null; + } + + if (firstPhaseAnswer.includes('「信息收集已完成」')) { + console.log('🔄 TopAgent: 检测到信息收集完成信号,切换到计划生成阶段'); + + const newMessages = [ + ...conversationMessages, + { role: 'assistant' as const, content: firstPhaseAnswer }, + { role: 'user' as const, content: '请你直接生成规划方案' } + ]; + + // console.log('📋 TopAgent 阶段 2: 计划生成'); + + const planResponse = await createLLMResponse({ + body: { + messages: newMessages, + model: modelName, + temperature, + stream, + max_tokens: maxToken + }, + onStreaming: ({ text }) => { + workflowResponseWrite?.({ event: SseResponseEventEnum.answer, - data: textAdaptGptResponse({ - reasoning_content: text - }) + data: textAdaptGptResponse({ text }) }); }, - onStreaming({ text }) { - if (!isResponseAnswerText) return; - workflowStreamResponse?.({ + onReasoning: ({ text }) => { + workflowResponseWrite?.({ event: SseResponseEventEnum.answer, - data: textAdaptGptResponse({ - text - }) + data: textAdaptGptResponse({ reasoning_content: text }) }); } - */ + }); + + let formData; + try { + const planJson = JSON.parse(planResponse.answerText); + // console.log('解析的计划 JSON:', planJson); + + formData = { + role: planJson.task_analysis?.role || '', + taskObject: planJson.task_analysis?.goal || '', + tools: planJson.resources?.tools?.map((tool: any) => tool.id) || [], + fileUploadEnabled: planJson.resources?.system_features?.file_upload?.enabled || false + }; + } catch (e) { + console.error('解析计划 JSON 失败:', e); + } + + return { + aiResponse: formatAIResponse(planResponse.answerText, planResponse.reasoningText), + formData + }; + } + + const displayText = parsedResponse?.question || firstPhaseAnswer; + return { + aiResponse: formatAIResponse(displayText, firstPhaseReasoning) + }; +}; + +const generateResourceList = async ({ + teamId, + userId +}: { + teamId: string; + userId: string; +}): Promise => { + let result = '\n## 可用资源列表\n'; + + const tools = await getSystemToolsWithInstalled({ + teamId, + isRoot: true // TODO: 需要传入实际的 isRoot 值 + }); + + const installedTools = tools.filter((tool) => { + return tool.installed && !tool.isFolder; + }); + + if (installedTools.length > 0) { + result += '### 工具\n'; + installedTools.forEach((tool) => { + const toolId = tool.id; + const name = + typeof tool.name === 'string' + ? tool.name + : tool.name?.en || tool.name?.['zh-CN'] || '未命名'; + const intro = + typeof tool.intro === 'string' ? tool.intro : tool.intro?.en || tool.intro?.['zh-CN'] || ''; + const description = tool.toolDescription || intro || '暂无描述'; + result += `- **${toolId}** [工具]: ${name} - ${description}\n`; + }); + } else { + result += '### 工具\n暂无已安装的工具\n'; + } + + // TODO: 知识库 + result += '\n### 知识库\n暂未配置知识库\n'; + + result += '\n### 系统功能\n'; + result += '- **file_upload**: 文件上传功能 (enabled, purpose, file_types)\n'; + + return result; +}; + +const formatAIResponse = (text: string, reasoning?: string): AIChatItemValueItemType[] => { + const result: AIChatItemValueItemType[] = []; + + if (reasoning) { + result.push({ + reasoning: { + content: reasoning + } + }); + } + + result.push({ + text: { + content: text + } + }); + + return result; }; diff --git a/packages/service/core/chat/HelperBot/dispatch/topAgent/prompt.ts b/packages/service/core/chat/HelperBot/dispatch/topAgent/prompt.ts index 2baadc002..ffa7c6e34 100644 --- a/packages/service/core/chat/HelperBot/dispatch/topAgent/prompt.ts +++ b/packages/service/core/chat/HelperBot/dispatch/topAgent/prompt.ts @@ -253,9 +253,11 @@ ${resourceList} - 形成完整的 resources 配置 **输出要求**: -**重要:只输出JSON,不要添加任何解释文字、代码块标记或其他内容!** +**重要** +1. 只输出JSON规定的字段,不要添加任何解释文字、代码块标记或其他内容! +2. 千万不能添加不属于以下模板中的字段到最终的结果中 -直接输出以下格式的JSON: +直接输出以下格式的JSON(千万不要添加其他字段进来): { "task_analysis": { "goal": "任务的核心目标描述", @@ -353,7 +355,7 @@ ${resourceList} - ❌ 不要使用旧格式的 tools 字段,必须使用 resources 结构 - ❌ 不要添加任何解释性文字或前言后语 - ✅ 必须使用 resources 对象,包含 tools、knowledges、system_features -- ✅ file_upload.enabled=true 时必须提供 purpose 字段 +- ✅ file_upload.enabled=true 时必须提供 purpose 字段, - ✅ knowledges 或 tools 可以为空数组(如果不需要) - ✅ 直接、纯净地输出JSON内容 diff --git a/packages/service/core/chat/HelperBot/dispatch/type.ts b/packages/service/core/chat/HelperBot/dispatch/type.ts index a0eac4a11..76a5cce64 100644 --- a/packages/service/core/chat/HelperBot/dispatch/type.ts +++ b/packages/service/core/chat/HelperBot/dispatch/type.ts @@ -11,11 +11,21 @@ export const HelperBotDispatchParamsSchema = z.object({ files: HelperBotCompletionsParamsSchema.shape.files, metadata: HelperBotCompletionsParamsSchema.shape.metadata, histories: z.array(HelperBotChatItemSchema), - workflowResponseWrite: WorkflowResponseFnSchema + workflowResponseWrite: WorkflowResponseFnSchema, + teamId: z.string(), + userId: z.string() }); export type HelperBotDispatchParamsType = z.infer; export const HelperBotDispatchResponseSchema = z.object({ - aiResponse: z.array(AIChatItemValueItemSchema) + aiResponse: z.array(AIChatItemValueItemSchema), + formData: z + .object({ + role: z.string().optional(), + taskObject: z.string().optional(), + tools: z.array(z.string()).optional(), + fileUploadEnabled: z.boolean().optional() + }) + .optional() }); export type HelperBotDispatchResponseType = z.infer; diff --git a/packages/service/core/chat/HelperBot/utils.ts b/packages/service/core/chat/HelperBot/utils.ts index b9d3045fd..761b71eda 100644 --- a/packages/service/core/chat/HelperBot/utils.ts +++ b/packages/service/core/chat/HelperBot/utils.ts @@ -7,7 +7,7 @@ import type { } from '@fastgpt/global/core/chat/type'; import { MongoHelperBotChatItem } from './chatItemSchema'; import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants'; -import { mongoSessionRun } from 'common/mongo/sessionRun'; +import { mongoSessionRun } from '../../../common/mongo/sessionRun'; export const pushChatRecords = async ({ type, diff --git a/packages/web/i18n/zh-CN/app.json b/packages/web/i18n/zh-CN/app.json index dadbf8cfd..fcc03d9b7 100644 --- a/packages/web/i18n/zh-CN/app.json +++ b/packages/web/i18n/zh-CN/app.json @@ -453,6 +453,8 @@ "toolkit_uninstalled": "未安装", "toolkit_update_failed": "更新失败", "toolkit_user_guide": "使用说明", + "tool_load_failed": "部分工具加载失败", + "failed_tools": "失败的工具", "tools": "工具", "tools_no_description": "这个工具没有介绍~", "tools_tip": "声明模型可用的工具,可以实现与外部系统交互等扩展能力", diff --git a/projects/app/src/components/core/chat/HelperBot/index.tsx b/projects/app/src/components/core/chat/HelperBot/index.tsx index 7f3042480..0b766a64a 100644 --- a/projects/app/src/components/core/chat/HelperBot/index.tsx +++ b/projects/app/src/components/core/chat/HelperBot/index.tsx @@ -34,7 +34,7 @@ import { streamFetch } from '@/web/common/api/fetch'; import type { generatingMessageProps } from '../ChatContainer/type'; import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants'; -const ChatBox = ({ type, metadata, ...props }: HelperBotProps) => { +const ChatBox = ({ type, metadata, onApply, ...props }: HelperBotProps) => { const { toast } = useToast(); const { t } = useTranslation(); @@ -144,7 +144,19 @@ const ChatBox = ({ type, metadata, ...props }: HelperBotProps) => { } ); const generatingMessage = useMemoizedFn( - ({ event, text = '', reasoningText, tool }: generatingMessageProps) => { + ({ event, text = '', reasoningText, tool, data }: generatingMessageProps & { data?: any }) => { + if (event === SseResponseEventEnum.formData && data) { + const formData = { + role: data.role || '', + taskObject: data.taskObject || '', + selectedTools: data.tools || [], + selectedDatasets: [], + fileUpload: data.fileUploadEnabled || false + }; + onApply?.(formData); + return; + } + setChatRecords((state) => state.map((item, index) => { if (index !== state.length - 1) return item; @@ -276,6 +288,7 @@ const ChatBox = ({ type, metadata, ...props }: HelperBotProps) => { const chatItemDataId = getNanoid(24); const newChatList: HelperBotChatItemSiteType[] = [ ...chatRecords, + // 用户消息 { _id: getNanoid(24), createTime: new Date(), @@ -289,6 +302,7 @@ const ChatBox = ({ type, metadata, ...props }: HelperBotProps) => { } ] }, + // AI 消息 - 空白,用于接收流式输出 { _id: getNanoid(24), createTime: new Date(), @@ -297,20 +311,7 @@ const ChatBox = ({ type, metadata, ...props }: HelperBotProps) => { value: [ { text: { - content: `我无法直接通过“读取静态网页工具”获取 GitHub(动态站点)上的实时信息,因此不能自动抓取 fastgpt 的 star 数量。 - -不过你可以告诉我: - -- 你指的是 **FastGPT(fastgpt-dev/FastGPT)** 吗? - GitHub 链接通常是:https://github.com/fastgpt-dev/FastGPT - -如果是这个项目,我可以根据我最新的训练数据给你一个**大致的历史 star 数范围**,或者你也可以让我协助编写脚本来实时查询它的 star。 - -你希望我: - -1. 提供现阶段近似 star 数(基于 2025 的大致趋势)? -2. 帮你写一个脚本实时查 GitHub API? -3. 还是提供该项目的介绍?` + content: '' } } ] @@ -340,7 +341,7 @@ const ChatBox = ({ type, metadata, ...props }: HelperBotProps) => { })), metadata: { type: 'topAgent', - data: {} + data: metadata } }, onMessage: generatingMessage, diff --git a/projects/app/src/pageComponents/app/detail/Edit/ChatAgent/ChatTest.tsx b/projects/app/src/pageComponents/app/detail/Edit/ChatAgent/ChatTest.tsx index 96e3dfdc0..8b9a52046 100644 --- a/projects/app/src/pageComponents/app/detail/Edit/ChatAgent/ChatTest.tsx +++ b/projects/app/src/pageComponents/app/detail/Edit/ChatAgent/ChatTest.tsx @@ -21,14 +21,19 @@ import type { Form2WorkflowFnType } from '../FormComponent/type'; import FillRowTabs from '@fastgpt/web/components/common/Tabs/FillRowTabs'; import HelperBot from '@/components/core/chat/HelperBot'; import { HelperBotTypeEnum } from '@fastgpt/global/core/chat/helperBot/type'; +import { getToolPreviewNode } from '@/web/core/app/api/tool'; +import type { FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/node'; +import { useToast } from '@fastgpt/web/hooks/useToast'; type Props = { appForm: AppFormEditFormType; + setAppForm?: React.Dispatch>; setRenderEdit: React.Dispatch>; form2WorkflowFn: Form2WorkflowFnType; }; -const ChatTest = ({ appForm, setRenderEdit, form2WorkflowFn }: Props) => { +const ChatTest = ({ appForm, setAppForm, setRenderEdit, form2WorkflowFn }: Props) => { const { t } = useTranslation(); + const { toast } = useToast(); const [activeTab, setActiveTab] = useSafeState<'helper' | 'chat_debug'>('helper'); @@ -58,6 +63,24 @@ const ChatTest = ({ appForm, setRenderEdit, form2WorkflowFn }: Props) => { isReady: true }); + // 构建 TopAgent metadata,从 appForm 中提取配置 + const topAgentMetadata = useMemo( + () => ({ + role: appForm.aiSettings.aiRole, + taskObject: appForm.aiSettings.aiTaskObject, + selectedTools: appForm.selectedTools.map((tool) => tool.id), + selectedDatasets: appForm.dataset.datasets.map((dataset) => dataset.datasetId), + fileUpload: false, // TODO: 从配置中获取文件上传设置 + modelConfig: { + model: appForm.aiSettings.model, + temperature: appForm.aiSettings.temperature, + maxToken: appForm.aiSettings.maxToken, + stream: true + } + }), + [appForm] + ); + return ( { {activeTab === 'helper' && ( - {}} /> + { + if (!setAppForm) { + console.warn('⚠️ setAppForm 未传入,无法更新表单'); + return; + } + + const existingToolIds = new Set( + appForm.selectedTools.map((tool) => tool.pluginId).filter(Boolean) + ); + + // console.log('📋 当前已存在的工具 pluginId:', Array.from(existingToolIds)); + // console.log('📋 formData.selectedTools:', formData.selectedTools); + + const newToolIds = (formData.selectedTools || []).filter( + (toolId: string) => !existingToolIds.has(toolId) + ); + + if (newToolIds.length === 0) { + // 没有新工具需要添加,仍然更新 role、taskObject 和文件上传配置 + setAppForm((prev) => ({ + ...prev, + aiSettings: { + ...prev.aiSettings, + aiRole: formData.role || '', + aiTaskObject: formData.taskObject || '' + }, + chatConfig: { + ...prev.chatConfig, + fileSelectConfig: { + ...prev.chatConfig.fileSelectConfig, + canSelectFile: formData.fileUpload || false + } + } + })); + return; + } + + let newTools: FlowNodeTemplateType[] = []; + const failedToolIds: string[] = []; + + // 使用 Promise.allSettled 并行请求所有工具 + const toolPromises = newToolIds.map((toolId: string) => + getToolPreviewNode({ appId: toolId }) + .then((tool) => ({ status: 'fulfilled' as const, toolId, tool })) + .catch((error) => ({ status: 'rejected' as const, toolId, error })) + ); + + const results = await Promise.allSettled(toolPromises); + + results.forEach((result: any) => { + if (result.status === 'fulfilled' && result.value.status === 'fulfilled') { + newTools.push(result.value.tool); + } else if (result.status === 'fulfilled' && result.value.status === 'rejected') { + failedToolIds.push(result.value.toolId); + console.error(`❌ 工具 ${result.value.toolId} 获取失败:`, result.value.error); + } + }); + + if (failedToolIds.length > 0) { + toast({ + title: t('app:tool_load_failed'), + description: `${t('app:failed_tools')}: ${failedToolIds.join(', ')}`, + status: 'warning', + duration: 5000 + }); + } + + setAppForm((prev) => { + const newForm: AppFormEditFormType = { + ...prev, + aiSettings: { + ...prev.aiSettings, + aiRole: formData.role || '', + aiTaskObject: formData.taskObject || '' + }, + selectedTools: [...prev.selectedTools, ...newTools], + chatConfig: { + ...prev.chatConfig, + fileSelectConfig: { + ...prev.chatConfig.fileSelectConfig, + canSelectFile: formData.fileUpload || false + } + } + }; + return newForm; + }); + }} + /> )} {activeTab === 'chat_debug' && } @@ -128,7 +241,7 @@ const ChatTest = ({ appForm, setRenderEdit, form2WorkflowFn }: Props) => { ); }; -const Render = ({ appForm, setRenderEdit, form2WorkflowFn }: Props) => { +const Render = ({ appForm, setAppForm, setRenderEdit, form2WorkflowFn }: Props) => { const { chatId } = useChatStore(); const { appDetail } = useContextSelector(AppContext, (v) => v); @@ -150,6 +263,7 @@ const Render = ({ appForm, setRenderEdit, form2WorkflowFn }: Props) => { diff --git a/projects/app/src/pageComponents/app/detail/Edit/ChatAgent/Edit.tsx b/projects/app/src/pageComponents/app/detail/Edit/ChatAgent/Edit.tsx index f8daa1928..75307632f 100644 --- a/projects/app/src/pageComponents/app/detail/Edit/ChatAgent/Edit.tsx +++ b/projects/app/src/pageComponents/app/detail/Edit/ChatAgent/Edit.tsx @@ -55,6 +55,7 @@ const Edit = ({ diff --git a/projects/app/src/pageComponents/app/detail/Edit/ChatAgent/utils.ts b/projects/app/src/pageComponents/app/detail/Edit/ChatAgent/utils.ts index 7f5542779..5609a58ff 100644 --- a/projects/app/src/pageComponents/app/detail/Edit/ChatAgent/utils.ts +++ b/projects/app/src/pageComponents/app/detail/Edit/ChatAgent/utils.ts @@ -293,17 +293,18 @@ export const checkNeedsUserConfiguration = (toolTemplate: FlowNodeTemplateType): [FlowNodeInputTypeEnum.timeRangeSelect]: true }; return ( - toolTemplate.inputs.length > 0 && - toolTemplate.inputs.some((input) => { - // 有工具描述的不需要配置 - if (input.toolDescription) return false; - // 禁用流的不需要配置 - if (input.key === NodeInputKeyEnum.forbidStream) return false; - // 系统输入配置需要配置 - if (input.key === NodeInputKeyEnum.systemInputConfig) return true; + ((toolTemplate.inputs?.length ?? 0) > 0 && + toolTemplate.inputs?.some((input) => { + // 有工具描述的不需要配置 + if (input.toolDescription) return false; + // 禁用流的不需要配置 + if (input.key === NodeInputKeyEnum.forbidStream) return false; + // 系统输入配置需要配置 + if (input.key === NodeInputKeyEnum.systemInputConfig) return true; - // 检查是否包含表单类型的输入 - return input.renderTypeList.some((type) => formRenderTypesMap[type]); - }) + // 检查是否包含表单类型的输入 + return input.renderTypeList.some((type) => formRenderTypesMap[type]); + })) || + false ); }; diff --git a/projects/app/src/pages/api/core/chat/helperBot/completions.ts b/projects/app/src/pages/api/core/chat/helperBot/completions.ts index c4c6867ed..0ca4a50c0 100644 --- a/projects/app/src/pages/api/core/chat/helperBot/completions.ts +++ b/projects/app/src/pages/api/core/chat/helperBot/completions.ts @@ -9,6 +9,7 @@ import { MongoHelperBotChatItem } from '@fastgpt/service/core/chat/HelperBot/cha import { getWorkflowResponseWrite } from '@fastgpt/service/core/workflow/dispatch/utils'; import { dispatchMap } from '@fastgpt/service/core/chat/HelperBot/dispatch/index'; import { pushChatRecords } from '@fastgpt/service/core/chat/HelperBot/utils'; +import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants'; export type completionsBody = HelperBotCompletionsParamsType; @@ -43,9 +44,19 @@ async function handler(req: ApiRequestProps, res: ApiResponseTy files, metadata, histories, - workflowResponseWrite + workflowResponseWrite, + teamId, + userId }); + // Send formData if exists + if (result.formData) { + workflowResponseWrite?.({ + event: SseResponseEventEnum.formData, + data: result.formData + }); + } + // Save chat await pushChatRecords({ type: metadata.type, diff --git a/projects/app/src/web/common/api/fetch.ts b/projects/app/src/web/common/api/fetch.ts index 7d68b939a..c5efebcd9 100644 --- a/projects/app/src/web/common/api/fetch.ts +++ b/projects/app/src/web/common/api/fetch.ts @@ -50,6 +50,9 @@ type ResponseQueueItemType = CommonResponseType & | SseResponseEventEnum.toolResponse; tools: any; } + | { + event: SseResponseEventEnum.formData; + } ); class FatalError extends Error {} @@ -269,6 +272,12 @@ export const streamFetch = ({ event, agentPlan: rest.agentPlan }); + } else if (event === SseResponseEventEnum.formData) { + // Directly call onMessage for formData, no need to queue + onMessage({ + event, + data: rest + }); } else if (event === SseResponseEventEnum.error) { if (rest.statusText === TeamErrEnum.aiPointsNotEnough) { useSystemStore.getState().setNotSufficientModalType(TeamErrEnum.aiPointsNotEnough);