From 166b45e52944c6e22b83ee763ecd03e9f5006d61 Mon Sep 17 00:00:00 2001 From: Huarch Date: Mon, 8 Jun 2026 19:47:13 +0800 Subject: [PATCH] fix(chat): normalize loaded messages --- .../chat/GlobalChatbox.utils.test.ts | 35 ++++++++++ src/components/chat/GlobalChatbox.utils.ts | 64 ++++++++++++++++++- 2 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 src/components/chat/GlobalChatbox.utils.test.ts diff --git a/src/components/chat/GlobalChatbox.utils.test.ts b/src/components/chat/GlobalChatbox.utils.test.ts new file mode 100644 index 0000000..8e89124 --- /dev/null +++ b/src/components/chat/GlobalChatbox.utils.test.ts @@ -0,0 +1,35 @@ +import { cloneMessage } from "./GlobalChatbox.utils"; +import type { Message } from "./GlobalChatbox.types"; + +describe("cloneMessage", () => { + it("normalizes persisted question and todo arrays", () => { + const message = { + id: "assistant-1", + role: "assistant", + content: "需要补充信息", + questions: [ + { + requestId: "question-1", + sessionId: "session-1", + questions: [ + { + header: "范围", + question: "请选择分析范围", + }, + ], + createdAt: 1, + status: "pending", + }, + ], + todos: { + sessionId: "session-1", + createdAt: 1, + }, + } as unknown as Message; + + const cloned = cloneMessage(message); + + expect(cloned.questions?.[0]?.questions[0]?.options).toEqual([]); + expect(cloned.todos?.todos).toEqual([]); + }); +}); diff --git a/src/components/chat/GlobalChatbox.utils.ts b/src/components/chat/GlobalChatbox.utils.ts index 4564a52..44ad9b6 100644 --- a/src/components/chat/GlobalChatbox.utils.ts +++ b/src/components/chat/GlobalChatbox.utils.ts @@ -1,4 +1,8 @@ import type { Message } from "./GlobalChatbox.types"; +import type { + AgentQuestionRequest, + AgentTodoUpdate, +} from "@/lib/chatStream"; export const createId = () => `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; @@ -29,10 +33,66 @@ export const stripMarkdown = (md: string): string => .replace(/<[^>]+>/g, "") .trim(); +const normalizeQuestionRequests = ( + questions: Message["questions"], +): Message["questions"] => + Array.isArray(questions) + ? questions.map((request) => ({ + ...request, + questions: Array.isArray(request.questions) + ? request.questions.map((question) => ({ + ...question, + header: typeof question.header === "string" ? question.header : "", + question: + typeof question.question === "string" ? question.question : "", + options: Array.isArray(question.options) + ? question.options.map((option) => ({ + label: + typeof option.label === "string" ? option.label : "", + description: + typeof option.description === "string" + ? option.description + : "", + })) + : [], + })) + : [], + answers: Array.isArray(request.answers) + ? request.answers.map((answer) => + Array.isArray(answer) + ? answer.filter((item): item is string => typeof item === "string") + : [], + ) + : undefined, + } satisfies AgentQuestionRequest)) + : undefined; + +const normalizeTodoUpdate = (todos: Message["todos"]): Message["todos"] => { + if (!todos) return undefined; + return { + ...todos, + todos: Array.isArray(todos.todos) + ? todos.todos.map((todo) => ({ ...todo })) + : [], + } satisfies AgentTodoUpdate; +}; + export const cloneMessage = (message: Message): Message => ({ ...message, - progress: message.progress ? [...message.progress] : undefined, - artifacts: message.artifacts ? [...message.artifacts] : undefined, + progress: Array.isArray(message.progress) ? [...message.progress] : undefined, + artifacts: Array.isArray(message.artifacts) ? [...message.artifacts] : undefined, + permissions: Array.isArray(message.permissions) + ? message.permissions.map((permission) => ({ + ...permission, + patterns: Array.isArray(permission.patterns) + ? [...permission.patterns] + : [], + always: Array.isArray(permission.always) ? [...permission.always] : [], + metadata: permission.metadata ?? {}, + })) + : undefined, + questions: normalizeQuestionRequests(message.questions), + todos: normalizeTodoUpdate(message.todos), }); export const cloneMessages = (messages: Message[]) => messages.map(cloneMessage);