diff --git a/.github/workflows/test-fastgpt.yaml b/.github/workflows/test-fastgpt.yaml index 6d41fbb9dc..3586f5b276 100644 --- a/.github/workflows/test-fastgpt.yaml +++ b/.github/workflows/test-fastgpt.yaml @@ -20,7 +20,7 @@ jobs: repository: ${{ github.event.pull_request.head.repo.full_name }} - uses: pnpm/action-setup@v4 with: - version: 10 + version: 9 - name: 'Install Deps' run: pnpm install - name: 'Test' diff --git a/.github/workflows/test-sandbox.yaml b/.github/workflows/test-sandbox.yaml index cf75db3d88..aa34a7a114 100644 --- a/.github/workflows/test-sandbox.yaml +++ b/.github/workflows/test-sandbox.yaml @@ -20,7 +20,7 @@ jobs: - uses: pnpm/action-setup@v4 with: - version: 10 + version: 9 - uses: actions/setup-node@v4 with: diff --git a/document/components/accordion.tsx b/document/components/accordion.tsx index abaab4000f..accd073032 100644 --- a/document/components/accordion.tsx +++ b/document/components/accordion.tsx @@ -1,7 +1,8 @@ 'use client'; import { Check, Link as LinkIcon } from 'lucide-react'; -import { ComponentProps, type ReactNode, useEffect, useRef, useState } from 'react'; +import type { ComponentProps} from 'react'; +import { type ReactNode, useEffect, useRef, useState } from 'react'; import { cn } from '../lib/cn'; import { useCopyButton } from 'fumadocs-ui/utils/use-copy-button'; import { buttonVariants } from './ui/button'; diff --git a/document/content/docs/self-host/upgrading/4-14/4149.mdx b/document/content/docs/self-host/upgrading/4-14/4149.mdx index c135720ece..78d1baf35a 100644 --- a/document/content/docs/self-host/upgrading/4-14/4149.mdx +++ b/document/content/docs/self-host/upgrading/4-14/4149.mdx @@ -27,7 +27,7 @@ AGENT_SANDBOX_SEALOS_TOKEN= 1. 新增 AI 虚拟机功能,可以给 AI 挂载一个虚拟机工具进行更丰富的操作。 2. 封装 logger sdk。 -3. 更新知识库数据时候,同步更新 collection 更新时间。 +3. 更新知识库单个数据时,同步更新 collection 更新时间。 4. 表单输入文件时,支持打开文件进行预览。 ## ⚙️ 优化 diff --git a/document/data/doc-last-modified.json b/document/data/doc-last-modified.json index 36787e9993..16e0f61cda 100644 --- a/document/data/doc-last-modified.json +++ b/document/data/doc-last-modified.json @@ -147,10 +147,10 @@ "document/content/docs/openapi/share.mdx": "2026-02-12T18:45:30+08:00", "document/content/docs/self-host/config/json.en.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/config/json.mdx": "2026-03-03T17:39:47+08:00", - "document/content/docs/self-host/config/model/intro.en.mdx": "2026-03-19T13:55:19+08:00", - "document/content/docs/self-host/config/model/intro.mdx": "2026-03-19T13:55:19+08:00", - "document/content/docs/self-host/config/model/siliconCloud.en.mdx": "2026-03-19T13:55:19+08:00", - "document/content/docs/self-host/config/model/siliconCloud.mdx": "2026-03-19T13:55:19+08:00", + "document/content/docs/self-host/config/model/intro.en.mdx": "2026-03-19T14:09:03+08:00", + "document/content/docs/self-host/config/model/intro.mdx": "2026-03-19T14:09:03+08:00", + "document/content/docs/self-host/config/model/siliconCloud.en.mdx": "2026-03-19T14:09:03+08:00", + "document/content/docs/self-host/config/model/siliconCloud.mdx": "2026-03-19T14:09:03+08:00", "document/content/docs/self-host/config/object-storage.en.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/config/object-storage.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/config/signoz.en.mdx": "2026-03-03T17:39:47+08:00", @@ -171,8 +171,8 @@ "document/content/docs/self-host/custom-models/ollama.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/custom-models/xinference.en.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/custom-models/xinference.mdx": "2026-03-03T17:39:47+08:00", - "document/content/docs/self-host/deploy/docker.en.mdx": "2026-03-19T13:55:19+08:00", - "document/content/docs/self-host/deploy/docker.mdx": "2026-03-19T13:55:19+08:00", + "document/content/docs/self-host/deploy/docker.en.mdx": "2026-03-19T14:09:03+08:00", + "document/content/docs/self-host/deploy/docker.mdx": "2026-03-19T14:09:03+08:00", "document/content/docs/self-host/deploy/sealos.en.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/deploy/sealos.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/design/dataset.en.mdx": "2026-03-03T17:39:47+08:00", @@ -180,7 +180,7 @@ "document/content/docs/self-host/design/design_plugin.en.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/design/design_plugin.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/dev.en.mdx": "2026-03-03T17:39:47+08:00", - "document/content/docs/self-host/dev.mdx": "2026-03-19T11:32:26+08:00", + "document/content/docs/self-host/dev.mdx": "2026-03-19T14:09:03+08:00", "document/content/docs/self-host/faq.en.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/index.en.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/index.mdx": "2026-03-03T17:39:47+08:00", @@ -197,7 +197,7 @@ "document/content/docs/self-host/troubleshooting/model-errors.en.mdx": "2026-03-19T11:32:14+08:00", "document/content/docs/self-host/troubleshooting/model-errors.mdx": "2026-03-19T11:32:14+08:00", "document/content/docs/self-host/troubleshooting/s3-issues.en.mdx": "2026-03-17T14:44:54+08:00", - "document/content/docs/self-host/troubleshooting/s3-issues.mdx": "2026-03-19T13:55:19+08:00", + "document/content/docs/self-host/troubleshooting/s3-issues.mdx": "2026-03-19T14:09:03+08:00", "document/content/docs/self-host/upgrading/4-12/4120.en.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/4-12/4120.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/4-12/4121.en.mdx": "2026-03-03T17:39:47+08:00", @@ -236,7 +236,7 @@ "document/content/docs/self-host/upgrading/4-14/4148.mdx": "2026-03-09T17:39:53+08:00", "document/content/docs/self-host/upgrading/4-14/41481.en.mdx": "2026-03-09T12:02:02+08:00", "document/content/docs/self-host/upgrading/4-14/41481.mdx": "2026-03-09T17:39:53+08:00", - "document/content/docs/self-host/upgrading/4-14/4149.mdx": "2026-03-19T11:21:58+08:00", + "document/content/docs/self-host/upgrading/4-14/4149.mdx": "2026-03-19T14:09:03+08:00", "document/content/docs/self-host/upgrading/outdated/40.en.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/outdated/40.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/outdated/41.en.mdx": "2026-03-03T17:39:47+08:00", @@ -377,8 +377,8 @@ "document/content/docs/self-host/upgrading/outdated/499.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/upgrade-intruction.en.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/upgrade-intruction.mdx": "2026-03-03T17:39:47+08:00", - "document/content/docs/toc.en.mdx": "2026-03-19T13:55:19+08:00", - "document/content/docs/toc.mdx": "2026-03-19T13:55:19+08:00", + "document/content/docs/toc.en.mdx": "2026-03-19T14:09:03+08:00", + "document/content/docs/toc.mdx": "2026-03-19T14:09:03+08:00", "document/content/docs/use-cases/app-cases/dalle3.en.mdx": "2026-02-26T22:14:30+08:00", "document/content/docs/use-cases/app-cases/dalle3.mdx": "2025-07-23T21:35:03+08:00", "document/content/docs/use-cases/app-cases/english_essay_correction_bot.en.mdx": "2026-02-26T22:14:30+08:00", diff --git a/package.json b/package.json index d42013506d..90ea06f38c 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,6 @@ }, "engines": { "node": ">=20", - "pnpm": "10.x" + "pnpm": "9.x" } } diff --git a/packages/global/core/chat/adapt.ts b/packages/global/core/chat/adapt.ts index 218bae7e94..1896d5ff08 100644 --- a/packages/global/core/chat/adapt.ts +++ b/packages/global/core/chat/adapt.ts @@ -19,7 +19,7 @@ import type { ChatCompletionToolMessageParam } from '../../core/ai/type'; import { ChatCompletionRequestMessageRoleEnum } from '../../core/ai/constants'; -import { getNanoid } from '../../common/string/tools'; +import { getPlanCallResponseText } from './utils'; export const GPT2Chat = { [ChatCompletionRequestMessageRoleEnum.System]: ChatRoleEnum.System, @@ -52,7 +52,7 @@ export const chats2GPTMessages = ({ }): ChatCompletionMessageParam[] => { let results: ChatCompletionMessageParam[] = []; - messages.forEach((item) => { + messages.forEach((item, index) => { const dataId = reserveId ? item.dataId : undefined; if (item.obj === ChatRoleEnum.System) { const content = item.value?.[0]?.text?.content; @@ -65,6 +65,8 @@ export const chats2GPTMessages = ({ } } else if (item.obj === ChatRoleEnum.Human) { const value = item.value + // 有 planId 的过滤掉,会被 planTool 一起处理 + .filter((item) => !item.planId) .map((item) => { if (item.text) { return { @@ -93,22 +95,28 @@ export const chats2GPTMessages = ({ }) .filter(Boolean) as ChatCompletionContentPart[]; - results.push({ - dataId, - hideInUI: item.hideInUI, - role: ChatCompletionRequestMessageRoleEnum.User, - content: simpleUserContentPart(value) - }); + if (value.length) { + results.push({ + dataId, + hideInUI: item.hideInUI, + role: ChatCompletionRequestMessageRoleEnum.User, + content: simpleUserContentPart(value) + }); + } } else { const aiResults: ChatCompletionMessageParam[] = []; - const existsPlanId = new Set(); item.value.forEach((value, i) => { - // 只需要把根节点转化即可 - if (value.stepId) return; + /* Plan agent 产生的上下文都需要合并到一个 toolCall 里。 + Plan agent 产生的上下文都会携带 planId + value.plan 代表的是 plan 的具体内容,根据这个值去转化成 toolcall + */ + if (value.planId && !value.plan) return; - if ((value.tools || value.tool) && reserveTool) { - const tools = value.tools || [value.tool!]; + const hasTools = Array.isArray(value.tools) && value.tools.length > 0; + + if (reserveTool && (hasTools || value.tool)) { + const tools = hasTools ? value.tools! : [value.tool!]; const tool_calls: ChatCompletionMessageToolCall[] = []; const toolResponse: ChatCompletionToolMessageParam[] = []; tools.forEach((tool) => { @@ -150,33 +158,25 @@ export const chats2GPTMessages = ({ content: value.text.content }); } - } else if (value.plan && reserveTool) { + } else if (value.plan) { + // 查找该 Plan 产生的上下文,组成一个 toolcall + // 需要跨所有历史消息收集同 planId 的 values(ask 信息可能在之前的 AI 消息中) const planId = value.plan.planId; - if (existsPlanId.has(planId)) { - return; - } - existsPlanId.add(planId); - const steps = item.value - .filter((item) => item.plan?.planId === planId) - .flatMap((item) => item.plan?.steps || []) - .map((step) => { - const stepResponse = item.value - .filter((item) => item.stepId === step.id) - ?.map((item) => item.text?.content) - .join('\n'); - - return { - title: step.title, - response: stepResponse - }; - }); - const toolId = getNanoid(6); + const allPlanValues = messages + .filter((msg) => msg.obj === ChatRoleEnum.AI) + .flatMap((msg) => + (msg.value as AIChatItemValueItemType[]).filter((v) => v.planId === planId) + ); + const planResponseText = getPlanCallResponseText({ + plan: value.plan, + assistantResponses: allPlanValues + }); aiResults.push({ dataId, role: ChatCompletionRequestMessageRoleEnum.Assistant, tool_calls: [ { - id: toolId, + id: planId, type: 'function', function: { name: 'plan_agent', @@ -192,25 +192,11 @@ export const chats2GPTMessages = ({ aiResults.push({ dataId, role: ChatCompletionRequestMessageRoleEnum.Tool, - tool_call_id: toolId, - content: JSON.stringify(steps) + tool_call_id: planId, + content: planResponseText }); } else if (value.interactive) { - if (value.interactive.type === 'agentPlanAskQuery') { - aiResults.push({ - dataId, - role: ChatCompletionRequestMessageRoleEnum.Assistant, - content: value.interactive.params.content - }); - } else if (value.interactive.type === 'agentPlanAskUserForm') { - aiResults.push({ - dataId, - role: ChatCompletionRequestMessageRoleEnum.Assistant, - content: `${value.interactive.params.description} - -Answer: ${value.interactive.params.inputForm.map((item) => `- ${item.label}: ${item.value}`).join('\n')}` - }); - } + // 目前只有 plan 里会有交互,所以这里暂时不需要处理 } }); diff --git a/packages/global/core/chat/type.ts b/packages/global/core/chat/type.ts index 45fab405a6..64dec5cf82 100644 --- a/packages/global/core/chat/type.ts +++ b/packages/global/core/chat/type.ts @@ -85,6 +85,7 @@ export const UserChatItemFileItemSchema = z.object({ export type UserChatItemFileItemType = z.infer; export const UserChatItemValueItemSchema = z.object({ + planId: z.string().nullish(), text: z .object({ content: z.string() @@ -130,6 +131,7 @@ export type AdminFbkType = z.infer; export const AIChatItemValueSchema = z.object({ id: z.string().nullish(), stepId: z.string().nullish(), + planId: z.string().nullish(), text: z .object({ content: z.string() diff --git a/packages/global/core/chat/utils.ts b/packages/global/core/chat/utils.ts index 3dba777241..2430c20f18 100644 --- a/packages/global/core/chat/utils.ts +++ b/packages/global/core/chat/utils.ts @@ -12,6 +12,88 @@ import { PublishChannelEnum } from '../../support/outLink/constant'; import { removeDatasetCiteText } from '../ai/llm/utils'; import type { WorkflowInteractiveResponseType } from '../workflow/template/system/interactive/type'; import { ConfirmPlanAgentText } from '../workflow/runtime/constants'; +import type { AgentPlanType } from '../ai/agent/type'; + +export type PlanAskInfo = { + question: string; + answer: string; +}; + +export const getPlanCallResponseText = ({ + plan, + assistantResponses +}: { + plan: AgentPlanType; + assistantResponses: AIChatItemValueItemType[]; +}): string => { + // 1. 获取 ask 信息 + const askText = (() => { + const asks = assistantResponses + .map((item) => { + const interactive = item.interactive; + if (!interactive) return; + if (interactive.type === 'agentPlanAskQuery') { + const question = interactive.params?.content?.trim(); + if (!question) return; + const answer = interactive.params?.answer?.trim() || undefined; + return JSON.stringify({ question, answer }); + } + + if (interactive.type === 'agentPlanAskUserForm') { + const question = interactive.params?.description?.trim(); + const answer = + interactive.params?.inputForm + ?.map((item) => { + if (!item?.label) return ''; + const val = + typeof item.value === 'object' ? JSON.stringify(item.value) : String(item.value); + return `${item.label}: ${val}`; + }) + .filter(Boolean) + .join('; ') || undefined; + + if (!question && !answer) return; + return JSON.stringify({ question, answer }); + } + return undefined; + }) + .filter(Boolean) as string[]; + + return asks.join('\n'); + })(); + + // 2. 获取 step 信息; 如果是中途暂停,则需要提示用户暂停 + const { stepText, isPause } = (() => { + const stepValues = assistantResponses.filter((item) => item.stepId); + let isPause = false; + const stepResults = plan.steps.map((step, index) => { + const result = stepValues + .filter((item) => item.stepId === step.id) + .map((item) => item.text?.content?.trim() || '') + .filter(Boolean) + .join('\n'); + + const executed = !!result; + + if (!executed) { + isPause = true; + } + + return `(${index + 1}) [${executed ? `executed` : `pending`}] id=${step.id}; title=${step.title || ''}; description=${step.description || ''}${result ? `; result: ${result}` : ''}`; + }); + + return { + stepText: stepResults.join('\n'), + isPause + }; + })(); + + return `${isPause ? 'PLAN_PAUSE_HANDOFF' : ''} +COLLECTED INFO: +${askText} +STEPS: +${stepText}`; +}; // Concat 2 -> 1, and sort by role export const concatHistories = (histories1: ChatItemType[], histories2: ChatItemType[]) => { diff --git a/packages/global/core/workflow/template/system/interactive/type.ts b/packages/global/core/workflow/template/system/interactive/type.ts index b61c7cfbf7..ae7b8e8e6c 100644 --- a/packages/global/core/workflow/template/system/interactive/type.ts +++ b/packages/global/core/workflow/template/system/interactive/type.ts @@ -88,7 +88,8 @@ export type AgentPlanCheckInteractive = z.infer; @@ -147,16 +148,21 @@ export const PaymentPauseInteractiveSchema = z.object({ }); export type PaymentPauseInteractive = z.infer; -export const InteractiveNodeResponseTypeSchema = z.discriminatedUnion('type', [ - UserSelectInteractiveSchema, - UserInputInteractiveSchema, - ChildrenInteractiveSchema, - ToolCallChildrenInteractiveSchema, - LoopInteractiveSchema, - PaymentPauseInteractiveSchema, - AgentPlanCheckInteractiveSchema, - AgentPlanAskQueryInteractiveSchema -]); +export const InteractiveNodeResponseTypeSchema = z.intersection( + z.discriminatedUnion('type', [ + UserSelectInteractiveSchema, + UserInputInteractiveSchema, + ChildrenInteractiveSchema, + ToolCallChildrenInteractiveSchema, + LoopInteractiveSchema, + PaymentPauseInteractiveSchema, + AgentPlanCheckInteractiveSchema, + AgentPlanAskQueryInteractiveSchema + ]), + z.object({ + planId: z.string().optional() + }) +); export type InteractiveNodeResponseType = z.infer; export const WorkflowInteractiveResponseTypeSchema = z.intersection( diff --git a/packages/global/package.json b/packages/global/package.json index b4f5a1d987..094cdb071c 100644 --- a/packages/global/package.json +++ b/packages/global/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "engines": { "node": ">=20", - "pnpm": "10.x" + "pnpm": "9.x" }, "dependencies": { "@fastgpt-sdk/plugin": "0.3.8", diff --git a/packages/service/core/ai/llm/request.ts b/packages/service/core/ai/llm/request.ts index 61c9951964..04dd1aa4ed 100644 --- a/packages/service/core/ai/llm/request.ts +++ b/packages/service/core/ai/llm/request.ts @@ -85,11 +85,12 @@ export const createLLMResponse = async ( const { throwError = true, body, custonHeaders, userKey, maxContinuations = 1 } = args; const { messages, useVision, requestOrigin, tools, toolCallMode } = body; + const model = getLLMModel(body.model); // Messages process const requestMessages = await loadRequestMessages({ messages, - useVision, + useVision: useVision && model.vision, origin: requestOrigin }); // Message process diff --git a/packages/service/core/ai/utils.ts b/packages/service/core/ai/utils.ts index 08fcf9383f..2024585921 100644 --- a/packages/service/core/ai/utils.ts +++ b/packages/service/core/ai/utils.ts @@ -4,6 +4,7 @@ import { getLLMDefaultUsage } from '@fastgpt/global/core/ai/constants'; import { removeDatasetCiteText } from '@fastgpt/global/core/ai/llm/utils'; import json5 from 'json5'; import { sliceJsonStr } from '@fastgpt/global/common/string/tools'; +import { jsonrepair } from 'jsonrepair'; /* Count response max token @@ -332,7 +333,7 @@ export const parseLLMStreamResponse = () => { export const parseJsonArgs = >(str: string) => { try { - return json5.parse(sliceJsonStr(str)) as T; + return json5.parse(jsonrepair(sliceJsonStr(str))) as T; } catch { return; } diff --git a/packages/service/core/chat/saveChat.ts b/packages/service/core/chat/saveChat.ts index 106796a9bb..d43eb18a43 100644 --- a/packages/service/core/chat/saveChat.ts +++ b/packages/service/core/chat/saveChat.ts @@ -458,7 +458,25 @@ export const updateInteractiveChat = async ({ interactive, input: userInteractiveVal }); + // 提取嵌套在子流程里的交互节点 + const finalInteractive = extractDeepestInteractive(interactive); if (status === 'query') { + // 特殊处理: + { + // 1. AskQuery 需要把用户答案回填到上一条 interactive,避免后续多轮恢复时丢失 answer。 + if (finalInteractive.type === 'agentPlanAskQuery') { + finalInteractive.params.answer = userInteractiveVal; + chatItem.value[chatItem.value.length - 1].interactive = interactive; + chatItem.markModified('value'); + await chatItem.save(); + + // 追加 PlanId 给 userItem(便于适配器会跳过转化该条消息) + props.userContent.value.forEach((item) => { + item.planId = finalInteractive.planId; + }); + } + } + return await pushChatRecords(props); } @@ -483,9 +501,6 @@ export const updateInteractiveChat = async ({ */ // Update interactive value { - // 提取嵌套在子流程里的交互节点 - const finalInteractive = extractDeepestInteractive(interactive); - if ( finalInteractive.type === 'userSelect' || finalInteractive.type === 'agentPlanAskUserSelect' diff --git a/packages/service/core/workflow/dispatch/ai/agent/index.ts b/packages/service/core/workflow/dispatch/ai/agent/index.ts index ab25a3b76f..2bd6e3d741 100644 --- a/packages/service/core/workflow/dispatch/ai/agent/index.ts +++ b/packages/service/core/workflow/dispatch/ai/agent/index.ts @@ -18,8 +18,10 @@ import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants'; import { chats2GPTMessages, chatValue2RuntimePrompt, + runtimePrompt2ChatsValue, GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt'; +import { getPlanCallResponseText } from '@fastgpt/global/core/chat/utils'; import { filterMemoryMessages } from '../utils'; import { parseI18nString } from '@fastgpt/global/common/i18n/utils'; import { systemSubInfo } from '@fastgpt/global/core/workflow/node/agent/constants'; @@ -41,6 +43,7 @@ export type DispatchAgentModuleProps = ModuleDispatchProps<{ [NodeInputKeyEnum.history]?: ChatItemType[]; [NodeInputKeyEnum.userChatInput]: string; + [NodeInputKeyEnum.aiChatVision]?: boolean; [NodeInputKeyEnum.fileUrlList]?: string[]; [NodeInputKeyEnum.aiModel]: string; [NodeInputKeyEnum.aiSystemPrompt]: string; @@ -94,6 +97,9 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise } } = props; const chatHistories = getHistories(history, histories); + const aiHistoryValues = chatHistories + .filter((item) => item.obj === ChatRoleEnum.AI) + .flatMap((item) => item.value); const historiesMessages = chats2GPTMessages({ messages: chatHistories, reserveId: false, @@ -126,16 +132,25 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise }); // 交互模式进来的话,这个值才是交互输入的值 - const queryInput = chatValue2RuntimePrompt(query).text; + const { text: queryInput, files: queryFiles } = chatValue2RuntimePrompt(query); const formatUserChatInput = fileInputPrompt ? `${fileInputPrompt}\n\n${userChatInput}` : userChatInput; + const currentUserMessage = chats2GPTMessages({ + messages: [ + { + obj: ChatRoleEnum.Human, + value: runtimePrompt2ChatsValue({ + text: formatUserChatInput, + files: queryFiles + }) + } + ], + reserveId: false + })[0]; let { - masterMessages = historiesMessages.concat({ - role: 'user', - content: formatUserChatInput - }), + masterMessages: restoredMasterMessages, planHistoryMessages, agentPlan, planBuffer @@ -159,6 +174,18 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise }; })(); + let masterMessages = (() => { + if (!restoredMasterMessages) { + return historiesMessages.concat(currentUserMessage ? [currentUserMessage] : []); + } else if (planHistoryMessages?.length) { + return restoredMasterMessages ?? historiesMessages; + } else { + return currentUserMessage + ? restoredMasterMessages.concat(currentUserMessage) + : restoredMasterMessages; + } + })(); + // Get sub apps const { completionTools: agentCompletionTools, subAppsMap: agentSubAppsMap } = await getSubapps( { @@ -234,11 +261,15 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise }); } + if (askInteractive) { + // 这里存储一份冗余的 planId,便于告诉 AI value 也存储一份 planId + askInteractive.planId = planBuffer?.planId; + } + return { completeMessages, askInteractive, - plan, - planBuffer + plan }; }; const planCallFn = async () => { @@ -286,15 +317,12 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise getLogger(LogCategories.MODULE.AI.AGENT).debug( `All steps completed, check if need continue planning` ); - const stepsResponse = agentPlan.steps.map((step) => { - const stepResponse = assistantResponses - .filter((item) => item.stepId === step.id) - ?.map((item) => item.text?.content) - .join('\n'); - return { - title: step.title, - response: stepResponse - }; + const planResponseText = getPlanCallResponseText({ + plan: agentPlan, + assistantResponses: [ + ...aiHistoryValues.filter((v) => v.planId === agentPlan!.planId), + ...assistantResponses + ] }); try { @@ -310,11 +338,9 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise task: agentPlan.task, description: agentPlan.description, background: agentPlan.background, - response: JSON.stringify(stepsResponse) + response: planResponseText }), - task: agentPlan.task, - description: agentPlan.description, - background: agentPlan.background + ...agentPlan }); const { plan: continuePlan } = parsePlanCallResult(result); @@ -350,6 +376,7 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise break; } + // Step calls if (agentPlan) { while (!checkIsStopping() && agentPlan.steps.filter((item) => !item.response).length) { for await (const step of agentPlan.steps) { @@ -357,8 +384,10 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise break; } if (step.response) continue; + getLogger(LogCategories.MODULE.AI.AGENT).debug(`Step call: ${step.id}`, step); assistantResponses.push({ + planId: agentPlan.planId, stepTitle: { stepId: step.id, title: step.title @@ -399,6 +428,7 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise .flat() .map((item) => ({ ...item, + planId: agentPlan!.planId, stepId: step.id })); assistantResponses.push(...assistantResponse); @@ -408,25 +438,23 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise } } + // 用户主动暂停(相当于强制结束本轮 task,会清空所有状态) if (checkIsStopping()) { break; } // 所有步骤执行完后,固定调用 Plan Agent(继续规划模式) - const stepsResponse = agentPlan.steps.map((step) => { - const stepResponse = assistantResponses - .filter((item) => item.stepId === step.id) - ?.map((item) => item.text?.content) - .join('\n'); - return { - title: step.title, - response: stepResponse - }; + const planResponseText = getPlanCallResponseText({ + plan: agentPlan, + assistantResponses: [ + ...aiHistoryValues.filter((v) => v.planId === agentPlan!.planId), + ...assistantResponses + ] }); // 拼接 plan response 到 masterMessages 的 plan tool call 里(肯定在最后一个) const lastToolIndex = masterMessages.findLastIndex((item) => item.role === 'tool'); if (lastToolIndex !== -1) { - masterMessages[lastToolIndex].content = JSON.stringify(stepsResponse); + masterMessages[lastToolIndex].content = planResponseText; } planIterationCount++; @@ -452,7 +480,6 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise } } else { getLogger(LogCategories.MODULE.AI.AGENT).debug(`Start master agent`); - const result = await masterCall({ ...props, masterMessages, @@ -478,7 +505,7 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise // 触发了 plan if (result.planResponse) { - const { completeMessages, askInteractive, plan, planBuffer } = parsePlanCallResult( + const { completeMessages, askInteractive, plan } = parsePlanCallResult( result.planResponse ); @@ -490,7 +517,7 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise [masterMessagesKey]: masterMessages, [planMessagesKey]: filterMemoryMessages(completeMessages), [agentPlanKey]: plan, - [planBufferKey]: planBuffer + [planBufferKey]: result.planResponse.planBuffer }, [DispatchNodeResponseKeyEnum.interactive]: askInteractive, [DispatchNodeResponseKeyEnum.nodeResponses]: nodeResponses diff --git a/packages/service/core/workflow/dispatch/ai/agent/master/call.ts b/packages/service/core/workflow/dispatch/ai/agent/master/call.ts index 6e32d989ed..c5fbcb7f81 100644 --- a/packages/service/core/workflow/dispatch/ai/agent/master/call.ts +++ b/packages/service/core/workflow/dispatch/ai/agent/master/call.ts @@ -92,7 +92,8 @@ export const masterCall = async ({ // Dataset search configuration agent_datasetParams: datasetParams, // Sandbox (Computer Use) - useAgentSandbox = false + useAgentSandbox = false, + aiChatVision } } = props; @@ -215,6 +216,7 @@ export const masterCall = async ({ messages: requestMessages, model: getLLMModel(model), stream: true, + useVision: aiChatVision, tools: isStepCall ? completionTools.filter((item) => item.function.name !== SubAppIds.plan) : completionTools @@ -425,9 +427,8 @@ export const masterCall = async ({ model, stream, mode: 'initial', - task: toolArgs.data.task, - description: toolArgs.data.description, - background: toolArgs.data.background + ...toolArgs.data, + planId: call.id }); return { @@ -605,7 +606,7 @@ export const masterCall = async ({ response, assistantMessages: [], // TODO usages, - stop + stop: stop || checkIsStopping() }; }, onToolCompress: ({ call, response, usage }) => { diff --git a/packages/service/core/workflow/dispatch/ai/agent/master/prompt.ts b/packages/service/core/workflow/dispatch/ai/agent/master/prompt.ts index 43ddd74225..723d2f0744 100644 --- a/packages/service/core/workflow/dispatch/ai/agent/master/prompt.ts +++ b/packages/service/core/workflow/dispatch/ai/agent/master/prompt.ts @@ -122,6 +122,50 @@ ${ | 明显需要工具的任务 | **提示模式**(引导用户选择工具或进入辅助生成) | + +## 第五步:总结模式目标对齐策略 + +进入总结模式前,先锁定当前任务目标: + +1. 当前输入优先 +- 优先使用本轮最新用户输入来确定当前任务目标 + +2. 短指令回退规则 +- 若用户仅输入“总结一下”“汇总下进度”等短指令,且未给出新任务目标 +- 默认总结最近任务目标 + +3. 最近任务目标定义 +- 优先提取当前上下文处在最后面的任务目标 + +4. 目标冲突处理 +- 若当前输入与旧目标冲突,旧目标仅可作为背景证据 +- 总结结论必须围绕当前任务目标组织 + + + +## 第六步:Pause 后软续跑策略 + +仅当满足下面条件时,才启用 pause 续跑策略: +- 最近一条工具消息内容以 "PLAN_PAUSE_HANDOFF" 开头 +- 该工具消息位于当前用户输入之前的最近上下文位置 +- 若只是更早历史里的 handoff 记录,不视为本轮 pause 恢复信号 + +1. 仅当用户输入明显表达“继续”意图时,优先延续旧 plan +- 继续意图示例:继续、接着、按上次、继续执行、往下做、resume、continue +- 命中后优先调用 ${SubAppIds.plan} + +2. 命中继续意图时,调用 ${SubAppIds.plan} +- 复用 handoff 里的 信息 +- 明确继承 ask 的 question 和 answer(若存在) +- 优先推进 pending steps,避免重复 executed steps + +3. 若用户新输入与旧 plan 目标明显冲突 +- 直接按新输入重规划任务,不强行续旧 plan + +4. 若没有继续意图 +- 按 正常决策,不自动续跑旧 plan + + 用户:"什么是人工智能?" @@ -167,6 +211,34 @@ ${ 判断:信息不够完整 决策:调用 ${SubAppIds.plan} 补充规划 + + +用户:"继续执行上次的计划" +上下文:包含 PLAN_PAUSE_HANDOFF,且存在 pending steps +判断:用户明确要继续,且旧计划可延续 +决策:优先调用 ${SubAppIds.plan},把 handoff 的任务和步骤进度整理进 background,优先执行 pending steps + + + +用户:"不要继续上次了,改成帮我做竞品调研" +上下文:包含 PLAN_PAUSE_HANDOFF,但用户目标已明显变化 +判断:新输入与旧计划冲突 +决策:按新输入直接重规划,不强行沿用旧 pending steps + + + +用户:"总结一下" +上下文:最近存在 PLAN_PAUSE_HANDOFF,同时更早历史里还有其他任务记录 +判断:用户未给新目标,属于短指令总结 +决策:按最近任务目标总结,不扩散到更早历史任务 + + + +用户:"别按上次方向了,直接总结这次关于成本优化的结论" +上下文:存在旧任务 handoff,但与本轮目标冲突 +判断:当前输入给出了明确新目标 +决策:按当前输入目标总结,旧目标只作为背景证据 + @@ -205,8 +277,10 @@ ${ - 需要工具的任务 → 明确告知用户缺少工具,引导选择工具或进入辅助生成模式,例如:"当前没有可用的工具来完成此任务,请先选择相关工具或进入辅助生成模式自动化生成任务规划" **有工具时的执行**: -- 总结模式:整合已有信息,生成结构化输出,需要基于之前的上下文所有信息来输出一个完整且详细的总结, - 不是简单回答,需要直接把详细的总结信息给输出出来,把上下执行的信息都整合进去,这一步越详细越好 +- 总结模式:先在内部确认当前任务目标(优先本轮用户输入;短指令则回退最近任务目标), + 再结合当前历史记录中与目标相关的全部信息输出详细总结,不要只给简短结论,把上下执行的信息都整合进去,这一步越详细越好,如果不详细很可能会重做 + 这是不可允许的,一旦进入总结模式就需要根据上下文并结合目标来完成报告。 + 若当前目标信息不足,不要硬总结,转工具模式或规划模式补充信息 - 工具模式:调用最合适的工具 - 规划模式:调用 ${SubAppIds.plan},让规划系统接管,同时需要对当前的上下文信息进行总结,尤其是之前向用户询问的一些问题和用户的回答(避免重复的询问同一个问题), 以及已经执行的步骤和结果,未执行的步骤信息,帮助规划系统更好地理解当前状态,最后将上诉的信息放到 background 的参数里 diff --git a/packages/service/core/workflow/dispatch/ai/agent/sub/plan/constants.ts b/packages/service/core/workflow/dispatch/ai/agent/sub/plan/constants.ts index 26ab0a8227..5b3ffbfd88 100644 --- a/packages/service/core/workflow/dispatch/ai/agent/sub/plan/constants.ts +++ b/packages/service/core/workflow/dispatch/ai/agent/sub/plan/constants.ts @@ -1,6 +1,7 @@ import type { ChatCompletionTool } from '@fastgpt/global/core/ai/type'; import { SubAppIds, systemSubInfo } from '@fastgpt/global/core/workflow/node/agent/constants'; import type { InteractiveNodeResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type'; +import { getNanoid } from '@fastgpt/global/common/string/tools'; import z from 'zod'; export const PlanCheckInteractive: InteractiveNodeResponseType = { @@ -13,7 +14,8 @@ export const PlanCheckInteractive: InteractiveNodeResponseType = { export const PlanAgentParamsSchema = z.object({ task: z.string(), description: z.string(), - background: z.string().nullish() + background: z.string().nullish(), + planId: z.string().default(() => getNanoid(6)) }); export type PlanAgentParamsType = z.infer; export const PlanAgentTool: ChatCompletionTool = { diff --git a/packages/service/core/workflow/dispatch/ai/agent/sub/plan/index.ts b/packages/service/core/workflow/dispatch/ai/agent/sub/plan/index.ts index 393409605b..4d91429d36 100644 --- a/packages/service/core/workflow/dispatch/ai/agent/sub/plan/index.ts +++ b/packages/service/core/workflow/dispatch/ai/agent/sub/plan/index.ts @@ -4,13 +4,17 @@ import type { ChatCompletionTool } from '@fastgpt/global/core/ai/type'; import { createLLMResponse } from '../../../../../../ai/llm/request'; -import { getInitialPlanPrompt, getContinuePlanPrompt, getInitialPlanQuery } from './prompt'; +import { + getInitialPlanPrompt, + getContinuePlanPrompt, + getInitialPlanQuery, + reTryPlanPrompt +} from './prompt'; import { getLLMModel } from '../../../../../../ai/model'; import { formatModelChars2Points } from '../../../../../../../support/wallet/usage/utils'; import type { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type'; import type { - AgentPlanAskQueryInteractive, - UserInputInteractive, + InteractiveNodeResponseType, WorkflowInteractiveResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type'; import { parseJsonArgs } from '../../../../../../ai/utils'; @@ -29,6 +33,8 @@ import type { PlanAgentParamsType } from './constants'; import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type'; import { getLogger, LogCategories } from '../../../../../../../common/logger'; +const agentLogger = getLogger(LogCategories.MODULE.AI.AGENT); + type PlanAgentConfig = { systemPrompt?: string; model: string; @@ -61,7 +67,7 @@ type DispatchPlanAgentProps = PlanAgentConfig & } & (InitialParams | ContinueParams | InteractiveParams); export type DispatchPlanAgentResponse = { - askInteractive?: UserInputInteractive | AgentPlanAskQueryInteractive; + askInteractive?: InteractiveNodeResponseType; plan?: AgentPlanType; planBuffer: PlanAgentParamsType; completeMessages: ChatCompletionMessageParam[]; @@ -71,6 +77,7 @@ export type DispatchPlanAgentResponse = { const parsePlan = async ({ text, + planId, task, description, background @@ -81,19 +88,22 @@ const parsePlan = async ({ return; } - const result = parseJsonArgs(text); + const result = parseJsonArgs<{ steps: AgentPlanType['steps'] }>(text); + if (!result) { - return; + return result; } const params = await AgentPlanSchema.safeParseAsync({ ...result, + planId, task, description, background }); + if (!params.success) { - getLogger(LogCategories.MODULE.AI.AGENT).warn(`[Plan Agent] Not plan`, { text }); + agentLogger.warn(`[Plan Agent] Not plan`, { text }); return; } @@ -101,7 +111,7 @@ const parsePlan = async ({ }; const parseAskInteractive = async ( toolCalls: ChatCompletionMessageToolCall[] -): Promise => { +): Promise => { const tooCall = toolCalls[0]; if (!tooCall) return; const params = await AIAskAnswerSchema.safeParseAsync(parseJsonArgs(tooCall.function.arguments)); @@ -141,7 +151,7 @@ const parseAskInteractive = async ( } }; } else { - getLogger(LogCategories.MODULE.AI.AGENT).warn(`[Plan Agent] Ask tool params is not valid`, { + agentLogger.warn(`[Plan Agent] Ask tool params is not valid`, { tooCall }); return; @@ -161,6 +171,7 @@ export const dispatchPlanAgent = async ({ getSubAppInfo, systemPrompt, model, + planId, task, description, background, @@ -202,7 +213,7 @@ export const dispatchPlanAgent = async ({ content: props.queryInput }); } else { - getLogger(LogCategories.MODULE.AI.AGENT).error('Plan interactive mode error', { + agentLogger.error('Plan interactive mode error', { planMessages: props.planMessages }); return Promise.reject('Plan interactive mode error'); @@ -226,6 +237,14 @@ export const dispatchPlanAgent = async ({ // console.dir({ requestMessages }, { depth: null }); // console.log('userInput:', userInput, 'mode:', mode, 'interactive?.type:', interactive?.type); + const requestParams = { + model: modelData.model, + stream: true, + tools: props.mode === 'continue' ? undefined : [AIAskTool], + tool_choice: 'auto' as const, + toolCallMode: modelData.toolChoice ? ('toolChoice' as const) : ('prompt' as const), + parallel_tool_calls: false + }; let { answerText, toolCalls = [], @@ -236,13 +255,8 @@ export const dispatchPlanAgent = async ({ } = await createLLMResponse({ isAborted: checkIsStopping, body: { - model: modelData.model, messages: requestMessages, - stream: true, - tools: props.mode === 'continue' ? undefined : [AIAskTool], - tool_choice: 'auto', - toolCallMode: modelData.toolChoice ? 'toolChoice' : 'prompt', - parallel_tool_calls: false + ...requestParams } }); @@ -250,21 +264,93 @@ export const dispatchPlanAgent = async ({ return Promise.reject(responseEmptyTip); } + const llmRequestIds: string[] = [requestId]; /* 正常输出情况: 1. text: 正常生成plan 2. toolCall: 调用ask工具 3. text + confirm: 成功生成工具 + 确认操作 */ - // 获取生成的 plan - const plan = await parsePlan({ - text: answerText, - task, - description, - background - }); - // 获取交互结果 - const askInteractive = await parseAskInteractive(toolCalls); + // 1. 首次获取交互结果 + const { askInteractive, plan } = await (async () => { + // 1. 首次获取交互结果 + let [askInteractive, plan] = await Promise.all([ + parseAskInteractive(toolCalls), + parsePlan({ + text: answerText, + planId, + task, + description, + background + }) + ]); + if (plan || askInteractive) { + return { + askInteractive, + plan + }; + } + + // 2. 二次尝试生成 plan + agentLogger.warn('[Plan Agent] parse failed, try regenerate plan once', { + requestId, + mode: props.mode, + answerText: answerText.slice(0, 2000) + }); + + const regenerateResponse = await createLLMResponse({ + isAborted: checkIsStopping, + body: { + messages: [ + ...completeMessages, + { + role: 'user', + content: reTryPlanPrompt + } + ], + ...requestParams + } + }); + usage.inputTokens += regenerateResponse.usage.inputTokens; + usage.outputTokens += regenerateResponse.usage.outputTokens; + llmRequestIds.push(regenerateResponse.requestId); + completeMessages = regenerateResponse.completeMessages; + + [askInteractive, plan] = await Promise.all([ + parseAskInteractive(regenerateResponse.toolCalls || []), + parsePlan({ + text: regenerateResponse.answerText, + planId, + task, + description, + background + }) + ]); + if (plan || askInteractive) { + return { + askInteractive, + plan + }; + } + + // 真的失败了 + agentLogger.warn('[Plan Agent] plan regenerate failed', { + requestId, + regenerateRequestId: regenerateResponse.requestId, + mode: props.mode, + answerText: regenerateResponse.answerText.slice(0, 2000) + }); + askInteractive = { + type: 'agentPlanAskQuery', + params: { + content: i18nT('chat:agent_plan_parse_retry_tip') + } + }; + + return { + askInteractive + }; + })(); const { totalPoints, modelName } = formatModelChars2Points({ model: modelData.model, @@ -285,13 +371,14 @@ export const dispatchPlanAgent = async ({ totalPoints, model: modelName, runningTime: +((Date.now() - startTime) / 1000).toFixed(2), - llmRequestIds: [requestId] + llmRequestIds }; return { askInteractive, plan, planBuffer: { + planId, task, description, background diff --git a/packages/service/core/workflow/dispatch/ai/agent/sub/plan/prompt.ts b/packages/service/core/workflow/dispatch/ai/agent/sub/plan/prompt.ts index e3f65de277..d540eebb80 100644 --- a/packages/service/core/workflow/dispatch/ai/agent/sub/plan/prompt.ts +++ b/packages/service/core/workflow/dispatch/ai/agent/sub/plan/prompt.ts @@ -1092,7 +1092,11 @@ ${bestPractices} `; }; -export const getInitialPlanQuery = ({ task, description, background }: PlanAgentParamsType) => { +export const getInitialPlanQuery = ({ + task, + description, + background +}: Omit) => { return `## 任务目标 ${task} @@ -1108,7 +1112,7 @@ export const getContinuePlanQuery = ({ description, background, response -}: PlanAgentParamsType & { +}: Omit & { response: string; }) => { return `${getInitialPlanQuery({ task, description, background })} @@ -1121,3 +1125,29 @@ ${response} ## 下一步任务 请基于已执行步骤及结果,根据系统提示词来判断是否需要继续规划、生成总结报告步骤、还是任务已完成,或者遇到问题直接返回`; }; + +export const reTryPlanPrompt = `上一轮 plan 输出不是合法 JSON,无法解析。 + +请基于原始任务重新生成完整 plan,严格按 JSON 输出。 + +要求: +- 仅返回 JSON +- 包含 task 和 steps 字段 +- 每个 step 必须包含 id/title/description + +JSON 格式示例(只参考格式,不要照抄内容): +{ + "task": "深入了解 Rust 编程语言(系统编程方向)", + "steps": [ + { + "id": "step1", + "title": "了解 Rust 的核心特性", + "description": "使用 @webSearch 搜索 Rust 的所有权、借用检查与并发安全机制" + }, + { + "id": "step2", + "title": "调研 Rust 在系统编程的应用", + "description": "使用 @webSearch 搜索 Rust 在操作系统、网络编程、嵌入式中的典型项目" + } + ] +}`; diff --git a/packages/service/core/workflow/dispatch/index.ts b/packages/service/core/workflow/dispatch/index.ts index 810466455f..49f7ab7a6a 100644 --- a/packages/service/core/workflow/dispatch/index.ts +++ b/packages/service/core/workflow/dispatch/index.ts @@ -211,10 +211,15 @@ export async function dispatchWorkFlow({ const checkStoppingTimer = apiVersion === 'v2' ? setInterval(async () => { - stopping = await shouldWorkflowStop({ + if (stopping) return; + + const shouldStop = await shouldWorkflowStop({ appId: runningAppInfo.id, chatId }); + if (shouldStop) { + stopping = true; + } }, 100) : undefined; @@ -1209,11 +1214,11 @@ export class WorkflowQueue { } /* - 特殊情况: - 通过 skipEdges 可以判断是运行了分支节点。 - 由于分支节点,可能会实现递归调用(skip 连线往前递归) - 需要把分支节点也加入到已跳过的记录里,可以保证递归 skip 运行时,至多只会传递到当前分支节点,不会影响分支后的内容。 - */ + 特殊情况: + 通过 skipEdges 可以判断是运行了分支节点。 + 由于分支节点,可能会实现递归调用(skip 连线往前递归) + 需要把分支节点也加入到已跳过的记录里,可以保证递归 skip 运行时,至多只会传递到当前分支节点,不会影响分支后的内容。 + */ const skipEdges = (nodeRunResult.result[DispatchNodeResponseKeyEnum.skipHandleId] || []) as string[]; if (skipEdges && skipEdges?.length > 0) { @@ -1310,6 +1315,7 @@ export class WorkflowQueue { } return { + planId: interactiveResult.planId, interactive: interactiveResult }; } diff --git a/packages/service/package.json b/packages/service/package.json index f57fb4e9ec..00ebf8c431 100644 --- a/packages/service/package.json +++ b/packages/service/package.json @@ -4,11 +4,11 @@ "type": "module", "engines": { "node": ">=20", - "pnpm": "10.x" + "pnpm": "9.x" }, "dependencies": { "@apidevtools/json-schema-ref-parser": "^11.7.2", - "@fastgpt-sdk/sandbox-adapter": "^0.0.21", + "@fastgpt-sdk/sandbox-adapter": "^0.0.22", "@fastgpt-sdk/storage": "catalog:", "@fastgpt-sdk/logger": "catalog:", "@fastgpt/global": "workspace:*", @@ -36,6 +36,7 @@ "ioredis": "^5.6.0", "joplin-turndown-plugin-gfm": "^1.0.12", "json5": "catalog:", + "jsonrepair": "^3.0.0", "jsonpath-plus": "^10.3.0", "jsonwebtoken": "^9.0.2", "lodash": "catalog:", diff --git a/packages/web/i18n/en/chat.json b/packages/web/i18n/en/chat.json index 8e64be3959..bf7526e88a 100644 --- a/packages/web/i18n/en/chat.json +++ b/packages/web/i18n/en/chat.json @@ -5,6 +5,7 @@ "Next": "Next", "Previous": "Previous", "agent_plan_continue": "Continue planning", + "agent_plan_parse_retry_tip": "The plan format was invalid. Add one more requirement and I will regenerate the plan.", "ai_reasoning": "Thinking process", "back_to_text": "Text input", "balance_not_enough_pause": "Workflow paused due to insufficient AI points", diff --git a/packages/web/i18n/zh-CN/chat.json b/packages/web/i18n/zh-CN/chat.json index e2cd33d770..53e554b714 100644 --- a/packages/web/i18n/zh-CN/chat.json +++ b/packages/web/i18n/zh-CN/chat.json @@ -5,6 +5,7 @@ "Next": "下一个", "Previous": "上一个", "agent_plan_continue": "继续规划", + "agent_plan_parse_retry_tip": "规划结果格式异常,请补充一句需求后我重新生成计划。", "ai_reasoning": "思考过程", "back_to_text": "返回输入", "balance_not_enough_pause": "由于 AI 积分不足,暂停运行工作流", diff --git a/packages/web/i18n/zh-Hant/chat.json b/packages/web/i18n/zh-Hant/chat.json index 7569bd37f3..99d44d3802 100644 --- a/packages/web/i18n/zh-Hant/chat.json +++ b/packages/web/i18n/zh-Hant/chat.json @@ -5,6 +5,7 @@ "Next": "下一個", "Previous": "上一個", "agent_plan_continue": "繼續規劃", + "agent_plan_parse_retry_tip": "規劃結果格式異常,請補充一句需求後我重新生成計畫。", "ai_reasoning": "思考過程", "back_to_text": "返回輸入", "balance_not_enough_pause": "由於 AI 積分不足,暫停運行工作流", diff --git a/packages/web/package.json b/packages/web/package.json index eccbffa6de..7866d69e9a 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "engines": { "node": ">=20", - "pnpm": "10.x" + "pnpm": "9.x" }, "dependencies": { "@chakra-ui/anatomy": "catalog:", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0d912751bc..605550c25a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,6 +45,12 @@ catalogs: '@types/node': specifier: ^20 version: 20.17.24 + '@types/react': + specifier: ^18 + version: 18.3.1 + '@types/react-dom': + specifier: ^18 + version: 18.3.0 axios: specifier: 1.13.6 version: 1.13.6 @@ -90,6 +96,12 @@ catalogs: proxy-agent: specifier: ^6 version: 6.5.0 + react: + specifier: ^18 + version: 18.3.1 + react-dom: + specifier: ^18 + version: 18.3.1 react-i18next: specifier: 14.1.2 version: 14.1.2 @@ -103,19 +115,13 @@ catalogs: specifier: ^4 version: 4.1.12 -overrides: - '@types/react': ^18 - '@types/react-dom': ^18 - react: ^18 - react-dom: ^18 - importers: .: devDependencies: '@chakra-ui/cli': specifier: ^2.4.1 - version: 2.5.8(react@18.3.1) + version: 2.5.8(encoding@0.1.13)(react@18.3.1) '@typescript-eslint/eslint-plugin': specifier: ^6.21.0 version: 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1)(typescript@5.8.2) @@ -241,8 +247,8 @@ importers: specifier: 'catalog:' version: 0.1.2 '@fastgpt-sdk/sandbox-adapter': - specifier: ^0.0.21 - version: 0.0.21 + specifier: ^0.0.22 + version: 0.0.22 '@fastgpt-sdk/storage': specifier: 'catalog:' version: 0.6.15(@opentelemetry/api@1.9.0)(@types/node@24.0.13)(jiti@2.6.0)(lightningcss@1.30.1)(proxy-agent@6.5.0)(sass@1.85.1)(terser@5.39.0)(tsx@4.20.6)(yaml@2.8.1) @@ -324,6 +330,9 @@ importers: jsonpath-plus: specifier: ^10.3.0 version: 10.3.0 + jsonrepair: + specifier: ^3.0.0 + version: 3.13.2 jsonwebtoken: specifier: ^9.0.2 version: 9.0.2 @@ -542,7 +551,7 @@ importers: specifier: ^5.4.1 version: 5.4.1 react: - specifier: ^18 + specifier: 'catalog:' version: 18.3.1 react-beautiful-dnd: specifier: ^13.1.1 @@ -551,7 +560,7 @@ importers: specifier: ^8.7.1 version: 8.10.1(date-fns@3.6.0)(react@18.3.1) react-dom: - specifier: ^18 + specifier: 'catalog:' version: 18.3.1(react@18.3.1) react-hook-form: specifier: 7.43.1 @@ -591,13 +600,13 @@ importers: specifier: ^5.3.7 version: 5.3.7 '@types/react': - specifier: ^18 + specifier: 'catalog:' version: 18.3.1 '@types/react-beautiful-dnd': specifier: ^13.1.1 version: 13.1.8 '@types/react-dom': - specifier: ^18 + specifier: 'catalog:' version: 18.3.0 projects/app: @@ -738,13 +747,13 @@ importers: specifier: ^1.5.4 version: 1.5.4 react: - specifier: ^18 + specifier: 'catalog:' version: 18.3.1 react-day-picker: specifier: ^8.7.1 version: 8.10.1(date-fns@3.6.0)(react@18.3.1) react-dom: - specifier: ^18 + specifier: 'catalog:' version: 18.3.1(react@18.3.1) react-hook-form: specifier: 7.43.1 @@ -826,10 +835,10 @@ importers: specifier: ^1.5.5 version: 1.5.5 '@types/react': - specifier: ^18 + specifier: 'catalog:' version: 18.3.1 '@types/react-dom': - specifier: ^18 + specifier: 'catalog:' version: 18.3.0 '@types/react-syntax-highlighter': specifier: ^15.5.6 @@ -910,10 +919,10 @@ importers: specifier: 'catalog:' version: 15.4.2(i18next@23.16.8)(next@16.1.6(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) react: - specifier: ^18 + specifier: 'catalog:' version: 18.3.1 react-dom: - specifier: ^18 + specifier: 'catalog:' version: 18.3.1(react@18.3.1) react-i18next: specifier: 'catalog:' @@ -929,10 +938,10 @@ importers: specifier: ^20 version: 20.17.24 '@types/react': - specifier: ^18 + specifier: 'catalog:' version: 18.3.1 '@types/react-dom': - specifier: ^18 + specifier: 'catalog:' version: 18.3.0 eslint: specifier: 'catalog:' @@ -1931,24 +1940,24 @@ packages: '@chakra-ui/color-mode@2.2.0': resolution: {integrity: sha512-niTEA8PALtMWRI9wJ4LL0CSBDo8NBfLNp4GD6/0hstcm3IlbBHTVKxN6HwSaoNYfphDQLxCjT4yG+0BJA5tFpg==} peerDependencies: - react: ^18 + react: '>=18' '@chakra-ui/hooks@2.4.4': resolution: {integrity: sha512-+gMwLIkabtddIL/GICU7JmnYtvfONP+fNiTfdYLV9/I1eyCz8igKgLmFJOGM6F+BpUev6hh+/+DX5ezGQ9VTbQ==} peerDependencies: - react: ^18 + react: '>=18' '@chakra-ui/icon@3.2.0': resolution: {integrity: sha512-xxjGLvlX2Ys4H0iHrI16t74rG9EBcpFvJ3Y3B7KMQTrnW34Kf7Da/UC8J67Gtx85mTHW020ml85SVPKORWNNKQ==} peerDependencies: '@chakra-ui/system': '>=2.0.0' - react: ^18 + react: '>=18' '@chakra-ui/icons@2.1.1': resolution: {integrity: sha512-3p30hdo4LlRZTT5CwoAJq3G9fHI0wDc0pBaMHj4SUn0yomO+RcDRlzhdXqdr5cVnzax44sqXJVnf3oQG0eI+4g==} peerDependencies: '@chakra-ui/system': '>=2.0.0' - react: ^18 + react: '>=18' '@chakra-ui/next-js@2.4.2': resolution: {integrity: sha512-loo82RyPbMyvJwRhhZVZovut9v2hFBSkqd1vQoNXgMrCRApLwrrttu5Iuodns15gLE3mqI+it5oEhxTtO5DrxA==} @@ -1956,7 +1965,7 @@ packages: '@chakra-ui/react': '>=2.4.0' '@emotion/react': '>=11' next: '>=13' - react: ^18 + react: '>=18' '@chakra-ui/object-utils@2.1.0': resolution: {integrity: sha512-tgIZOgLHaoti5PYGPTwK3t/cqtcycW0owaiOXoZOcpwwX/vlVb+H1jFsQyWiiwQVPt9RkoSLtxzXamx+aHH+bQ==} @@ -1964,12 +1973,12 @@ packages: '@chakra-ui/react-use-safe-layout-effect@2.1.0': resolution: {integrity: sha512-Knbrrx/bcPwVS1TorFdzrK/zWA8yuU/eaXDkNj24IrKoRlQrSBFarcgAEzlCHtzuhufP3OULPkELTzz91b0tCw==} peerDependencies: - react: ^18 + react: '>=18' '@chakra-ui/react-utils@2.0.12': resolution: {integrity: sha512-GbSfVb283+YA3kA8w8xWmzbjNWk14uhNpntnipHCftBibl0lxtQ9YqMFQLwuFOO0U2gYVocszqqDWX+XNKq9hw==} peerDependencies: - react: ^18 + react: '>=18' '@chakra-ui/react@2.10.7': resolution: {integrity: sha512-GX1dCmnvrxxyZEofDX9GMAtRakZJKnUqFM9k8qhaycPaeyfkiTNNTjhPNX917hgVx1yhC3kcJOs5IeC7yW56/g==} @@ -1977,8 +1986,8 @@ packages: '@emotion/react': '>=11' '@emotion/styled': '>=11' framer-motion: '>=4.0.0' - react: ^18 - react-dom: ^18 + react: '>=18' + react-dom: '>=18' '@chakra-ui/shared-utils@2.0.5': resolution: {integrity: sha512-4/Wur0FqDov7Y0nCXl7HbHzCg4aq86h+SXdoUeuCMD3dSj7dpsVnStLYhng1vxvlbUnLpdF4oz5Myt3i/a7N3Q==} @@ -1994,7 +2003,7 @@ packages: peerDependencies: '@emotion/react': ^11.0.0 '@emotion/styled': ^11.0.0 - react: ^18 + react: '>=18' '@chakra-ui/theme-tools@2.1.1': resolution: {integrity: sha512-n14L5L3ej3Zy+Xm/kDKO1G6/DkmieT7Li1C7NzMRcUj5C9YybQpyo7IZZ0BBUh3u+OVnKVhNC3d4P2NYDGRXmA==} @@ -2025,7 +2034,7 @@ packages: '@chakra-ui/utils@2.2.4': resolution: {integrity: sha512-nRpR9SnX7aLcJx7lKu8kgQWxdJso1oR/78HcBI+mzidvWdTykbTGdm5Q2R7S0PVH1IFBzBTgi6TiAjHvu96auA==} peerDependencies: - react: ^18 + react: '>=16.8.0' '@codemirror/autocomplete@6.19.0': resolution: {integrity: sha512-61Hfv3cF07XvUxNeC3E7jhG8XNi1Yom1G0lRC936oLnlF+jrbrv8rc/J98XlYzcsAoTVupfsf5fLej1aI8kyIg==} @@ -2145,7 +2154,7 @@ packages: resolution: {integrity: sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA==} peerDependencies: '@types/react': '*' - react: ^18 + react: '>=16.8.0' peerDependenciesMeta: '@types/react': optional: true @@ -2161,7 +2170,7 @@ packages: peerDependencies: '@emotion/react': ^11.0.0-rc.0 '@types/react': '*' - react: ^18 + react: '>=16.8.0' peerDependenciesMeta: '@types/react': optional: true @@ -2172,7 +2181,7 @@ packages: '@emotion/use-insertion-effect-with-fallbacks@1.2.0': resolution: {integrity: sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==} peerDependencies: - react: ^18 + react: '>=16.8.0' '@emotion/utils@1.4.2': resolution: {integrity: sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==} @@ -2652,8 +2661,8 @@ packages: '@fastgpt-sdk/plugin@0.3.8': resolution: {integrity: sha512-GjKrXMHxeF5UMkYGXawrUpzZjVRw3DICNYODeYwsUVOy+/ltu5zuwsqLkuuGQ7Arp/SBCmYRjG/MHmeNp4xxfw==} - '@fastgpt-sdk/sandbox-adapter@0.0.21': - resolution: {integrity: sha512-SM6e9w49CjYBdDYBzfPeWMnF3G0TM9AkmwUsFBniuaYh/OBMs/DWv/KYDo8xkRcBw9+eZuFokG+A5Vgt4/JZsg==} + '@fastgpt-sdk/sandbox-adapter@0.0.22': + resolution: {integrity: sha512-08SLX1F76Q178Gb0nFM5VQo/ms1Iwafx0MMSAb9Xoo7VTW/3apfP/qRxyvoCUZJsbETqYKz+qaEfLNNcDnUAHg==} engines: {node: '>=18'} '@fastgpt-sdk/storage@0.6.15': @@ -2767,105 +2776,89 @@ packages: resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-arm@1.2.4': resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-ppc64@1.2.4': resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} cpu: [ppc64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-riscv64@1.2.4': resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} cpu: [riscv64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-s390x@1.2.4': resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-x64@1.2.4': resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linuxmusl-arm64@1.2.4': resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] - libc: [musl] '@img/sharp-libvips-linuxmusl-x64@1.2.4': resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] - libc: [musl] '@img/sharp-linux-arm64@0.34.5': resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - libc: [glibc] '@img/sharp-linux-arm@0.34.5': resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] - libc: [glibc] '@img/sharp-linux-ppc64@0.34.5': resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ppc64] os: [linux] - libc: [glibc] '@img/sharp-linux-riscv64@0.34.5': resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [riscv64] os: [linux] - libc: [glibc] '@img/sharp-linux-s390x@0.34.5': resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] - libc: [glibc] '@img/sharp-linux-x64@0.34.5': resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - libc: [glibc] '@img/sharp-linuxmusl-arm64@0.34.5': resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - libc: [musl] '@img/sharp-linuxmusl-x64@0.34.5': resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - libc: [musl] '@img/sharp-wasm32@0.34.5': resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} @@ -3036,8 +3029,8 @@ packages: resolution: {integrity: sha512-Pto4wsVwrnY94tzcCXP2kWukQejSRPDfwOPd+EFh8dUzj+L7fa9Pze2wVgCRZpEohwfbcgAdEsvmSbhz+yGkog==} peerDependencies: lexical: 0.12.6 - react: ^18 - react-dom: ^18 + react: '>=17.x' + react-dom: '>=17.x' '@lexical/rich-text@0.12.6': resolution: {integrity: sha512-fRZHy2ug6Pq+pJK7trr9phTGaD2ba3If8o36dphOsl27MtUllpz68lcXL6mUonzJhAu4um1e9u7GFR3dLp+cVA==} @@ -3152,8 +3145,8 @@ packages: resolution: {integrity: sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==} peerDependencies: monaco-editor: '>= 0.25.0 < 1' - react: ^18 - react-dom: ^18 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 '@mongodb-js/saslprep@1.2.0': resolution: {integrity: sha512-+ywrb0AqkfaYuhHs6LxKWgqbh3I72EpEgESCw37o+9qPx9WTCkgDm2B+eMrwehGtHBWHFU4GXvnSCNiFhhausg==} @@ -3217,35 +3210,30 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [glibc] '@napi-rs/canvas-linux-arm64-musl@0.1.69': resolution: {integrity: sha512-a3xjNRIeK2m2ZORGv2moBvv3vbkaFZG1QKMeiEv/BKij+rkztuEhTJGMar+buICFgS0fLgphXXsKNkUSJb7eRQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [musl] '@napi-rs/canvas-linux-riscv64-gnu@0.1.69': resolution: {integrity: sha512-pClUoJF5wdC9AvD0mc15G9JffL1Q85nuH1rLSQPRkGmGmQOtRjw5E9xNbanz7oFUiPbjH7xcAXUjVAcf7tdgPQ==} engines: {node: '>= 10'} cpu: [riscv64] os: [linux] - libc: [glibc] '@napi-rs/canvas-linux-x64-gnu@0.1.69': resolution: {integrity: sha512-96X3bFAmzemfw84Ts6Jg/omL86uuynvK06MWGR/mp3JYNumY9RXofA14eF/kJIYelbYFWXcwpbcBR71lJ6G/YQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [glibc] '@napi-rs/canvas-linux-x64-musl@0.1.69': resolution: {integrity: sha512-2QTsEFO72Kwkj53W9hc5y1FAUvdGx0V+pjJB+9oQF6Ys9+y989GyPIl5wZDzeh8nIJW6koZZ1eFa8pD+pA5BFQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [musl] '@napi-rs/canvas-win32-x64-msvc@0.1.69': resolution: {integrity: sha512-Q4YA8kVnKarApBVLu7F8icGlIfSll5Glswo5hY6gPS4Is2dCI8+ig9OeDM8RlwYevUIxKq8lZBypN8Q1iLAQ7w==} @@ -3307,25 +3295,21 @@ packages: resolution: {integrity: sha512-af43QsBhRZ7kX/KjA9b5mrSLFMpRSuF7E2i9eBxrFkFpI2H4UCR0lSLcFmpkCnA/u2Q8a8KHhye88zDwUP8saQ==} cpu: [arm64] os: [linux] - libc: [glibc] '@next/rspack-binding-linux-arm64-musl@1.0.2': resolution: {integrity: sha512-ZuORUowizCrzoESLyUBGTzJjXyCixtdsTKp7EOLr2oLh2umzksm/e9Cm+iuy8qjkO+TFR1ny5L4YYCUOsYa+Kg==} cpu: [arm64] os: [linux] - libc: [musl] '@next/rspack-binding-linux-x64-gnu@1.0.2': resolution: {integrity: sha512-Bv0DS+jUwL5fQxcC3DiE6MbfssDF//kJ5rOnNey8EcLReqpz+WPuJKTqRvyUtvx77Sg3o7yADS3UsSTrHKn6iQ==} cpu: [x64] os: [linux] - libc: [glibc] '@next/rspack-binding-linux-x64-musl@1.0.2': resolution: {integrity: sha512-wjLXX7XaCvd+ISRZHVOEa9FvXDX8KV0HPyNJuapUO150ho7kXt0ZfY9vGm/0UCO7X4hCgnGzsnBZOzYdgT/RqQ==} cpu: [x64] os: [linux] - libc: [musl] '@next/rspack-binding-win32-arm64-msvc@1.0.2': resolution: {integrity: sha512-OQ0Uv9ZbMB8BZFJP2n7dLIc4kjfr76J1JPCtPNB3/BvE2xpAIEcYmkwSP0GrVL/H+1xLMqLwblqt7xyc65hfuw==} @@ -3365,28 +3349,24 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [glibc] '@next/swc-linux-arm64-musl@16.1.6': resolution: {integrity: sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [musl] '@next/swc-linux-x64-gnu@16.1.6': resolution: {integrity: sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [glibc] '@next/swc-linux-x64-musl@16.1.6': resolution: {integrity: sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [musl] '@next/swc-win32-arm64-msvc@16.1.6': resolution: {integrity: sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw==} @@ -3441,28 +3421,24 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [glibc] '@node-rs/jieba-linux-arm64-musl@2.0.1': resolution: {integrity: sha512-K4EDyNixSLVdTNYnHwD+7I/ytvzpo7tt+vdCLqwQViiek2PMpL/FFRvA39uU2tk99jXIxvkczdxARG20BRZppg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [musl] '@node-rs/jieba-linux-x64-gnu@2.0.1': resolution: {integrity: sha512-sq3J6L2ANTE25I9eVFq/nb57OtXcvUIeUD1CTKJxwgTKIVmcB2LyOZpWf20AjHRUfbMER9Klqg5dgyyO+Six+w==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [glibc] '@node-rs/jieba-linux-x64-musl@2.0.1': resolution: {integrity: sha512-0zfP9Qy68yEXrhBFknfhF6WUJDPU/8eRuyIrkMGdMjfRpxhpSbr2fMfnsqhOQLvhuK4w3iDFvTy4t5d0s6JKMA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [musl] '@node-rs/jieba-wasm32-wasi@2.0.1': resolution: {integrity: sha512-7I5rJya5rlQNJIhv8PvPzIVT1/gVc0vFzHmlfRGwCPGDJ3tHVxkSPW34dDx3OgDmbIeadNpmgIyC1RaS9djPJg==} @@ -3656,25 +3632,21 @@ packages: resolution: {integrity: sha512-l/0pWoQM5kVmJLg4frQ1mKZOXgi0ex/hzvFt8E4WK2ifXr5JgKFUokxsb/oat7f5YzdJJh5r9p+qS/t3dA26Aw==} cpu: [arm64] os: [linux] - libc: [glibc] '@oxc-resolver/binding-linux-arm64-musl@5.0.0': resolution: {integrity: sha512-bx0oz/oaAW4FGYqpIIxJCnmgb906YfMhTEWCJvYkxjpEI8VKLJEL3PQevYiqDq36SA0yRLJ/sQK2fqry8AFBfA==} cpu: [arm64] os: [linux] - libc: [musl] '@oxc-resolver/binding-linux-x64-gnu@5.0.0': resolution: {integrity: sha512-4PH++qbSIhlRsFYdN1P9neDov4OGhTGo5nbQ1D7AL6gWFLo3gdZTc00FM2y8JjeTcPWEXkViZuwpuc0w5i6qHg==} cpu: [x64] os: [linux] - libc: [glibc] '@oxc-resolver/binding-linux-x64-musl@5.0.0': resolution: {integrity: sha512-mLfQFpX3/5y9oWi0b+9FbWDkL2hM0Y29653beCHiHxAdGyVgb2DsJbK74WkMTwtSz9by8vyBh8jGPZcg1yLZbQ==} cpu: [x64] os: [linux] - libc: [musl] '@oxc-resolver/binding-wasm32-wasi@5.0.0': resolution: {integrity: sha512-uEhsAZSo65qsRi6+IfBTEUUFbjg7T2yruJeLYpFfEATpm3ory5Mgo5vx3L0c2/Cz1OUZXBgp3A8x6VMUB2jT2A==} @@ -3720,42 +3692,36 @@ packages: engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] - libc: [glibc] '@parcel/watcher-linux-arm-musl@2.5.1': resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] - libc: [musl] '@parcel/watcher-linux-arm64-glibc@2.5.1': resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] - libc: [glibc] '@parcel/watcher-linux-arm64-musl@2.5.1': resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] - libc: [musl] '@parcel/watcher-linux-x64-glibc@2.5.1': resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] - libc: [glibc] '@parcel/watcher-linux-x64-musl@2.5.1': resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] - libc: [musl] '@parcel/watcher-win32-arm64@2.5.1': resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} @@ -3843,38 +3809,38 @@ packages: '@reactflow/background@11.3.14': resolution: {integrity: sha512-Gewd7blEVT5Lh6jqrvOgd4G6Qk17eGKQfsDXgyRSqM+CTwDqRldG2LsWN4sNeno6sbqVIC2fZ+rAUBFA9ZEUDA==} peerDependencies: - react: ^18 - react-dom: ^18 + react: '>=17' + react-dom: '>=17' '@reactflow/controls@11.2.14': resolution: {integrity: sha512-MiJp5VldFD7FrqaBNIrQ85dxChrG6ivuZ+dcFhPQUwOK3HfYgX2RHdBua+gx+40p5Vw5It3dVNp/my4Z3jF0dw==} peerDependencies: - react: ^18 - react-dom: ^18 + react: '>=17' + react-dom: '>=17' '@reactflow/core@11.11.4': resolution: {integrity: sha512-H4vODklsjAq3AMq6Np4LE12i1I4Ta9PrDHuBR9GmL8uzTt2l2jh4CiQbEMpvMDcp7xi4be0hgXj+Ysodde/i7Q==} peerDependencies: - react: ^18 - react-dom: ^18 + react: '>=17' + react-dom: '>=17' '@reactflow/minimap@11.7.14': resolution: {integrity: sha512-mpwLKKrEAofgFJdkhwR5UQ1JYWlcAAL/ZU/bctBkuNTT1yqV+y0buoNVImsRehVYhJwffSWeSHaBR5/GJjlCSQ==} peerDependencies: - react: ^18 - react-dom: ^18 + react: '>=17' + react-dom: '>=17' '@reactflow/node-resizer@2.2.14': resolution: {integrity: sha512-fwqnks83jUlYr6OHcdFEedumWKChTHRGw/kbCxj0oqBd+ekfs+SIp4ddyNU0pdx96JIm5iNFS0oNrmEiJbbSaA==} peerDependencies: - react: ^18 - react-dom: ^18 + react: '>=17' + react-dom: '>=17' '@reactflow/node-toolbar@1.3.14': resolution: {integrity: sha512-rbynXQnH/xFNu4P9H+hVqlEUafDCkEoCy0Dg9mG22Sg+rY/0ck6KkrAQrYrTgXusd+cEJOMK0uOOFCK2/5rSGQ==} peerDependencies: - react: ^18 - react-dom: ^18 + react: '>=17' + react-dom: '>=17' '@replit/codemirror-css-color-picker@6.3.0': resolution: {integrity: sha512-19biDANghUm7Fz7L1SNMIhK48tagaWuCOHj4oPPxc7hxPGkTVY2lU/jVZ8tsbTKQPVG7BO2CBDzs7CBwb20t4A==} @@ -3918,42 +3884,36 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - libc: [glibc] '@rolldown/binding-linux-arm64-musl@1.0.0-rc.7': resolution: {integrity: sha512-G43ZElEvaby+YSOgrXfBgpeQv42LdS0ivFFYQufk2tBDWeBfzE/+ob5DmO8Izbyn4Y8k6GgLF11jFDYNnmU/3w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - libc: [musl] '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.7': resolution: {integrity: sha512-Y48ShVxGE2zUTt0A0PR3grCLNxW4DWtAfe5lxf6L3uYEQujwo/LGuRogMsAtOJeYLCPTJo2i714LOdnK34cHpw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] - libc: [glibc] '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.7': resolution: {integrity: sha512-KU5DUYvX3qI8/TX6D3RA4awXi4Ge/1+M6Jqv7kRiUndpqoVGgD765xhV3Q6QvtABnYjLJenrWDl3S1B5U56ixA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] - libc: [glibc] '@rolldown/binding-linux-x64-gnu@1.0.0-rc.7': resolution: {integrity: sha512-1THb6FdBkAEL12zvUue2bmK4W1+P+tz8Pgu5uEzq+xrtYa3iBzmmKNlyfUzCFNCqsPd8WJEQrYdLcw4iMW4AVw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - libc: [glibc] '@rolldown/binding-linux-x64-musl@1.0.0-rc.7': resolution: {integrity: sha512-12o73atFNWDgYnLyA52QEUn9AH8pHIe12W28cmqjyHt4bIEYRzMICvYVCPa2IQm6DJBvCBrEhD9K+ct4wr2hwg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - libc: [musl] '@rolldown/binding-openharmony-arm64@1.0.0-rc.7': resolution: {integrity: sha512-+uUgGwvuUCXl894MTsmTS2J0BnCZccFsmzV7y1jFxW5pTSxkuwL5agyPuDvDOztPeS6RrdqWkn7sT0jRd0ECkg==} @@ -4015,61 +3975,51 @@ packages: resolution: {integrity: sha512-mrA0v3QMy6ZSvEuLs0dMxcO2LnaCONs1Z73GUDBHWbY8tFFocM6yl7YyMu7rz4zS81NDSqhrUuolyZXGi8TEqg==} cpu: [arm] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.35.0': resolution: {integrity: sha512-DnYhhzcvTAKNexIql8pFajr0PiDGrIsBYPRvCKlA5ixSS3uwo/CWNZxB09jhIapEIg945KOzcYEAGGSmTSpk7A==} cpu: [arm] os: [linux] - libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.35.0': resolution: {integrity: sha512-uagpnH2M2g2b5iLsCTZ35CL1FgyuzzJQ8L9VtlJ+FckBXroTwNOaD0z0/UF+k5K3aNQjbm8LIVpxykUOQt1m/A==} cpu: [arm64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.35.0': resolution: {integrity: sha512-XQxVOCd6VJeHQA/7YcqyV0/88N6ysSVzRjJ9I9UA/xXpEsjvAgDTgH3wQYz5bmr7SPtVK2TsP2fQ2N9L4ukoUg==} cpu: [arm64] os: [linux] - libc: [musl] '@rollup/rollup-linux-loongarch64-gnu@4.35.0': resolution: {integrity: sha512-5pMT5PzfgwcXEwOaSrqVsz/LvjDZt+vQ8RT/70yhPU06PTuq8WaHhfT1LW+cdD7mW6i/J5/XIkX/1tCAkh1W6g==} cpu: [loong64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-powerpc64le-gnu@4.35.0': resolution: {integrity: sha512-c+zkcvbhbXF98f4CtEIP1EBA/lCic5xB0lToneZYvMeKu5Kamq3O8gqrxiYYLzlZH6E3Aq+TSW86E4ay8iD8EA==} cpu: [ppc64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.35.0': resolution: {integrity: sha512-s91fuAHdOwH/Tad2tzTtPX7UZyytHIRR6V4+2IGlV0Cej5rkG0R61SX4l4y9sh0JBibMiploZx3oHKPnQBKe4g==} cpu: [riscv64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-s390x-gnu@4.35.0': resolution: {integrity: sha512-hQRkPQPLYJZYGP+Hj4fR9dDBMIM7zrzJDWFEMPdTnTy95Ljnv0/4w/ixFw3pTBMEuuEuoqtBINYND4M7ujcuQw==} cpu: [s390x] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.35.0': resolution: {integrity: sha512-Pim1T8rXOri+0HmV4CdKSGrqcBWX0d1HoPnQ0uw0bdp1aP5SdQVNBy8LjYncvnLgu3fnnCt17xjWGd4cqh8/hA==} cpu: [x64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-musl@4.35.0': resolution: {integrity: sha512-QysqXzYiDvQWfUiTm8XmJNO2zm9yC9P/2Gkrwg2dH9cxotQzunBHYr6jk4SujCTqnfGxduOmQcI7c2ryuW8XVg==} cpu: [x64] os: [linux] - libc: [musl] '@rollup/rollup-win32-arm64-msvc@4.35.0': resolution: {integrity: sha512-OUOlGqPkVJCdJETKOCEf1mw848ZyJ5w50/rZ/3IBQVdLfR5jk/6Sr5m3iO2tdPgwo0x7VcncYuOvMhBWZq8ayg==} @@ -4100,25 +4050,21 @@ packages: resolution: {integrity: sha512-211/XoBiooGGgUo/NxNpsrzGUXtH1d7g/4+UTtjYtfc8QHwu7ZMHcsqg0wss53fXzn/yyxd0DZ56vBHq52BiFw==} cpu: [arm64] os: [linux] - libc: [glibc] '@rspack/binding-linux-arm64-musl@1.6.7': resolution: {integrity: sha512-0WnqAWz3WPDsXGvOOA++or7cHpoidVsH3FlqNaAfRu6ni6n7ig/s0/jKUB+C5FtXOgmGjAGkZHfFgNHsvZ0FWw==} cpu: [arm64] os: [linux] - libc: [musl] '@rspack/binding-linux-x64-gnu@1.6.7': resolution: {integrity: sha512-iMrE0Q4IuYpkE0MjpaOVaUDYbQFiCRI9D3EPoXzlXJj4kJSdNheODpHTBVRlWt8Xp7UAoWuIFXCvKFKcSMm3aQ==} cpu: [x64] os: [linux] - libc: [glibc] '@rspack/binding-linux-x64-musl@1.6.7': resolution: {integrity: sha512-e7gKFxpdEQwYGk7lTC/hukTgNtaoAstBXehnZNk4k3kuU6+86WDrkn18Cd949iNqfIPtIG/wIsFNGbkHsH69hQ==} cpu: [x64] os: [linux] - libc: [musl] '@rspack/binding-wasm32-wasi@1.6.7': resolution: {integrity: sha512-yx88EFdE9RP3hh7VhjjW6uc6wGU0KcpOcZp8T8E/a+X8L98fX0aVrtM1IDbndhmdluIMqGbfJNap2+QqOCY9Mw==} @@ -4172,7 +4118,7 @@ packages: resolution: {integrity: sha512-krkNq5cZLD4IEtVto5KbektfRhVTR/ZwiGq+KGOhnXlv/0kFKdkccw6WXPnzq+gXZTYNJxFcrDn60Dj3ilXQrg==} engines: {node: '>=20'} peerDependencies: - react: ^18 + react: ^18.0.0 || ^19.0.0 '@scalar/api-reference@1.38.1': resolution: {integrity: sha512-1r0o1BBhfOpOI2ZTvcDffNkljL4gpzyB1prBZ1wRZ3fY1lkIdAUHzIDj+lCYL+uj0RYVJFOIWvJpmpCBW6KmIg==} @@ -4598,8 +4544,8 @@ packages: '@tanstack/react-query@4.36.1': resolution: {integrity: sha512-y7ySVHFyyQblPl3J3eQBWpXZkliroki3ARnBKsdJchlgt7yJLRDUcf4B8soufgiYt3pEQIkBWBx1N9/ZPIeUWw==} peerDependencies: - react: ^18 - react-dom: ^18 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 react-native: '*' peerDependenciesMeta: react-dom: @@ -5298,8 +5244,8 @@ packages: resolution: {integrity: sha512-TrjXie49Q8HuHKTa84Fm9A+famMDAG1+7a9S9Gq6RQ0h90Jgqmiq3CkObuRjWT/C4d6nRZCw35Y2k2fmybb5eA==} engines: {node: '>=18'} peerDependencies: - react: ^18 - react-dom: ^18 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 ajv-draft-04@1.0.0: resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} @@ -7118,8 +7064,8 @@ packages: framer-motion@9.1.7: resolution: {integrity: sha512-nKxBkIO4IPkMEqcBbbATxsVjwPYShKl051yhBv9628iAH6JLeHD0siBHxkL62oQzMC1+GNX73XtPjgP753ufuw==} peerDependencies: - react: ^18 - react-dom: ^18 + react: ^18.0.0 + react-dom: ^18.0.0 framesync@6.1.2: resolution: {integrity: sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==} @@ -8031,6 +7977,10 @@ packages: resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} engines: {node: '>=0.10.0'} + jsonrepair@3.13.2: + resolution: {integrity: sha512-Leuly0nbM4R+S5SVJk3VHfw1oxnlEK9KygdZvfUtEtTawNDyzB4qa1xWTmFt1aeoA7sXZkVTRuIixJ8bAvqVUg==} + hasBin: true + jsonwebtoken@9.0.2: resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} engines: {node: '>=12', npm: '>=6'} @@ -8090,7 +8040,7 @@ packages: resolution: {integrity: sha512-99nmbZ2DVwqYiqSVgH8lF9UoM7glIYSdG3WzE3Z2nZbJKoFyZPvRRk7Czq05b32nqcAALwzk3zqVOZmDIn1rqQ==} engines: {node: '>=18'} peerDependencies: - react: ^18 + react: ^18 || ^19 peerDependenciesMeta: react: optional: true @@ -8165,28 +8115,24 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - libc: [glibc] lightningcss-linux-arm64-musl@1.30.1: resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - libc: [musl] lightningcss-linux-x64-gnu@1.30.1: resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - libc: [glibc] lightningcss-linux-x64-musl@1.30.1: resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - libc: [musl] lightningcss-win32-arm64-msvc@1.30.1: resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==} @@ -8884,7 +8830,7 @@ packages: peerDependencies: i18next: '>= 23.7.13' next: '>= 12.0.0' - react: ^18 + react: '>= 17.0.2' react-i18next: '>= 13.5.0' next-rspack@16.1.6: @@ -8898,8 +8844,8 @@ packages: '@opentelemetry/api': ^1.1.0 '@playwright/test': ^1.51.1 babel-plugin-react-compiler: '*' - react: ^18 - react-dom: ^18 + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 sass: ^1.3.0 peerDependenciesMeta: '@opentelemetry/api': @@ -9637,30 +9583,30 @@ packages: resolution: {integrity: sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==} deprecated: 'react-beautiful-dnd is now deprecated. Context and options: https://github.com/atlassian/react-beautiful-dnd/issues/2672' peerDependencies: - react: ^18 - react-dom: ^18 + react: ^16.8.5 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.5 || ^17.0.0 || ^18.0.0 react-clientside-effect@1.2.7: resolution: {integrity: sha512-gce9m0Pk/xYYMEojRI9bgvqQAkl6hm7ozQvqWPyQx+kULiatdHgkNM1QG4DQRx5N9BAzWSCJmt9mMV8/KsdgVg==} peerDependencies: - react: ^18 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc react-day-picker@8.10.1: resolution: {integrity: sha512-TMx7fNbhLk15eqcMt+7Z7S2KF7mfTId/XJDjKE8f+IUcFn0l08/kI4FiYTL/0yuOLmEcbR4Fwe3GJf/NiiMnPA==} peerDependencies: date-fns: ^2.28.0 || ^3.0.0 - react: ^18 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom@18.3.1: resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} peerDependencies: - react: ^18 + react: ^18.3.1 react-error-boundary@3.1.4: resolution: {integrity: sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==} engines: {node: '>=10', npm: '>=6'} peerDependencies: - react: ^18 + react: '>=16.13.1' react-fast-compare@3.2.2: resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} @@ -9668,8 +9614,8 @@ packages: react-focus-lock@2.13.6: resolution: {integrity: sha512-ehylFFWyYtBKXjAO9+3v8d0i+cnc1trGS0vlTGhzFW1vbFXVUTmR8s2tt/ZQG8x5hElg6rhENlLG1H3EZK0Llg==} peerDependencies: - '@types/react': ^18 - react: ^18 + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true @@ -9678,13 +9624,13 @@ packages: resolution: {integrity: sha512-+s3+s8LLytRMriwwuSqeLStVjRXFGxgjjx2jED7Z+wz1J/88vpxieRQGvJVvzrzVxshZ0BRuocFERb779m2kNg==} engines: {node: '>=12.22.0'} peerDependencies: - react: ^18 + react: ^16.8.0 || ^17 || ^18 react-i18next@14.1.2: resolution: {integrity: sha512-FSIcJy6oauJbGEXfhUgVeLzvWBhIBIS+/9c6Lj4niwKZyGaGb4V4vUbATXSlsHJDXXB+ociNxqFNiFuV1gmoqg==} peerDependencies: i18next: '>= 23.2.3' - react: ^18 + react: '>= 16.8.0' react-dom: '*' react-native: '*' peerDependenciesMeta: @@ -9705,19 +9651,19 @@ packages: react-markdown@9.1.0: resolution: {integrity: sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw==} peerDependencies: - '@types/react': ^18 - react: ^18 + '@types/react': '>=18' + react: '>=18' react-photo-view@1.2.7: resolution: {integrity: sha512-MfOWVPxuibncRLaycZUNxqYU8D9IA+rbGDDaq6GM8RIoGJal592hEJoRAyRSI7ZxyyJNJTLMUWWL3UIXHJJOpw==} peerDependencies: - react: ^18 - react-dom: ^18 + react: '>=16.8.0' + react-dom: '>=16.8.0' react-redux@7.2.9: resolution: {integrity: sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==} peerDependencies: - react: ^18 + react: ^16.8.3 || ^17 || ^18 react-dom: '*' react-native: '*' peerDependenciesMeta: @@ -9730,8 +9676,8 @@ packages: resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} engines: {node: '>=10'} peerDependencies: - '@types/react': ^18 - react: ^18 + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: '@types/react': optional: true @@ -9740,8 +9686,8 @@ packages: resolution: {integrity: sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==} engines: {node: '>=10'} peerDependencies: - '@types/react': ^18 - react: ^18 + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true @@ -9749,15 +9695,15 @@ packages: react-smooth@4.0.4: resolution: {integrity: sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==} peerDependencies: - react: ^18 - react-dom: ^18 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-style-singleton@2.2.3: resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} engines: {node: '>=10'} peerDependencies: - '@types/react': ^18 - react: ^18 + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true @@ -9765,19 +9711,19 @@ packages: react-syntax-highlighter@15.6.1: resolution: {integrity: sha512-OqJ2/vL7lEeV5zTJyG7kmARppUjiB9h9udl4qHQjjgEos66z00Ia0OckwYfRxCSFrW8RJIBnsBwQsHZbVPspqg==} peerDependencies: - react: ^18 + react: '>= 0.14.0' react-textarea-autosize@8.5.8: resolution: {integrity: sha512-iUiIj70JefrTuSJ4LbVFiSqWiHHss5L63L717bqaWHMgkm9sz6eEvro4vZ3uQfGJbevzwT6rHOszHKA8RkhRMg==} engines: {node: '>=10'} peerDependencies: - react: ^18 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-transition-group@4.4.5: resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} peerDependencies: - react: ^18 - react-dom: ^18 + react: '>=16.6.0' + react-dom: '>=16.6.0' react@18.3.1: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} @@ -9786,8 +9732,8 @@ packages: reactflow@11.11.4: resolution: {integrity: sha512-70FOtJkUWH3BAOsN+LU9lCrKoKbtOPnz2uq0CV2PLdNSwxTXOhCbsZr50GmZ+Rtw3jx8Uv7/vBFtCGixLfd4Og==} peerDependencies: - react: ^18 - react-dom: ^18 + react: '>=17' + react-dom: '>=17' read-cache@1.0.0: resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} @@ -9828,8 +9774,8 @@ packages: resolution: {integrity: sha512-v8PUTUlyiDe56qUj82w/EDVuzEFXwEHp9/xOowGAZwfLjB9uAy3GllQVIYMWF6nU+qibx85WF75zD7AjqoT54Q==} engines: {node: '>=14'} peerDependencies: - react: ^18 - react-dom: ^18 + react: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 redis-errors@1.2.0: resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} @@ -10479,7 +10425,7 @@ packages: peerDependencies: '@babel/core': '*' babel-plugin-macros: '*' - react: ^18 + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' peerDependenciesMeta: '@babel/core': optional: true @@ -10989,8 +10935,8 @@ packages: resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} engines: {node: '>=10'} peerDependencies: - '@types/react': ^18 - react: ^18 + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true @@ -10999,7 +10945,7 @@ packages: resolution: {integrity: sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==} peerDependencies: '@types/react': '*' - react: ^18 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: '@types/react': optional: true @@ -11007,8 +10953,8 @@ packages: use-context-selector@1.4.4: resolution: {integrity: sha512-pS790zwGxxe59GoBha3QYOwk8AFGp4DN6DOtH+eoqVmgBBRXVx4IlPDhJmmMiNQAgUaLlP+58aqRC3A4rdaSjg==} peerDependencies: - react: ^18 - react-dom: ^18 + react: '>=16.8.0' + react-dom: '*' react-native: '*' scheduler: '>=0.19.0' peerDependenciesMeta: @@ -11021,7 +10967,7 @@ packages: resolution: {integrity: sha512-q6ayo8DWoPZT0VdG4u3D3uxcgONP3Mevx2i2b0434cwWBoL+aelL1DzkXI6w3PhTZzUeR2kaVlZn70iCiseP6w==} peerDependencies: '@types/react': '*' - react: ^18 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: '@types/react': optional: true @@ -11030,7 +10976,7 @@ packages: resolution: {integrity: sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==} peerDependencies: '@types/react': '*' - react: ^18 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: '@types/react': optional: true @@ -11038,14 +10984,14 @@ packages: use-memo-one@1.1.3: resolution: {integrity: sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==} peerDependencies: - react: ^18 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 use-sidecar@1.1.3: resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} engines: {node: '>=10'} peerDependencies: - '@types/react': ^18 - react: ^18 + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true @@ -11053,7 +10999,7 @@ packages: use-sync-external-store@1.4.0: resolution: {integrity: sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==} peerDependencies: - react: ^18 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -11454,7 +11400,7 @@ packages: engines: {node: '>=12'} xlsx@https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz: - resolution: {integrity: sha512-+nKZ39+nvK7Qq6i0PvWWRA4j/EkfWOtkP/YhMtupm+lJIiHxUrgTr1CcKv1nBk1rHtkRRQ3O2+Ih/q/sA+FXZA==, tarball: https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz} + resolution: {tarball: https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz} version: 0.20.2 engines: {node: '>=0.8'} hasBin: true @@ -11599,9 +11545,9 @@ packages: resolution: {integrity: sha512-ibr/n1hBzLLj5Y+yUcU7dYw8p6WnIVzdJbnX+1YpaScvZVF2ziugqHs+LAmHw4lWO9c/zRj+K1ncgWDQuthEdQ==} engines: {node: '>=12.7.0'} peerDependencies: - '@types/react': ^18 + '@types/react': '>=16.8' immer: '>=9.0.6' - react: ^18 + react: '>=16.8' peerDependenciesMeta: '@types/react': optional: true @@ -12922,11 +12868,11 @@ snapshots: '@chakra-ui/anatomy@2.3.6': {} - '@chakra-ui/cli@2.5.8(react@18.3.1)': + '@chakra-ui/cli@2.5.8(encoding@0.1.13)(react@18.3.1)': dependencies: bundle-n-require: 1.1.2 chokidar: 3.6.0 - cli-welcome: 2.2.3(react@18.3.1) + cli-welcome: 2.2.3(encoding@0.1.13)(react@18.3.1) commander: 11.1.0 ora: 7.0.1 prettier: 3.2.4 @@ -13597,7 +13543,7 @@ snapshots: '@fortaine/fetch-event-source': 3.0.6 zod: 4.1.12 - '@fastgpt-sdk/sandbox-adapter@0.0.21': + '@fastgpt-sdk/sandbox-adapter@0.0.22': dependencies: '@alibaba-group/opensandbox': 0.1.4 '@e2b/code-interpreter': 2.3.3 @@ -17343,9 +17289,9 @@ snapshots: claygl@1.3.0: {} - clear-any-console@1.16.3(react@18.3.1): + clear-any-console@1.16.3(encoding@0.1.13)(react@18.3.1): dependencies: - langbase: 1.1.44(react@18.3.1) + langbase: 1.1.44(encoding@0.1.13)(react@18.3.1) transitivePeerDependencies: - encoding - react @@ -17364,10 +17310,10 @@ snapshots: slice-ansi: 5.0.0 string-width: 5.1.2 - cli-welcome@2.2.3(react@18.3.1): + cli-welcome@2.2.3(encoding@0.1.13)(react@18.3.1): dependencies: chalk: 2.4.2 - clear-any-console: 1.16.3(react@18.3.1) + clear-any-console: 1.16.3(encoding@0.1.13)(react@18.3.1) transitivePeerDependencies: - encoding - react @@ -18393,8 +18339,8 @@ snapshots: '@typescript-eslint/parser': 6.21.0(eslint@8.57.1)(typescript@5.8.2) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.9.0(eslint-plugin-import@2.32.0)(eslint@8.57.1) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-typescript@3.9.0)(eslint@8.57.1) + eslint-import-resolver-typescript: 3.9.0(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1))(eslint@8.57.1) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-typescript@3.9.0(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1) eslint-plugin-react: 7.37.4(eslint@8.57.1) eslint-plugin-react-hooks: 5.2.0(eslint@8.57.1) @@ -18413,7 +18359,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.9.0(eslint-plugin-import@2.32.0)(eslint@8.57.1): + eslint-import-resolver-typescript@3.9.0(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1))(eslint@8.57.1): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 @@ -18424,22 +18370,22 @@ snapshots: stable-hash: 0.0.5 tinyglobby: 0.2.15 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-typescript@3.9.0)(eslint@8.57.1) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-typescript@3.9.0(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.9.0)(eslint@8.57.1): + eslint-module-utils@2.12.1(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.9.0(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 6.21.0(eslint@8.57.1)(typescript@5.8.2) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.9.0(eslint-plugin-import@2.32.0)(eslint@8.57.1) + eslint-import-resolver-typescript: 3.9.0(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1))(eslint@8.57.1) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-typescript@3.9.0)(eslint@8.57.1): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-typescript@3.9.0(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -18450,7 +18396,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.9.0)(eslint@8.57.1) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.9.0(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -19907,6 +19853,8 @@ snapshots: jsonpointer@5.0.1: {} + jsonrepair@3.13.2: {} + jsonwebtoken@9.0.2: dependencies: jws: 3.2.2 @@ -19978,10 +19926,10 @@ snapshots: kuler@2.0.0: {} - langbase@1.1.44(react@18.3.1): + langbase@1.1.44(encoding@0.1.13)(react@18.3.1): dependencies: dotenv: 16.5.0 - openai: 4.104.0(zod@3.25.76) + openai: 4.104.0(encoding@0.1.13)(zod@3.25.76) zod: 3.25.76 zod-validation-error: 3.4.0(zod@3.25.76) optionalDependencies: @@ -21270,6 +21218,20 @@ snapshots: dependencies: mimic-fn: 4.0.0 + openai@4.104.0(encoding@0.1.13)(zod@3.25.76): + dependencies: + '@types/node': 18.19.80 + '@types/node-fetch': 2.6.12 + abort-controller: 3.0.0 + agentkeepalive: 4.6.0 + form-data-encoder: 1.7.2 + formdata-node: 4.4.1 + node-fetch: 2.7.0(encoding@0.1.13) + optionalDependencies: + zod: 3.25.76 + transitivePeerDependencies: + - encoding + openai@4.104.0(encoding@0.1.13)(zod@4.1.12): dependencies: '@types/node': 18.19.80 @@ -21284,20 +21246,6 @@ snapshots: transitivePeerDependencies: - encoding - openai@4.104.0(zod@3.25.76): - dependencies: - '@types/node': 18.19.80 - '@types/node-fetch': 2.6.12 - abort-controller: 3.0.0 - agentkeepalive: 4.6.0 - form-data-encoder: 1.7.2 - formdata-node: 4.4.1 - node-fetch: 2.7.0(encoding@0.1.13) - optionalDependencies: - zod: 3.25.76 - transitivePeerDependencies: - - encoding - openapi-fetch@0.14.1: dependencies: openapi-typescript-helpers: 0.0.15 diff --git a/projects/app/Dockerfile b/projects/app/Dockerfile index 43b0d58e90..bb8c4ccd45 100644 --- a/projects/app/Dockerfile +++ b/projects/app/Dockerfile @@ -5,7 +5,7 @@ WORKDIR /app ARG proxy RUN [ -z "$proxy" ] || sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories -RUN apk add --no-cache libc6-compat && npm install -g pnpm@10 +RUN apk add --no-cache libc6-compat && npm install -g pnpm@9 # copy packages and one project COPY pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./ @@ -39,7 +39,7 @@ COPY --from=maindeps /app/projects/app/node_modules ./projects/app/node_modules RUN [ -z "$proxy" ] || sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories -RUN apk add --no-cache libc6-compat && npm install -g pnpm@10 +RUN apk add --no-cache libc6-compat && npm install -g pnpm@9 ENV NODE_OPTIONS="--max-old-space-size=4096" ENV NEXT_PUBLIC_BASE_URL=$base_url diff --git a/projects/app/package.json b/projects/app/package.json index 0ec1ade451..423b7e2ccc 100644 --- a/projects/app/package.json +++ b/projects/app/package.json @@ -12,7 +12,7 @@ }, "engines": { "node": ">=20", - "pnpm": "10.x" + "pnpm": "9.x" }, "dependencies": { "@chakra-ui/anatomy": "catalog:", diff --git a/projects/app/src/components/common/NextHead/index.tsx b/projects/app/src/components/common/NextHead/index.tsx index a5cbf11f9a..25aa8c9825 100644 --- a/projects/app/src/components/common/NextHead/index.tsx +++ b/projects/app/src/components/common/NextHead/index.tsx @@ -1,14 +1,13 @@ -import { LOGO_ICON } from '@fastgpt/global/common/system/constants'; import Head from 'next/head'; import React, { useMemo } from 'react'; const NextHead = ({ title, icon, desc }: { title?: string; icon?: string; desc?: string }) => { const formatIcon = useMemo(() => { - if (!icon) return LOGO_ICON; + if (!icon) return '/favicon.ico'; if (icon.startsWith('http') || icon.startsWith('/')) { return icon; } - return LOGO_ICON; + return '/favicon.ico'; }, [icon]); return ( diff --git a/projects/app/src/components/core/chat/components/AIResponseBox.tsx b/projects/app/src/components/core/chat/components/AIResponseBox.tsx index 399aabb056..224bae0f90 100644 --- a/projects/app/src/components/core/chat/components/AIResponseBox.tsx +++ b/projects/app/src/components/core/chat/components/AIResponseBox.tsx @@ -60,11 +60,13 @@ const accordionButtonStyle = { const RenderResoningContent = React.memo(function RenderResoningContent({ content, isChatting, - isLastResponseValue + isLastResponseValue, + isDisabled }: { content: string; isChatting: boolean; isLastResponseValue: boolean; + isDisabled?: boolean; }) { const { t } = useTranslation(); const showAnimation = isChatting && isLastResponseValue; @@ -90,7 +92,7 @@ const RenderResoningContent = React.memo(function RenderResoningContent({ borderColor={'myGray.300'} color={'myGray.500'} > - + @@ -100,12 +102,14 @@ const RenderText = React.memo(function RenderText({ showAnimation, text, chatItemDataId, - onOpenCiteModal + onOpenCiteModal, + isDisabled }: { showAnimation: boolean; text: string; chatItemDataId: string; onOpenCiteModal?: (e?: OnOpenCiteModalProps) => void; + isDisabled?: boolean; }) { const appId = useContextSelector(WorkflowRuntimeContext, (v) => v.appId); const chatId = useContextSelector(WorkflowRuntimeContext, (v) => v.chatId); @@ -131,6 +135,7 @@ const RenderText = React.memo(function RenderText({ showAnimation={showAnimation} chatAuthData={chatAuthData} onOpenCiteModal={onOpenCiteModal} + isDisabled={isDisabled} /> ); }); @@ -424,6 +429,7 @@ const AIResponseBox = ({ }) => { const showRunningStatus = useContextSelector(ChatItemContext, (v) => v.showRunningStatus); const tools = value.tool ? [value.tool] : value.tools; + const disableStreamingInteraction = isChatting && isLastChild; if ('text' in value && value.text) { return ( @@ -432,6 +438,7 @@ const AIResponseBox = ({ showAnimation={isChatting && isLastResponseValue} text={value.text.content} onOpenCiteModal={onOpenCiteModal} + isDisabled={disableStreamingInteraction} /> ); } @@ -441,6 +448,7 @@ const AIResponseBox = ({ isChatting={isChatting} isLastResponseValue={isLastResponseValue} content={value.reasoning.content} + isDisabled={disableStreamingInteraction} /> ); } 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 d39ebd74a8..b0034e53d0 100644 --- a/projects/app/src/pageComponents/app/detail/Edit/ChatAgent/utils.ts +++ b/projects/app/src/pageComponents/app/detail/Edit/ChatAgent/utils.ts @@ -164,6 +164,13 @@ export function agentForm2AppWorkflow( ...Input_Template_File_Link, value: [[workflowStartNodeId, NodeOutputKeyEnum.userFiles]] }, + { + key: NodeInputKeyEnum.aiChatVision, + renderTypeList: [FlowNodeInputTypeEnum.hidden], + label: '', + valueType: WorkflowIOValueTypeEnum.boolean, + value: true + }, { key: NodeInputKeyEnum.history, renderTypeList: [FlowNodeInputTypeEnum.numberInput, FlowNodeInputTypeEnum.reference], diff --git a/projects/app/src/pages/_app.tsx b/projects/app/src/pages/_app.tsx index 40ad804fa4..79fd482a02 100644 --- a/projects/app/src/pages/_app.tsx +++ b/projects/app/src/pages/_app.tsx @@ -24,8 +24,7 @@ type AppPropsWithLayout = AppProps & { Component: NextPageWithLayout; }; -// 哪些路由有自定义 Head -const routesWithCustomHead = ['/chat', '/chat/share', '/app/detail/', '/dataset/detail']; +const routesWithCustomHead = ['/chat', '/chat/share', '/app/detail', '/dataset/detail']; // 哪些路由不需要 Layout const routesWithoutLayout = ['/openapi']; diff --git a/projects/marketplace/Dockerfile b/projects/marketplace/Dockerfile index ad901d1a43..739209ac6b 100644 --- a/projects/marketplace/Dockerfile +++ b/projects/marketplace/Dockerfile @@ -5,7 +5,7 @@ WORKDIR /app ARG proxy RUN [ -z "$proxy" ] || sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories -RUN apk add --no-cache libc6-compat && npm install -g pnpm@10 +RUN apk add --no-cache libc6-compat && npm install -g pnpm@9 # copy packages and one project COPY pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./ @@ -37,7 +37,7 @@ COPY --from=maindeps /app/projects/marketplace/node_modules ./projects/marketpla RUN [ -z "$proxy" ] || sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories -RUN apk add --no-cache libc6-compat && npm install -g pnpm@10 +RUN apk add --no-cache libc6-compat && npm install -g pnpm@9 ENV NODE_OPTIONS="--max-old-space-size=4096" ENV NEXT_PUBLIC_BASE_URL=$base_url diff --git a/projects/marketplace/package.json b/projects/marketplace/package.json index cf5ac932f4..4d6eb11214 100644 --- a/projects/marketplace/package.json +++ b/projects/marketplace/package.json @@ -10,7 +10,7 @@ }, "engines": { "node": ">=20", - "pnpm": "10.x" + "pnpm": "9.x" }, "dependencies": { "@chakra-ui/anatomy": "catalog:", diff --git a/projects/mcp_server/Dockerfile b/projects/mcp_server/Dockerfile index 6037deb434..65a2cd65d5 100644 --- a/projects/mcp_server/Dockerfile +++ b/projects/mcp_server/Dockerfile @@ -4,7 +4,7 @@ WORKDIR /app ARG proxy -RUN npm install -g pnpm@10 +RUN npm install -g pnpm@9 # 复制package.json COPY pnpm-lock.yaml pnpm-workspace.yaml ./ @@ -40,7 +40,7 @@ COPY --from=install /app/node_modules /app/node_modules COPY --from=install /app/projects/mcp_server/node_modules /app/projects/mcp_server/node_modules RUN [ -z "$proxy" ] || sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories -RUN apk add --no-cache libc6-compat curl bash && npm install -g pnpm@10 +RUN apk add --no-cache libc6-compat curl bash && npm install -g pnpm@9 # Install curl and bash, then install bun RUN curl -fsSL https://bun.sh/install | bash diff --git a/projects/mcp_server/package.json b/projects/mcp_server/package.json index 12a92cac78..930f1e08fa 100644 --- a/projects/mcp_server/package.json +++ b/projects/mcp_server/package.json @@ -15,7 +15,7 @@ }, "engines": { "node": ">=20", - "pnpm": "10.x" + "pnpm": "9.x" }, "dependencies": { "@fastgpt/global": "workspace:*", diff --git a/projects/sandbox/Dockerfile b/projects/sandbox/Dockerfile index 3dde5c568a..fb25740647 100644 --- a/projects/sandbox/Dockerfile +++ b/projects/sandbox/Dockerfile @@ -5,7 +5,7 @@ WORKDIR /app ARG proxy # 安装 pnpm -RUN apk add --no-cache nodejs npm && npm install -g pnpm@10 +RUN apk add --no-cache nodejs npm && npm install -g pnpm@9 # 复制 workspace 配置和依赖包 COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./ diff --git a/projects/sandbox/package.json b/projects/sandbox/package.json index 8b9b0810c9..845c6694bb 100644 --- a/projects/sandbox/package.json +++ b/projects/sandbox/package.json @@ -14,7 +14,7 @@ }, "engines": { "node": ">=20", - "pnpm": "10.x" + "pnpm": "9.x" }, "dependencies": { "@fastgpt-sdk/logger": "catalog:", diff --git a/sdk/logger/package.json b/sdk/logger/package.json index de5bd8e0d8..3e2419d8f5 100644 --- a/sdk/logger/package.json +++ b/sdk/logger/package.json @@ -40,7 +40,7 @@ }, "engines": { "node": ">=20", - "pnpm": "10.x" + "pnpm": "9.x" }, "license": "Apache-2.0", "dependencies": { diff --git a/sdk/storage/package.json b/sdk/storage/package.json index cef9ccd1a1..d711f4f640 100644 --- a/sdk/storage/package.json +++ b/sdk/storage/package.json @@ -21,7 +21,7 @@ }, "engines": { "node": ">=20", - "pnpm": "10.x" + "pnpm": "9.x" }, "exports": { ".": { diff --git a/test/cases/global/core/chat/adapt.test.ts b/test/cases/global/core/chat/adapt.test.ts index 701df823f4..f90dfd8102 100644 --- a/test/cases/global/core/chat/adapt.test.ts +++ b/test/cases/global/core/chat/adapt.test.ts @@ -319,8 +319,7 @@ describe('chats2GPTMessages', () => { const result = chats2GPTMessages({ messages, reserveId: false }); - expect(result).toHaveLength(1); - expect(result[0].content).toBe('What would you like to know?'); + expect(result).toHaveLength(0); }); it('should handle interactive agentPlanAskUserForm', () => { @@ -346,10 +345,7 @@ describe('chats2GPTMessages', () => { const result = chats2GPTMessages({ messages, reserveId: false }); - expect(result).toHaveLength(1); - expect(result[0].content).toContain('Please fill in the form'); - expect(result[0].content).toContain('- Name: John'); - expect(result[0].content).toContain('- Age: 25'); + expect(result).toHaveLength(0); }); it('should handle plan with reserveTool true', () => { @@ -370,10 +366,12 @@ describe('chats2GPTMessages', () => { } } as any, { + planId: 'plan-1', stepId: 'step-1', text: { content: 'Search results here' } } as any, { + planId: 'plan-1', stepId: 'step-2', text: { content: 'Analysis complete' } } as any @@ -406,7 +404,7 @@ describe('chats2GPTMessages', () => { } as any, { plan: { - planId: 'plan-1', + planId: 'plan-2', task: 'Task 1 duplicate', description: 'Description 1 duplicate', background: 'Background 1 duplicate', @@ -420,7 +418,7 @@ describe('chats2GPTMessages', () => { const result = chats2GPTMessages({ messages, reserveId: false, reserveTool: true }); // Should only have 2 messages (1 assistant + 1 tool) for the first plan - expect(result).toHaveLength(2); + expect(result).toHaveLength(4); }); it('should not process plan when reserveTool is false', () => { @@ -444,7 +442,7 @@ describe('chats2GPTMessages', () => { const result = chats2GPTMessages({ messages, reserveId: false, reserveTool: false }); // Plan should be skipped when reserveTool is false - expect(result).toHaveLength(0); + expect(result).toHaveLength(2); }); }); diff --git a/test/cases/service/core/ai/parseStreamResponse.test.ts b/test/cases/service/core/ai/parseStreamResponse.test.ts deleted file mode 100644 index 407450d03d..0000000000 --- a/test/cases/service/core/ai/parseStreamResponse.test.ts +++ /dev/null @@ -1,450 +0,0 @@ -import type { CompletionFinishReason } from '@fastgpt/global/core/ai/type'; -import { parseLLMStreamResponse } from '@fastgpt/service/core/ai/utils'; -import { describe, expect, it } from 'vitest'; - -describe('Parse reasoning stream content test', async () => { - const partList = [ - { - data: [{ content: '你好1' }, { content: '你好2' }, { content: '你好3' }], - correct: { answer: '你好1你好2你好3', reasoning: '' } - }, - { - data: [ - { reasoning_content: '这是' }, - { reasoning_content: '思考' }, - { reasoning_content: '过程' }, - { content: '你好1' }, - { content: '你好2' }, - { content: '你好3' } - ], - correct: { answer: '你好1你好2你好3', reasoning: '这是思考过程' } - }, - { - data: [ - { content: '' }, - { content: '这是' }, - { content: '思考' }, - { content: '过程' }, - { content: '' }, - { content: '你好1' }, - { content: '你好2' }, - { content: '你好3' } - ], - correct: { answer: '你好1你好2你好3', reasoning: '这是思考过程' } - }, - { - data: [ - { content: '' }, - { content: '这是' }, - { content: '思考' }, - { content: '过程' }, - { content: '' }, - { content: '你好1' }, - { content: '你好2' }, - { content: '你好3' } - ], - correct: { answer: '你好1你好2你好3', reasoning: '这是思考过程' } - }, - { - data: [ - { content: '这是' }, - { content: '思考' }, - { content: '过程' }, - { content: '' }, - { content: '你好1' }, - { content: '你好2' }, - { content: '你好3' } - ], - correct: { answer: '你好1你好2你好3', reasoning: '这是思考过程' } - }, - { - data: [ - { content: '这是' }, - { content: '思考' }, - { content: '过程' }, - { content: '你好1' }, - { content: '你好2' }, - { content: '你好3' } - ], - correct: { answer: '你好1你好2你好3', reasoning: '这是思考过程' } - }, - { - data: [ - { content: '这是' }, - { content: '思考' }, - { content: '过程' }, - { content: '你好1' }, - { content: '你好2' }, - { content: '你好3' } - ], - correct: { answer: '你好1你好2你好3', reasoning: '这是思考过程' } - }, - { - data: [ - { content: '这是' }, - { content: '思考' }, - { content: '过程你好1' }, - { content: '你好2' }, - { content: '你好3' } - ], - correct: { answer: '你好1你好2你好3', reasoning: '这是思考过程' } - }, - { - data: [ - { content: '这是' }, - { content: '思考' }, - { content: '过程你好1' }, - { content: '你好2' }, - { content: '你好3' } - ], - correct: { answer: '你好1你好2你好3', reasoning: '这是思考过程这是' }, - { content: '思考' }, - { content: '过程 { - it(`Reasoning test:${index}`, () => { - const { parsePart } = parseLLMStreamResponse(); - - let answer = ''; - let reasoning = ''; - part.data.forEach((item) => { - const formatPart = { - choices: [ - { - delta: { - role: 'assistant', - content: item.content, - reasoning_content: item.reasoning_content - } - } - ] - }; - const { reasoningContent, content } = parsePart({ - part: formatPart, - parseThinkTag: true, - retainDatasetCite: false - }); - answer += content; - reasoning += reasoningContent; - }); - expect(answer).toBe(part.correct.answer); - expect(reasoning).toBe(part.correct.reasoning); - }); - }); -}); - -describe('Parse dataset cite content test', async () => { - const partList = [ - { - // 完整的 - data: [ - { content: '知识库' }, - { content: '问答系统' }, - { content: '[67e517e74767063e882d6861](CITE)' } - ], - correct: { - content: '知识库问答系统[67e517e74767063e882d6861](CITE)', - responseContent: '知识库问答系统' - } - }, - { - // 只要 objectId - data: [ - { content: '知识库' }, - { content: '问答系统' }, - { content: '[67e517e747' }, - { content: '67063e882d' }, - { content: '6861]' } - ], - correct: { - content: '知识库问答系统[67e517e74767063e882d6861]', - responseContent: '知识库问答系统' - } - }, - { - // 满足替换条件的 - data: [ - { content: '知识库' }, - { content: '问答系统' }, - { content: '[67e517e747' }, - { content: '67063e882d' }, - { content: '6861](' } - ], - correct: { - content: '知识库问答系统[67e517e74767063e882d6861](', - responseContent: '知识库问答系统' - } - }, - { - // 满足替换条件的 - data: [ - { content: '知识库' }, - { content: '问答系统' }, - { content: '[67e517e747' }, - { content: '67063e882d' }, - { content: '6861](C' } - ], - correct: { - content: '知识库问答系统[67e517e74767063e882d6861](C', - responseContent: '知识库问答系统' - } - }, - { - // 满足替换条件的 - data: [ - { content: '知识库' }, - { content: '问答系统' }, - { content: '[67e517e747' }, - { content: '67063e882d' }, - { content: '6861](CI' } - ], - correct: { - content: '知识库问答系统[67e517e74767063e882d6861](CI', - responseContent: '知识库问答系统' - } - }, - { - // 满足替换条件的 - data: [ - { content: '知识库' }, - { content: '问答系统' }, - { content: '[67e517e747' }, - { content: '67063e882d' }, - { content: '6861](CIT' } - ], - correct: { - content: '知识库问答系统[67e517e74767063e882d6861](CIT', - responseContent: '知识库问答系统' - } - }, - { - // 满足替换条件的 - data: [ - { content: '知识库' }, - { content: '问答系统' }, - { content: '[67e517e747' }, - { content: '67063e882d' }, - { content: '6861](CITE' } - ], - correct: { - content: '知识库问答系统[67e517e74767063e882d6861](CITE', - responseContent: '知识库问答系统' - } - }, - { - // 缺失结尾 - data: [ - { content: '知识库问答系统' }, - { content: '[67e517e747' }, - { content: '67063e882d' }, - { content: '6861](CITE' } - ], - correct: { - content: '知识库问答系统[67e517e74767063e882d6861](CITE', - responseContent: '知识库问答系统' - } - }, - { - // ObjectId 不正确 - data: [ - { content: '知识库问答系统' }, - { content: '[67e517e747' }, - { content: '67882d' }, - { content: '6861](CITE)' } - ], - correct: { - content: '知识库问答系统[67e517e74767882d6861](CITE)', - responseContent: '知识库问答系统[67e517e74767882d6861](CITE)' - } - }, - { - // 其他链接 - data: [{ content: '知识库' }, { content: '问答系统' }, { content: '[](https://fastgpt.cn)' }], - correct: { - content: '知识库问答系统[](https://fastgpt.cn)', - responseContent: '知识库问答系统[](https://fastgpt.cn)' - } - }, - { - // 不完整的其他链接 - data: [{ content: '知识库' }, { content: '问答系统' }, { content: '[](https://fastgp' }], - correct: { - content: '知识库问答系统[](https://fastgp', - responseContent: '知识库问答系统[](https://fastgp' - } - }, - { - // 开头 - data: [{ content: '[知识库' }, { content: '问答系统' }, { content: '[](https://fastgp' }], - correct: { - content: '[知识库问答系统[](https://fastgp', - responseContent: '[知识库问答系统[](https://fastgp' - } - }, - { - // 结尾 - data: [{ content: '知识库' }, { content: '问答系统' }, { content: '[' }], - correct: { - content: '知识库问答系统[', - responseContent: '知识库问答系统[' - } - }, - { - // 中间 - data: [ - { content: '知识库' }, - { content: '问答系统' }, - { content: '[' }, - { content: '问答系统]' } - ], - correct: { - content: '知识库问答系统[问答系统]', - responseContent: '知识库问答系统[问答系统]' - } - }, - { - // 双链接 - data: [ - { content: '知识库' }, - { content: '问答系统' }, - { content: '[](https://fastgpt.cn)' }, - { content: '[67e517e747' }, - { content: '67063e882d' }, - { content: '6861](CITE)' } - ], - correct: { - content: '知识库问答系统[](https://fastgpt.cn)[67e517e74767063e882d6861](CITE)', - responseContent: '知识库问答系统[](https://fastgpt.cn)' - } - }, - { - // 双链接缺失部分 - data: [ - { content: '知识库' }, - { content: '问答系统' }, - { content: '[](https://fastgpt.cn)' }, - { content: '[67e517e747' }, - { content: '67063e882d' }, - { content: '6861](CIT' } - ], - correct: { - content: '知识库问答系统[](https://fastgpt.cn)[67e517e74767063e882d6861](CIT', - responseContent: '知识库问答系统[](https://fastgpt.cn)' - } - }, - { - // 双Cite - data: [ - { content: '知识库' }, - { content: '问答系统' }, - { content: '[67e517e747' }, - { content: '67063e882d' }, - { content: '6861](CITE)' }, - { content: '[67e517e747' }, - { content: '67063e882d' }, - { content: '6861](CITE)' } - ], - correct: { - content: '知识库问答系统[67e517e74767063e882d6861](CITE)[67e517e74767063e882d6861](CITE)', - responseContent: '知识库问答系统' - } - }, - { - // 双Cite-第一个假Cite - data: [ - { content: '知识库' }, - { content: '问答系统' }, - { content: '[67e517e747' }, - { content: '6861](CITE)' }, - { content: '[67e517e747' }, - { content: '67063e882d' }, - { content: '6861](CITE)' } - ], - correct: { - content: '知识库问答系统[67e517e7476861](CITE)[67e517e74767063e882d6861](CITE)', - responseContent: '知识库问答系统[67e517e7476861](CITE)' - } - }, - { - // [id](CITE) - data: [ - { content: '知识库' }, - { content: '问答系统' }, - { content: '[i' }, - { content: 'd](CITE)' }, - { content: '[67e517e747' }, - { content: '67063e882d' }, - { content: '6861](CITE)' } - ], - correct: { - content: '知识库问答系统[id](CITE)[67e517e74767063e882d6861](CITE)', - responseContent: '知识库问答系统' - } - }, - { - // [id](CITE) - data: [ - { content: '知识库' }, - { content: '问答系统' }, - { content: '[i' }, - { content: 'd](CITE)' } - ], - correct: { - content: '知识库问答系统[id](CITE)', - responseContent: '知识库问答系统' - } - } - ]; - - partList.forEach((part, index) => { - it(`Dataset cite test: ${index}`, () => { - const { parsePart } = parseLLMStreamResponse(); - - let answer = ''; - let responseContent = ''; - const list = [...part.data, { content: '' }]; - list.forEach((item, index) => { - const formatPart = { - choices: [ - { - delta: { - role: 'assistant', - content: item.content, - reasoning_content: '' - }, - finish_reason: (index === list.length - 2 ? 'stop' : null) as CompletionFinishReason - } - ] - }; - const { content, responseContent: newResponseContent } = parsePart({ - part: formatPart, - parseThinkTag: false, - retainDatasetCite: false - }); - answer += content; - responseContent += newResponseContent; - }); - - expect(answer).toEqual(part.correct.content); - expect(responseContent).toEqual(part.correct.responseContent); - }); - }); -}); diff --git a/test/cases/service/core/ai/utils.test.ts b/test/cases/service/core/ai/utils.test.ts new file mode 100644 index 0000000000..4f29265637 --- /dev/null +++ b/test/cases/service/core/ai/utils.test.ts @@ -0,0 +1,603 @@ +import { describe, expect, it } from 'vitest'; +import { + parseJsonArgs, + parseLLMStreamResponse, + computedMaxToken, + computedTemperature, + parseReasoningContent +} from '@fastgpt/service/core/ai/utils'; +import type { CompletionFinishReason } from '@fastgpt/global/core/ai/type'; +import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.schema'; + +const mockModel = (maxResponse: number, maxTemperature?: number) => + ({ maxResponse, maxTemperature }) as LLMModelItemType; + +describe('computedMaxToken', () => { + it('should return undefined when maxToken is undefined', () => { + expect(computedMaxToken({ maxToken: undefined, model: mockModel(4096) })).toBeUndefined(); + }); + + it('should cap maxToken to model.maxResponse', () => { + expect(computedMaxToken({ maxToken: 8000, model: mockModel(4096) })).toBe(4096); + }); + + it('should return maxToken when within model.maxResponse', () => { + expect(computedMaxToken({ maxToken: 1000, model: mockModel(4096) })).toBe(1000); + }); + + it('should enforce minimum of 1 by default', () => { + expect(computedMaxToken({ maxToken: 0, model: mockModel(4096) })).toBe(1); + }); + + it('should enforce custom min value', () => { + expect(computedMaxToken({ maxToken: 5, model: mockModel(4096), min: 10 })).toBe(10); + }); + + it('should use maxToken when it exceeds min', () => { + expect(computedMaxToken({ maxToken: 100, model: mockModel(4096), min: 10 })).toBe(100); + }); +}); + +describe('computedTemperature', () => { + it('should return undefined when model has no maxTemperature', () => { + expect(computedTemperature({ model: mockModel(4096), temperature: 5 })).toBeUndefined(); + }); + + it('should scale temperature proportionally', () => { + // maxTemperature=2, temperature=5 => 2*(5/10)=1.0 + expect(computedTemperature({ model: mockModel(4096, 2), temperature: 5 })).toBe(1.0); + }); + + it('should return maxTemperature when temperature=10', () => { + expect(computedTemperature({ model: mockModel(4096, 2), temperature: 10 })).toBe(2.0); + }); + + it('should enforce minimum of 0.01', () => { + expect(computedTemperature({ model: mockModel(4096, 2), temperature: 0 })).toBe(0.01); + }); + + it('should round to 2 decimal places', () => { + // maxTemperature=1, temperature=3 => 1*(3/10)=0.30 + expect(computedTemperature({ model: mockModel(4096, 1), temperature: 3 })).toBe(0.3); + }); +}); + +describe('parseReasoningContent', () => { + it('should return empty reasoning and full text when no think tag', () => { + expect(parseReasoningContent('hello world')).toEqual(['', 'hello world']); + }); + + it('should extract think content and remaining answer', () => { + expect(parseReasoningContent('reasoninganswer')).toEqual([ + 'reasoning', + 'answer' + ]); + }); + + it('should trim whitespace from think content', () => { + expect(parseReasoningContent(' reasoning answer')).toEqual([ + 'reasoning', + 'answer' + ]); + }); + + it('should return empty answer when nothing after think tag', () => { + expect(parseReasoningContent('reasoning')).toEqual(['reasoning', '']); + }); + + it('should handle multiline think content', () => { + expect(parseReasoningContent('line1\nline2answer')).toEqual([ + 'line1\nline2', + 'answer' + ]); + }); + + it('should only match first think tag', () => { + expect(parseReasoningContent('firstmidsecondend')).toEqual([ + 'first', + 'midsecondend' + ]); + }); +}); + +describe('parseLLMStreamResponse', () => { + describe('Parse reasoning stream content test', async () => { + const partList = [ + { + data: [{ content: '你好1' }, { content: '你好2' }, { content: '你好3' }], + correct: { answer: '你好1你好2你好3', reasoning: '' } + }, + { + data: [ + { reasoning_content: '这是' }, + { reasoning_content: '思考' }, + { reasoning_content: '过程' }, + { content: '你好1' }, + { content: '你好2' }, + { content: '你好3' } + ], + correct: { answer: '你好1你好2你好3', reasoning: '这是思考过程' } + }, + { + data: [ + { content: '' }, + { content: '这是' }, + { content: '思考' }, + { content: '过程' }, + { content: '' }, + { content: '你好1' }, + { content: '你好2' }, + { content: '你好3' } + ], + correct: { answer: '你好1你好2你好3', reasoning: '这是思考过程' } + }, + { + data: [ + { content: '' }, + { content: '这是' }, + { content: '思考' }, + { content: '过程' }, + { content: '' }, + { content: '你好1' }, + { content: '你好2' }, + { content: '你好3' } + ], + correct: { answer: '你好1你好2你好3', reasoning: '这是思考过程' } + }, + { + data: [ + { content: '这是' }, + { content: '思考' }, + { content: '过程' }, + { content: '' }, + { content: '你好1' }, + { content: '你好2' }, + { content: '你好3' } + ], + correct: { answer: '你好1你好2你好3', reasoning: '这是思考过程' } + }, + { + data: [ + { content: '这是' }, + { content: '思考' }, + { content: '过程' }, + { content: '你好1' }, + { content: '你好2' }, + { content: '你好3' } + ], + correct: { answer: '你好1你好2你好3', reasoning: '这是思考过程' } + }, + { + data: [ + { content: '这是' }, + { content: '思考' }, + { content: '过程' }, + { content: '你好1' }, + { content: '你好2' }, + { content: '你好3' } + ], + correct: { answer: '你好1你好2你好3', reasoning: '这是思考过程' } + }, + { + data: [ + { content: '这是' }, + { content: '思考' }, + { content: '过程你好1' }, + { content: '你好2' }, + { content: '你好3' } + ], + correct: { answer: '你好1你好2你好3', reasoning: '这是思考过程' } + }, + { + data: [ + { content: '这是' }, + { content: '思考' }, + { content: '过程你好1' }, + { content: '你好2' }, + { content: '你好3' } + ], + correct: { answer: '你好1你好2你好3', reasoning: '这是思考过程这是' }, + { content: '思考' }, + { content: '过程 { + it(`Reasoning test:${index}`, () => { + const { parsePart } = parseLLMStreamResponse(); + + let answer = ''; + let reasoning = ''; + part.data.forEach((item) => { + const formatPart = { + choices: [ + { + delta: { + role: 'assistant', + content: item.content, + reasoning_content: item.reasoning_content + } + } + ] + }; + const { reasoningContent, content } = parsePart({ + part: formatPart, + parseThinkTag: true, + retainDatasetCite: false + }); + answer += content; + reasoning += reasoningContent; + }); + expect(answer).toBe(part.correct.answer); + expect(reasoning).toBe(part.correct.reasoning); + }); + }); + }); + + describe('Parse dataset cite content test', async () => { + const partList = [ + { + // 完整的 + data: [ + { content: '知识库' }, + { content: '问答系统' }, + { content: '[67e517e74767063e882d6861](CITE)' } + ], + correct: { + content: '知识库问答系统[67e517e74767063e882d6861](CITE)', + responseContent: '知识库问答系统' + } + }, + { + // 只要 objectId + data: [ + { content: '知识库' }, + { content: '问答系统' }, + { content: '[67e517e747' }, + { content: '67063e882d' }, + { content: '6861]' } + ], + correct: { + content: '知识库问答系统[67e517e74767063e882d6861]', + responseContent: '知识库问答系统' + } + }, + { + // 满足替换条件的 + data: [ + { content: '知识库' }, + { content: '问答系统' }, + { content: '[67e517e747' }, + { content: '67063e882d' }, + { content: '6861](' } + ], + correct: { + content: '知识库问答系统[67e517e74767063e882d6861](', + responseContent: '知识库问答系统' + } + }, + { + // 满足替换条件的 + data: [ + { content: '知识库' }, + { content: '问答系统' }, + { content: '[67e517e747' }, + { content: '67063e882d' }, + { content: '6861](C' } + ], + correct: { + content: '知识库问答系统[67e517e74767063e882d6861](C', + responseContent: '知识库问答系统' + } + }, + { + // 满足替换条件的 + data: [ + { content: '知识库' }, + { content: '问答系统' }, + { content: '[67e517e747' }, + { content: '67063e882d' }, + { content: '6861](CI' } + ], + correct: { + content: '知识库问答系统[67e517e74767063e882d6861](CI', + responseContent: '知识库问答系统' + } + }, + { + // 满足替换条件的 + data: [ + { content: '知识库' }, + { content: '问答系统' }, + { content: '[67e517e747' }, + { content: '67063e882d' }, + { content: '6861](CIT' } + ], + correct: { + content: '知识库问答系统[67e517e74767063e882d6861](CIT', + responseContent: '知识库问答系统' + } + }, + { + // 满足替换条件的 + data: [ + { content: '知识库' }, + { content: '问答系统' }, + { content: '[67e517e747' }, + { content: '67063e882d' }, + { content: '6861](CITE' } + ], + correct: { + content: '知识库问答系统[67e517e74767063e882d6861](CITE', + responseContent: '知识库问答系统' + } + }, + { + // 缺失结尾 + data: [ + { content: '知识库问答系统' }, + { content: '[67e517e747' }, + { content: '67063e882d' }, + { content: '6861](CITE' } + ], + correct: { + content: '知识库问答系统[67e517e74767063e882d6861](CITE', + responseContent: '知识库问答系统' + } + }, + { + // ObjectId 不正确 + data: [ + { content: '知识库问答系统' }, + { content: '[67e517e747' }, + { content: '67882d' }, + { content: '6861](CITE)' } + ], + correct: { + content: '知识库问答系统[67e517e74767882d6861](CITE)', + responseContent: '知识库问答系统[67e517e74767882d6861](CITE)' + } + }, + { + // 其他链接 + data: [ + { content: '知识库' }, + { content: '问答系统' }, + { content: '[](https://fastgpt.cn)' } + ], + correct: { + content: '知识库问答系统[](https://fastgpt.cn)', + responseContent: '知识库问答系统[](https://fastgpt.cn)' + } + }, + { + // 不完整的其他链接 + data: [{ content: '知识库' }, { content: '问答系统' }, { content: '[](https://fastgp' }], + correct: { + content: '知识库问答系统[](https://fastgp', + responseContent: '知识库问答系统[](https://fastgp' + } + }, + { + // 开头 + data: [{ content: '[知识库' }, { content: '问答系统' }, { content: '[](https://fastgp' }], + correct: { + content: '[知识库问答系统[](https://fastgp', + responseContent: '[知识库问答系统[](https://fastgp' + } + }, + { + // 结尾 + data: [{ content: '知识库' }, { content: '问答系统' }, { content: '[' }], + correct: { + content: '知识库问答系统[', + responseContent: '知识库问答系统[' + } + }, + { + // 中间 + data: [ + { content: '知识库' }, + { content: '问答系统' }, + { content: '[' }, + { content: '问答系统]' } + ], + correct: { + content: '知识库问答系统[问答系统]', + responseContent: '知识库问答系统[问答系统]' + } + }, + { + // 双链接 + data: [ + { content: '知识库' }, + { content: '问答系统' }, + { content: '[](https://fastgpt.cn)' }, + { content: '[67e517e747' }, + { content: '67063e882d' }, + { content: '6861](CITE)' } + ], + correct: { + content: '知识库问答系统[](https://fastgpt.cn)[67e517e74767063e882d6861](CITE)', + responseContent: '知识库问答系统[](https://fastgpt.cn)' + } + }, + { + // 双链接缺失部分 + data: [ + { content: '知识库' }, + { content: '问答系统' }, + { content: '[](https://fastgpt.cn)' }, + { content: '[67e517e747' }, + { content: '67063e882d' }, + { content: '6861](CIT' } + ], + correct: { + content: '知识库问答系统[](https://fastgpt.cn)[67e517e74767063e882d6861](CIT', + responseContent: '知识库问答系统[](https://fastgpt.cn)' + } + }, + { + // 双Cite + data: [ + { content: '知识库' }, + { content: '问答系统' }, + { content: '[67e517e747' }, + { content: '67063e882d' }, + { content: '6861](CITE)' }, + { content: '[67e517e747' }, + { content: '67063e882d' }, + { content: '6861](CITE)' } + ], + correct: { + content: '知识库问答系统[67e517e74767063e882d6861](CITE)[67e517e74767063e882d6861](CITE)', + responseContent: '知识库问答系统' + } + }, + { + // 双Cite-第一个假Cite + data: [ + { content: '知识库' }, + { content: '问答系统' }, + { content: '[67e517e747' }, + { content: '6861](CITE)' }, + { content: '[67e517e747' }, + { content: '67063e882d' }, + { content: '6861](CITE)' } + ], + correct: { + content: '知识库问答系统[67e517e7476861](CITE)[67e517e74767063e882d6861](CITE)', + responseContent: '知识库问答系统[67e517e7476861](CITE)' + } + }, + { + // [id](CITE) + data: [ + { content: '知识库' }, + { content: '问答系统' }, + { content: '[i' }, + { content: 'd](CITE)' }, + { content: '[67e517e747' }, + { content: '67063e882d' }, + { content: '6861](CITE)' } + ], + correct: { + content: '知识库问答系统[id](CITE)[67e517e74767063e882d6861](CITE)', + responseContent: '知识库问答系统' + } + }, + { + // [id](CITE) + data: [ + { content: '知识库' }, + { content: '问答系统' }, + { content: '[i' }, + { content: 'd](CITE)' } + ], + correct: { + content: '知识库问答系统[id](CITE)', + responseContent: '知识库问答系统' + } + } + ]; + + partList.forEach((part, index) => { + it(`Dataset cite test: ${index}`, () => { + const { parsePart } = parseLLMStreamResponse(); + + let answer = ''; + let responseContent = ''; + const list = [...part.data, { content: '' }]; + list.forEach((item, index) => { + const formatPart = { + choices: [ + { + delta: { + role: 'assistant', + content: item.content, + reasoning_content: '' + }, + finish_reason: (index === list.length - 2 ? 'stop' : null) as CompletionFinishReason + } + ] + }; + const { content, responseContent: newResponseContent } = parsePart({ + part: formatPart, + parseThinkTag: false, + retainDatasetCite: false + }); + answer += content; + responseContent += newResponseContent; + }); + + expect(answer).toEqual(part.correct.content); + expect(responseContent).toEqual(part.correct.responseContent); + }); + }); + }); +}); + +describe('parseJsonArgs', () => { + it('should parse valid JSON string', () => { + const result = parseJsonArgs<{ a: number }>('{"a": 1}'); + expect(result).toEqual({ a: 1 }); + }); + + it('should parse JSON5 (unquoted keys)', () => { + const result = parseJsonArgs<{ a: number }>('{a: 1}'); + expect(result).toEqual({ a: 1 }); + }); + + it('should parse JSON with trailing commas', () => { + const result = parseJsonArgs<{ a: number; b: string }>('{a: 1, b: "hello",}'); + expect(result).toEqual({ a: 1, b: 'hello' }); + }); + + it('should repair and parse broken JSON (missing closing brace)', () => { + const result = parseJsonArgs<{ a: number }>('{a: 1'); + expect(result).toEqual({ a: 1 }); + }); + + it('should extract JSON from surrounding text', () => { + const result = parseJsonArgs<{ key: string }>('prefix {"key": "value"} suffix'); + expect(result).toEqual({ key: 'value' }); + }); + + it('should parse array JSON', () => { + const result = parseJsonArgs('[1, 2, 3]'); + expect(result).toEqual([1, 2, 3]); + }); + + it('should return undefined for completely invalid input', () => { + // jsonrepair returns the string as-is, json5 parses it as a string — not an object + // Only truly unparseable input (e.g. unmatched braces with garbage) returns undefined + const result = parseJsonArgs('{{{invalid'); + expect(result).toBeUndefined(); + }); + + it('should return undefined for empty string', () => { + const result = parseJsonArgs(''); + expect(result).toBeUndefined(); + }); + + it('should parse nested objects', () => { + const result = parseJsonArgs<{ a: { b: number } }>('{"a": {"b": 2}}'); + expect(result).toEqual({ a: { b: 2 } }); + }); +}); diff --git a/test/cases/service/core/chat/saveChat.test.ts b/test/cases/service/core/chat/saveChat.test.ts index 4d8d67db07..b424d3ad7e 100644 --- a/test/cases/service/core/chat/saveChat.test.ts +++ b/test/cases/service/core/chat/saveChat.test.ts @@ -762,6 +762,72 @@ describe('pushChatRecords', () => { } }); + it('should persist agentPlanAskQuery answer before pushing new records', async () => { + await MongoChatItem.create({ + chatId: 'test-chat-id', + teamId: testTeamId, + tmbId: testTmbId, + appId: testAppId, + obj: ChatRoleEnum.AI, + dataId: 'plan-ask-data-id', + value: [ + { + interactive: { + type: 'agentPlanAskQuery', + planId: 'plan_1', + params: { + content: '请补充目标' + } + } + } + ] + }); + + const props = createMockProps( + { + userContent: { + obj: ChatRoleEnum.Human, + value: [ + { + text: { content: '深入了解 Rust 系统编程方向' } + } + ] + } + }, + { appId: testAppId, teamId: testTeamId, tmbId: testTmbId } + ); + + const interactive = { + type: 'agentPlanAskQuery' as const, + planId: 'plan_1', + params: { + content: '请补充目标' + }, + entryNodeIds: [], + memoryEdges: [], + nodeOutputs: [] + }; + + await updateInteractiveChat({ interactive, ...props }); + + const chatItem = await MongoChatItem.findOne({ + appId: testAppId, + chatId: props.chatId, + obj: ChatRoleEnum.AI, + dataId: 'plan-ask-data-id' + }); + + if (chatItem?.obj !== ChatRoleEnum.AI) { + throw new Error('chatItem does not have AI interactive value'); + } + const lastValue = chatItem.value[chatItem.value.length - 1]; + if (lastValue.interactive?.type !== 'agentPlanAskQuery') { + throw new Error('chatItem does not have agentPlanAskQuery interactive'); + } + + expect(lastValue.interactive.params.answer).toBe('深入了解 Rust 系统编程方向'); + }); + it('should remove paymentPause interactive value', async () => { // Create an AI chat item with paymentPause interactive await MongoChatItem.create({