fix(chat): normalize loaded messages
Build Push and Deploy / docker-image (push) Successful in 1m34s
Build Push and Deploy / deploy-fallback-log (push) Has been skipped

This commit is contained in:
2026-06-08 19:47:13 +08:00
parent e5f13c3d46
commit 166b45e529
2 changed files with 97 additions and 2 deletions
@@ -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([]);
});
});
+62 -2
View File
@@ -1,4 +1,8 @@
import type { Message } from "./GlobalChatbox.types"; import type { Message } from "./GlobalChatbox.types";
import type {
AgentQuestionRequest,
AgentTodoUpdate,
} from "@/lib/chatStream";
export const createId = () => export const createId = () =>
`${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
@@ -29,10 +33,66 @@ export const stripMarkdown = (md: string): string =>
.replace(/<[^>]+>/g, "") .replace(/<[^>]+>/g, "")
.trim(); .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 => ({ export const cloneMessage = (message: Message): Message => ({
...message, ...message,
progress: message.progress ? [...message.progress] : undefined, progress: Array.isArray(message.progress) ? [...message.progress] : undefined,
artifacts: message.artifacts ? [...message.artifacts] : 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); export const cloneMessages = (messages: Message[]) => messages.map(cloneMessage);