From 9f2adcd523ce9af6f7cbf286ed67c9a8be3c9958 Mon Sep 17 00:00:00 2001 From: Archer <545436317@qq.com> Date: Tue, 6 Jan 2026 13:21:57 +0800 Subject: [PATCH] perf: request llm (#6191) * perf: request error info * perf: request llm' * perf: request llm' * openapi doc --- document/content/docs/upgrading/4-14/4145.mdx | 1 + document/data/doc-last-modified.json | 2 +- packages/global/core/ai/constants.ts | 1 + packages/global/core/ai/type.d.ts | 9 +- .../global/openapi/core/chat/controler/api.ts | 25 ++ packages/global/openapi/index.ts | 8 +- packages/global/openapi/tag.ts | 8 +- .../service/core/ai/llm/agentCall/index.ts | 11 +- packages/service/core/ai/llm/request.ts | 343 ++++++++++-------- packages/service/core/ai/llm/utils.ts | 3 + packages/service/core/ai/utils.ts | 12 +- .../service/core/workflow/dispatch/ai/chat.ts | 117 ++++-- .../workflow/dispatch/ai/classifyQuestion.ts | 1 - .../core/workflow/dispatch/ai/tool/index.ts | 43 ++- .../workflow/dispatch/ai/tool/toolCall.ts | 4 +- .../core/workflow/dispatch/ai/tool/type.d.ts | 1 + .../core/workflow/dispatch/child/runTool.ts | 2 +- .../core/workflow/dispatch/plugin/run.ts | 5 +- .../service/core/workflow/dispatch/utils.ts | 19 +- packages/web/i18n/en/chat.json | 1 + packages/web/i18n/zh-CN/chat.json | 1 + packages/web/i18n/zh-Hant/chat.json | 1 + .../chat/components/WholeResponseModal.tsx | 24 +- projects/app/src/global/core/chat/api.d.ts | 5 - projects/app/src/pages/api/core/chat/init.ts | 24 +- projects/app/src/web/core/chat/api.ts | 8 +- 26 files changed, 425 insertions(+), 254 deletions(-) diff --git a/document/content/docs/upgrading/4-14/4145.mdx b/document/content/docs/upgrading/4-14/4145.mdx index 09db9a7249..b6b5832211 100644 --- a/document/content/docs/upgrading/4-14/4145.mdx +++ b/document/content/docs/upgrading/4-14/4145.mdx @@ -20,6 +20,7 @@ description: 'FastGPT V4.14.5 更新说明' 2. MongoDB, Redis 和 MQ 的重连逻辑优化。 3. 变量输入框禁用状态可复制。 4. LLM 请求空响应判断,排除敏感过滤错误被误认为无响应。 +5. 完善 AI 对话和工具调用的错误提示,提供更多原始数据。 ## 🐛 修复 diff --git a/document/data/doc-last-modified.json b/document/data/doc-last-modified.json index b7e195d909..285c1d5d7d 100644 --- a/document/data/doc-last-modified.json +++ b/document/data/doc-last-modified.json @@ -120,7 +120,7 @@ "document/content/docs/upgrading/4-14/4142.mdx": "2025-11-18T19:27:14+08:00", "document/content/docs/upgrading/4-14/4143.mdx": "2025-11-26T20:52:05+08:00", "document/content/docs/upgrading/4-14/4144.mdx": "2025-12-16T14:56:04+08:00", - "document/content/docs/upgrading/4-14/4145.mdx": "2026-01-05T13:44:33+08:00", + "document/content/docs/upgrading/4-14/4145.mdx": "2026-01-05T15:39:04+08:00", "document/content/docs/upgrading/4-8/40.mdx": "2025-08-02T19:38:37+08:00", "document/content/docs/upgrading/4-8/41.mdx": "2025-08-02T19:38:37+08:00", "document/content/docs/upgrading/4-8/42.mdx": "2025-08-02T19:38:37+08:00", diff --git a/packages/global/core/ai/constants.ts b/packages/global/core/ai/constants.ts index 358a661184..8fb01aa7e4 100644 --- a/packages/global/core/ai/constants.ts +++ b/packages/global/core/ai/constants.ts @@ -41,6 +41,7 @@ export enum EmbeddingTypeEnm { } export const completionFinishReasonMap = { + error: i18nT('chat:completion_finish_error'), close: i18nT('chat:completion_finish_close'), stop: i18nT('chat:completion_finish_stop'), length: i18nT('chat:completion_finish_length'), diff --git a/packages/global/core/ai/type.d.ts b/packages/global/core/ai/type.d.ts index d7e7e61fed..c35ed4458c 100644 --- a/packages/global/core/ai/type.d.ts +++ b/packages/global/core/ai/type.d.ts @@ -1,5 +1,6 @@ import openai from 'openai'; import type { + ChatCompletion as SdkChatCompletion, ChatCompletionMessageToolCall, ChatCompletionMessageParam as SdkChatCompletionMessageParam, ChatCompletionToolMessageParam, @@ -70,10 +71,16 @@ export type ChatCompletionMessageFunctionCall = }; // Stream response -export type StreamChatType = Stream; +export type StreamChatType = Stream; export type UnStreamChatType = openai.Chat.Completions.ChatCompletion; +// UnStream response +export type ChatCompletion = SdkChatCompletion & { + error?: any; +}; + export type CompletionFinishReason = + | 'error' | 'close' | 'stop' | 'length' diff --git a/packages/global/openapi/core/chat/controler/api.ts b/packages/global/openapi/core/chat/controler/api.ts index e6c519392f..b29d69b338 100644 --- a/packages/global/openapi/core/chat/controler/api.ts +++ b/packages/global/openapi/core/chat/controler/api.ts @@ -2,6 +2,31 @@ import { OutLinkChatAuthSchema } from '../../../../support/permission/chat'; import { ObjectIdSchema } from '../../../../common/type/mongo'; import z from 'zod'; +/* Init */ +// Online chat +export const InitChatQuerySchema = z + .object({ + appId: ObjectIdSchema.describe('应用ID'), + chatId: z.string().min(1).describe('对话ID'), + loadCustomFeedbacks: z.boolean().optional().describe('是否加载自定义反馈') + }) + .meta({ + example: { + appId: '1234567890', + chatId: '1234567890', + loadCustomFeedbacks: true + } + }); +export type InitChatQueryType = z.infer; +export const InitChatResponseSchema = z.object({ + chatId: z.string().min(1).describe('对话ID'), + appId: ObjectIdSchema.describe('应用ID'), + userAvatar: z.string().optional().describe('用户头像'), + title: z.string().min(1).describe('对话标题'), + variables: z.record(z.string(), z.any()).optional().describe('全局变量值'), + app: z.object({}).describe('应用配置') +}); + /* ============ v2/chat/stop ============ */ export const StopV2ChatSchema = z .object({ diff --git a/packages/global/openapi/index.ts b/packages/global/openapi/index.ts index f7c2366b50..82c41e6fc9 100644 --- a/packages/global/openapi/index.ts +++ b/packages/global/openapi/index.ts @@ -28,7 +28,13 @@ export const openAPIDocument = createDocument({ }, { name: '对话管理', - tags: [TagsMap.chatHistory, TagsMap.chatPage, TagsMap.chatFeedback, TagsMap.chatSetting] + tags: [ + TagsMap.chatPage, + TagsMap.chatHistory, + TagsMap.chatController, + TagsMap.chatFeedback, + TagsMap.chatSetting + ] }, { name: '知识库', diff --git a/packages/global/openapi/tag.ts b/packages/global/openapi/tag.ts index c12b78df20..2cbe8d78c0 100644 --- a/packages/global/openapi/tag.ts +++ b/packages/global/openapi/tag.ts @@ -6,11 +6,11 @@ export const TagsMap = { appCommon: 'Agent 管理', // Chat - home - chatPage: '对话页', - chatController: '对话框操作', - chatHistory: '对话历史管理', - chatSetting: '门户页配置', + chatPage: '对话页面通用', + chatHistory: '历史记录管理', + chatController: '对话操作', chatFeedback: '对话反馈', + chatSetting: '门户页配置', // Dataset datasetCollection: '集合', diff --git a/packages/service/core/ai/llm/agentCall/index.ts b/packages/service/core/ai/llm/agentCall/index.ts index d937c58c05..84624e599a 100644 --- a/packages/service/core/ai/llm/agentCall/index.ts +++ b/packages/service/core/ai/llm/agentCall/index.ts @@ -55,6 +55,7 @@ type RunAgentCallProps = { } & ResponseEvents; type RunAgentResponse = { + error?: any; completeMessages: ChatCompletionMessageParam[]; // Step request complete messages assistantMessages: ChatCompletionMessageParam[]; // Step assistant response messages interactiveResponse?: ToolCallChildrenInteractive; @@ -134,6 +135,7 @@ export const runAgentCall = async ({ let inputTokens: number = 0; let outputTokens: number = 0; let finish_reason: CompletionFinishReason | undefined; + let requestError: any; const subAppUsages: ChatNodeUsageType[] = []; // 处理 tool 里的交互 @@ -213,8 +215,10 @@ export const runAgentCall = async ({ usage, responseEmptyTip, assistantMessage: llmAssistantMessage, - finish_reason: finishReason + finish_reason: finishReason, + error } = await createLLMResponse({ + throwError: false, body: { ...body, max_tokens: maxTokens, @@ -234,7 +238,11 @@ export const runAgentCall = async ({ }); finish_reason = finishReason; + requestError = error; + if (requestError) { + break; + } if (responseEmptyTip) { return Promise.reject(responseEmptyTip); } @@ -303,6 +311,7 @@ export const runAgentCall = async ({ } return { + error: requestError, inputTokens, outputTokens, subAppUsages, diff --git a/packages/service/core/ai/llm/request.ts b/packages/service/core/ai/llm/request.ts index 0c0e1d089e..43c03a7b57 100644 --- a/packages/service/core/ai/llm/request.ts +++ b/packages/service/core/ai/llm/request.ts @@ -39,6 +39,7 @@ export type ResponseEvents = { }; export type CreateLLMResponseProps = { + throwError?: boolean; userKey?: OpenaiAccountType; body: LLMRequestBodyType; isAborted?: () => boolean | undefined; @@ -46,6 +47,7 @@ export type CreateLLMResponseProps( args: CreateLLMResponseProps ): Promise => { - const { body, custonHeaders, userKey } = args; + const { throwError = true, body, custonHeaders, userKey } = args; const { messages, useVision, requestOrigin, tools, toolCallMode } = body; // Messages process @@ -104,7 +106,7 @@ export const createLLMResponse = async ( } }); - const { answerText, reasoningText, toolCalls, finish_reason, usage } = await (async () => { + let { answerText, reasoningText, toolCalls, finish_reason, usage, error } = await (async () => { if (isStreamResponse) { return createStreamResponse({ response, @@ -151,6 +153,14 @@ export const createLLMResponse = async ( usage?.prompt_tokens || (await countGptMessagesTokens(requestBody.messages, requestBody.tools)); const outputTokens = usage?.completion_tokens || (await countGptMessagesTokens(assistantMessage)); + if (error) { + finish_reason = 'error'; + + if (throwError) { + throw error; + } + } + const getEmptyResponseTip = () => { if (userKey?.baseUrl) { addLog.warn(`User LLM response empty`, { @@ -172,10 +182,12 @@ export const createLLMResponse = async ( !answerText && !reasoningText && !toolCalls?.length && + !error && (finish_reason === 'stop' || !finish_reason); const responseEmptyTip = isNotResponse ? getEmptyResponseTip() : undefined; return { + error, isStreamResponse, responseEmptyTip, answerText, @@ -183,8 +195,8 @@ export const createLLMResponse = async ( toolCalls, finish_reason, usage: { - inputTokens, - outputTokens + inputTokens: error ? 0 : inputTokens, + outputTokens: error ? 0 : outputTokens }, requestMessages, @@ -200,6 +212,7 @@ type CompleteResponse = Pick< 'answerText' | 'reasoningText' | 'toolCalls' | 'finish_reason' > & { usage?: CompletionUsage; + error?: any; }; export const createStreamResponse = async ({ @@ -217,13 +230,174 @@ export const createStreamResponse = async ({ const { retainDatasetCite = true, tools, toolCallMode = 'toolChoice', model } = body; const modelData = getLLMModel(model); - const { parsePart, getResponseData, updateFinishReason } = parseLLMStreamResponse(); + const { parsePart, getResponseData, updateFinishReason, updateError } = parseLLMStreamResponse(); if (tools?.length) { if (toolCallMode === 'toolChoice') { let callingTool: ChatCompletionMessageToolCall['function'] | null = null; const toolCalls: ChatCompletionMessageToolCall[] = []; + try { + for await (const part of response) { + if (isAborted?.()) { + response.controller?.abort(); + updateFinishReason('close'); + break; + } + + const { reasoningContent, responseContent } = parsePart({ + part, + parseThinkTag: modelData.reasoning, + retainDatasetCite + }); + + if (reasoningContent) { + onReasoning?.({ text: reasoningContent }); + } + if (responseContent) { + onStreaming?.({ text: responseContent }); + } + + const responseChoice = part.choices?.[0]?.delta; + + // Parse tool calls + if (responseChoice?.tool_calls?.length) { + responseChoice.tool_calls.forEach((toolCall, i) => { + const index = toolCall.index ?? i; + + // Call new tool + const hasNewTool = toolCall?.function?.name || callingTool; + if (hasNewTool) { + // Call new tool + if (toolCall?.function?.name) { + callingTool = { + name: toolCall.function?.name || '', + arguments: toolCall.function?.arguments || '' + }; + } else if (callingTool) { + // Continue call(Perhaps the name of the previous function was incomplete) + callingTool.name += toolCall.function?.name || ''; + callingTool.arguments += toolCall.function?.arguments || ''; + } + + // New tool, add to list. + if (tools.find((item) => item.function.name === callingTool!.name)) { + const call: ChatCompletionMessageToolCall = { + id: getNanoid(), + type: 'function', + function: callingTool! + }; + toolCalls[index] = call; + onToolCall?.({ call }); + callingTool = null; + } + } else { + /* arg 追加到当前工具的参数里 */ + const arg: string = toolCall?.function?.arguments ?? ''; + const currentTool = toolCalls[index]; + if (currentTool && arg) { + currentTool.function.arguments += arg; + + onToolParam?.({ tool: currentTool, params: arg }); + } + } + }); + } + } + } catch (error: any) { + updateError(error?.error || error); + } + + const { reasoningContent, content, finish_reason, usage, error } = getResponseData(); + + return { + error, + answerText: content, + reasoningText: reasoningContent, + finish_reason, + usage, + toolCalls: toolCalls.filter((call) => !!call) + }; + } else { + let startResponseWrite = false; + let answer = ''; + + try { + for await (const part of response) { + if (isAborted?.()) { + response.controller?.abort(); + updateFinishReason('close'); + break; + } + + const { reasoningContent, content, responseContent } = parsePart({ + part, + parseThinkTag: modelData.reasoning, + retainDatasetCite + }); + answer += content; + + if (reasoningContent) { + onReasoning?.({ text: reasoningContent }); + } + + if (content) { + if (startResponseWrite) { + if (responseContent) { + onStreaming?.({ text: responseContent }); + } + } else if (answer.length >= 3) { + answer = answer.trimStart(); + + // Not call tool + if (/0(:|:)/.test(answer)) { + startResponseWrite = true; + + // find first : index + const firstIndex = + answer.indexOf('0:') !== -1 ? answer.indexOf('0:') : answer.indexOf('0:'); + answer = answer.substring(firstIndex + 2).trim(); + + onStreaming?.({ text: answer }); + } + // Not response tool + else if (/1(:|:)/.test(answer)) { + } + // Not start 1/0, start response + else { + startResponseWrite = true; + onStreaming?.({ text: answer }); + } + } + } + } + } catch (error: any) { + updateError(error?.error || error); + } + + const { reasoningContent, content, finish_reason, usage, error } = getResponseData(); + const { answer: llmAnswer, streamAnswer, toolCalls } = parsePromptToolCall(content); + + if (streamAnswer) { + onStreaming?.({ text: streamAnswer }); + } + + toolCalls?.forEach((call) => { + onToolCall?.({ call }); + }); + + return { + error, + answerText: llmAnswer, + reasoningText: reasoningContent, + finish_reason, + usage, + toolCalls + }; + } + } else { + // Not use tool + try { for await (const part of response) { if (isAborted?.()) { response.controller?.abort(); @@ -243,161 +417,15 @@ export const createStreamResponse = async ({ if (responseContent) { onStreaming?.({ text: responseContent }); } - - const responseChoice = part.choices?.[0]?.delta; - - // Parse tool calls - if (responseChoice?.tool_calls?.length) { - responseChoice.tool_calls.forEach((toolCall, i) => { - const index = toolCall.index ?? i; - - // Call new tool - const hasNewTool = toolCall?.function?.name || callingTool; - if (hasNewTool) { - // Call new tool - if (toolCall?.function?.name) { - callingTool = { - name: toolCall.function?.name || '', - arguments: toolCall.function?.arguments || '' - }; - } else if (callingTool) { - // Continue call(Perhaps the name of the previous function was incomplete) - callingTool.name += toolCall.function?.name || ''; - callingTool.arguments += toolCall.function?.arguments || ''; - } - - // New tool, add to list. - if (tools.find((item) => item.function.name === callingTool!.name)) { - const call: ChatCompletionMessageToolCall = { - id: getNanoid(), - type: 'function', - function: callingTool! - }; - toolCalls[index] = call; - onToolCall?.({ call }); - callingTool = null; - } - } else { - /* arg 追加到当前工具的参数里 */ - const arg: string = toolCall?.function?.arguments ?? ''; - const currentTool = toolCalls[index]; - if (currentTool && arg) { - currentTool.function.arguments += arg; - - onToolParam?.({ tool: currentTool, params: arg }); - } - } - }); - } - } - - const { reasoningContent, content, finish_reason, usage } = getResponseData(); - - return { - answerText: content, - reasoningText: reasoningContent, - finish_reason, - usage, - toolCalls: toolCalls.filter((call) => !!call) - }; - } else { - let startResponseWrite = false; - let answer = ''; - - for await (const part of response) { - if (isAborted?.()) { - response.controller?.abort(); - updateFinishReason('close'); - break; - } - - const { reasoningContent, content, responseContent } = parsePart({ - part, - parseThinkTag: modelData.reasoning, - retainDatasetCite - }); - answer += content; - - if (reasoningContent) { - onReasoning?.({ text: reasoningContent }); - } - - if (content) { - if (startResponseWrite) { - if (responseContent) { - onStreaming?.({ text: responseContent }); - } - } else if (answer.length >= 3) { - answer = answer.trimStart(); - - // Not call tool - if (/0(:|:)/.test(answer)) { - startResponseWrite = true; - - // find first : index - const firstIndex = - answer.indexOf('0:') !== -1 ? answer.indexOf('0:') : answer.indexOf('0:'); - answer = answer.substring(firstIndex + 2).trim(); - - onStreaming?.({ text: answer }); - } - // Not response tool - else if (/1(:|:)/.test(answer)) { - } - // Not start 1/0, start response - else { - startResponseWrite = true; - onStreaming?.({ text: answer }); - } - } - } - } - - const { reasoningContent, content, finish_reason, usage } = getResponseData(); - const { answer: llmAnswer, streamAnswer, toolCalls } = parsePromptToolCall(content); - - if (streamAnswer) { - onStreaming?.({ text: streamAnswer }); - } - - toolCalls?.forEach((call) => { - onToolCall?.({ call }); - }); - - return { - answerText: llmAnswer, - reasoningText: reasoningContent, - finish_reason, - usage, - toolCalls - }; - } - } else { - // Not use tool - for await (const part of response) { - if (isAborted?.()) { - response.controller?.abort(); - updateFinishReason('close'); - break; - } - - const { reasoningContent, responseContent } = parsePart({ - part, - parseThinkTag: modelData.reasoning, - retainDatasetCite - }); - - if (reasoningContent) { - onReasoning?.({ text: reasoningContent }); - } - if (responseContent) { - onStreaming?.({ text: responseContent }); } + } catch (error: any) { + updateError(error?.error || error); } - const { reasoningContent, content, finish_reason, usage } = getResponseData(); + const { reasoningContent, content, finish_reason, usage, error } = getResponseData(); return { + error, answerText: content, reasoningText: reasoningContent, finish_reason, @@ -479,6 +507,7 @@ export const createCompleteResponse = async ({ } return { + error: response.error, reasoningText: formatReasonContent, answerText: formatContent, toolCalls, @@ -580,9 +609,9 @@ const llmCompletionsBodyFormat = async ({ }) } as T; - // Filter null value + // Filter undefined/null value requestBody = Object.fromEntries( - Object.entries(requestBody).filter(([_, value]) => value !== null) + Object.entries(requestBody).filter(([_, value]) => value !== null && value !== undefined) ) as T; // field map diff --git a/packages/service/core/ai/llm/utils.ts b/packages/service/core/ai/llm/utils.ts index 9af77f7739..673c4dddd2 100644 --- a/packages/service/core/ai/llm/utils.ts +++ b/packages/service/core/ai/llm/utils.ts @@ -364,6 +364,9 @@ export const loadRequestMessages = async ({ const loadMessages = ( await Promise.all( mergeMessages.map(async (item, i) => { + delete item.dataId; + delete item.hideInUI; + if (item.role === ChatCompletionRequestMessageRoleEnum.System) { const content = parseSystemMessage(item.content); if (!content) return; diff --git a/packages/service/core/ai/utils.ts b/packages/service/core/ai/utils.ts index 5df84546ca..d38f243ab9 100644 --- a/packages/service/core/ai/utils.ts +++ b/packages/service/core/ai/utils.ts @@ -73,6 +73,7 @@ export const parseLLMStreamResponse = () => { let buffer_usage: CompletionUsage = getLLMDefaultUsage(); let buffer_reasoningContent = ''; let buffer_content = ''; + let error: any = undefined; /* parseThinkTag - 只控制是否主动解析 ,如果接口已经解析了,则不再解析。 @@ -84,6 +85,7 @@ export const parseLLMStreamResponse = () => { retainDatasetCite = true }: { part: { + error?: any; choices: { delta: { content?: string | null; @@ -96,6 +98,7 @@ export const parseLLMStreamResponse = () => { parseThinkTag?: boolean; retainDatasetCite?: boolean; }): { + error?: any; reasoningContent: string; content: string; // 原始内容,不去掉 cite responseContent: string; // 响应的内容,会去掉 cite @@ -297,11 +300,14 @@ export const parseLLMStreamResponse = () => { buffer_reasoningContent += data.reasoningContent; buffer_content += data.content; + error = part.error || error; + return data; }; const getResponseData = () => { return { + error, finish_reason: buffer_finishReason, usage: buffer_usage, reasoningContent: buffer_reasoningContent, @@ -312,11 +318,15 @@ export const parseLLMStreamResponse = () => { const updateFinishReason = (finishReason: CompletionFinishReason) => { buffer_finishReason = finishReason; }; + const updateError = (err: any) => { + error = err; + }; return { parsePart, getResponseData, - updateFinishReason + updateFinishReason, + updateError }; }; diff --git a/packages/service/core/workflow/dispatch/ai/chat.ts b/packages/service/core/workflow/dispatch/ai/chat.ts index 8abaa1eb09..7d309771e5 100644 --- a/packages/service/core/workflow/dispatch/ai/chat.ts +++ b/packages/service/core/workflow/dispatch/ai/chat.ts @@ -177,47 +177,55 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise { const adaptMessages = chats2GPTMessages({ messages, @@ -224,6 +225,46 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise< // Preview assistant responses const previewAssistantResponses = filterToolResponseToPreview(assistantResponses); + if (error) { + return getNodeErrResponse({ + error, + [DispatchNodeResponseKeyEnum.nodeResponse]: { + totalPoints: totalPointsUsage, + toolCallInputTokens: toolCallInputTokens, + toolCallOutputTokens: toolCallOutputTokens, + childTotalPoints: toolTotalPoints, + model: modelName, + query: userChatInput, + historyPreview: getHistoryPreview( + GPTMessages2Chats({ messages: completeMessages, reserveTool: false }), + 10000, + useVision + ), + toolDetail: toolDispatchFlowResponses.map((item) => item.flowResponses).flat(), + mergeSignId: nodeId, + finishReason: finish_reason + }, + [DispatchNodeResponseKeyEnum.runTimes]: toolDispatchFlowResponses.reduce( + (sum, item) => sum + item.runTimes, + 0 + ), + ...(totalPointsUsage && { + [DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [ + // 模型本身的积分消耗 + { + moduleName: name, + model: modelName, + totalPoints: modelUsage, + inputTokens: toolCallInputTokens, + outputTokens: toolCallOutputTokens + }, + // 工具的消耗 + ...toolUsages + ] + }) + }); + } + return { data: { [NodeOutputKeyEnum.answerText]: previewAssistantResponses diff --git a/packages/service/core/workflow/dispatch/ai/tool/toolCall.ts b/packages/service/core/workflow/dispatch/ai/tool/toolCall.ts index 807d09211c..2bb8e141fb 100644 --- a/packages/service/core/workflow/dispatch/ai/tool/toolCall.ts +++ b/packages/service/core/workflow/dispatch/ai/tool/toolCall.ts @@ -110,7 +110,8 @@ export const runToolCall = async (props: DispatchToolModuleProps): Promise; - customNodeResponse?: Record; + [DispatchNodeResponseKeyEnum.nodeResponse]?: Record; + [DispatchNodeResponseKeyEnum.nodeDispatchUsages]?: ChatNodeUsageType[]; // Node total usage + [DispatchNodeResponseKeyEnum.runTimes]?: number; + [DispatchNodeResponseKeyEnum.newVariables]?: Record; + [DispatchNodeResponseKeyEnum.memories]?: Record; }) => { const errorText = getErrText(error); return { + [DispatchNodeResponseKeyEnum.nodeDispatchUsages]: nodeDispatchUsages, + [DispatchNodeResponseKeyEnum.runTimes]: runTimes, + [DispatchNodeResponseKeyEnum.newVariables]: newVariables, + [DispatchNodeResponseKeyEnum.memories]: system_memories, error: { [NodeOutputKeyEnum.errorText]: errorText, ...(typeof customErr === 'object' ? customErr : {}) }, [DispatchNodeResponseKeyEnum.nodeResponse]: { errorText, - ...(typeof customNodeResponse === 'object' ? customNodeResponse : {}) + ...(typeof responseData === 'object' ? responseData : {}) }, [DispatchNodeResponseKeyEnum.toolResponses]: { error: errorText, diff --git a/packages/web/i18n/en/chat.json b/packages/web/i18n/en/chat.json index 528d729e99..402c2c2eb6 100644 --- a/packages/web/i18n/en/chat.json +++ b/packages/web/i18n/en/chat.json @@ -19,6 +19,7 @@ "click_to_add_url": "Enter file link", "completion_finish_close": "Disconnection", "completion_finish_content_filter": "Trigger safe wind control", + "completion_finish_error": "Request error", "completion_finish_function_call": "Function Calls", "completion_finish_length": "Reply limit exceeded", "completion_finish_null": "unknown", diff --git a/packages/web/i18n/zh-CN/chat.json b/packages/web/i18n/zh-CN/chat.json index 91366610d4..9da92c004b 100644 --- a/packages/web/i18n/zh-CN/chat.json +++ b/packages/web/i18n/zh-CN/chat.json @@ -19,6 +19,7 @@ "click_to_add_url": "输入文件链接", "completion_finish_close": "请求关闭", "completion_finish_content_filter": "触发安全风控", + "completion_finish_error": "请求错误", "completion_finish_function_call": "函数调用", "completion_finish_length": "超出回复限制", "completion_finish_null": "未知", diff --git a/packages/web/i18n/zh-Hant/chat.json b/packages/web/i18n/zh-Hant/chat.json index 7a4a0ecc71..76f2bd5d99 100644 --- a/packages/web/i18n/zh-Hant/chat.json +++ b/packages/web/i18n/zh-Hant/chat.json @@ -19,6 +19,7 @@ "click_to_add_url": "輸入文件鏈接", "completion_finish_close": "連接斷開", "completion_finish_content_filter": "觸發安全風控", + "completion_finish_error": "請求錯誤", "completion_finish_function_call": "函式呼叫", "completion_finish_length": "超出回覆限制", "completion_finish_null": "未知", diff --git a/projects/app/src/components/core/chat/components/WholeResponseModal.tsx b/projects/app/src/components/core/chat/components/WholeResponseModal.tsx index d5d2d499cf..d702c4b559 100644 --- a/projects/app/src/components/core/chat/components/WholeResponseModal.tsx +++ b/projects/app/src/components/core/chat/components/WholeResponseModal.tsx @@ -149,6 +149,18 @@ export const WholeResponseContent = ({ value={formatNumber(activeModule.childTotalPoints)} /> )} + + + + + {/* ai chat */} + <> + {activeModule?.finishReason && ( + + )} {activeModule?.tokens && ( @@ -171,12 +183,6 @@ export const WholeResponseContent = ({ label={t('common:core.chat.response.context total length')} value={activeModule?.contextTotalLen} /> - - - - - {/* ai chat */} - <> - {activeModule?.finishReason && ( - - )} { - let { appId, chatId } = req.query as InitChatProps; - - if (!appId) { - return jsonRes(res, { - code: 501, - message: "You don't have an app yet" - }); - } +async function handler(req: NextApiRequest, res: NextApiResponse): Promise { + const { appId, chatId } = InitChatQuerySchema.parse(req.query); try { // auth app permission @@ -99,9 +89,3 @@ async function handler( } export default NextAPI(handler); - -export const config = { - api: { - responseLimit: '10mb' - } -}; diff --git a/projects/app/src/web/core/chat/api.ts b/projects/app/src/web/core/chat/api.ts index 3de7373577..a00f8ff21b 100644 --- a/projects/app/src/web/core/chat/api.ts +++ b/projects/app/src/web/core/chat/api.ts @@ -2,7 +2,6 @@ import { GET, POST, DELETE, PUT } from '@/web/common/api/request'; import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d'; import type { getResDataQuery } from '@/pages/api/core/chat/getResData'; import type { - InitChatProps, InitChatResponse, InitOutLinkChatProps, InitTeamChatProps @@ -24,7 +23,10 @@ import type { UpdateFavouriteAppParamsType } from '@fastgpt/global/openapi/core/chat/favourite/api'; import type { ChatFavouriteAppType } from '@fastgpt/global/core/chat/favouriteApp/type'; -import type { StopV2ChatParams } from '@fastgpt/global/openapi/core/chat/controler/api'; +import type { + InitChatQueryType, + StopV2ChatParams +} from '@fastgpt/global/openapi/core/chat/controler/api'; import type { GetRecentlyUsedAppsResponseType } from '@fastgpt/global/openapi/core/chat/api'; export const getRecentlyUsedApps = () => @@ -33,7 +35,7 @@ export const getRecentlyUsedApps = () => /** * 获取初始化聊天内容 */ -export const getInitChatInfo = (data: InitChatProps) => +export const getInitChatInfo = (data: InitChatQueryType) => GET(`/core/chat/init`, data); export const getInitOutLinkChatInfo = (data: InitOutLinkChatProps) => GET(`/core/chat/outLink/init`, data);