fix(swads): make daily report generation resilient
This commit is contained in:
@@ -102,11 +102,9 @@ export class HarnessClientStore {
|
||||
} else if (event.type === "assistant.completed") {
|
||||
messages = updateAssistant(messages, event.payload.messageId, event.timestamp, (parts) => parts, { type: "complete", reason: "stop" }, event.runId);
|
||||
} else if (event.type === "report.generated") {
|
||||
const { artifact, assistantMessageId } = event.payload;
|
||||
if (artifact.previewUrl) {
|
||||
messages = updateAssistant(messages, assistantMessageId, event.timestamp, (parts) => parts, { type: "running" }, event.runId);
|
||||
void this.loadReportImage(event);
|
||||
}
|
||||
const { artifact, assistantMessageId, markdown } = event.payload;
|
||||
messages = updateAssistant(messages, assistantMessageId, event.timestamp, (parts) => markdown ? insertReportMarkdown(parts, markdown) : parts, { type: "running" }, event.runId);
|
||||
if (artifact.previewUrl) void this.loadReportImage(event);
|
||||
} else if (event.type === "tool.requested") {
|
||||
const id = assistantIdForRun(messages, event.runId);
|
||||
messages = updateAssistant(messages, id, event.timestamp, (parts) => upsertToolCall(parts, event.payload.toolCallId, event.payload.toolName, asArgs(event.payload.arguments)), { type: "running" }, event.runId);
|
||||
@@ -243,6 +241,11 @@ function updateAssistant(messages: ThreadMessageLike[], id: string, timestamp: s
|
||||
return messages.map((item, current) => current === index ? { ...item, id, content: change(typeof item.content === "string" ? [{ type: "text", text: item.content }] : item.content), status } : item);
|
||||
}
|
||||
function appendText(parts: ContentParts, delta: string): ContentParts { const last = parts.at(-1); return last?.type === "text" ? [...parts.slice(0, -1), { ...last, text: last.text + delta }] : [...parts, { type: "text", text: delta }]; }
|
||||
function insertReportMarkdown(parts: ContentParts, markdown: string): ContentParts {
|
||||
if (parts.some((part) => part.type === "text" && part.text.trim())) return parts;
|
||||
const renderIndex = parts.findIndex((part) => part.type === "tool-call" && part.toolName === "render_swads_daily_report");
|
||||
return renderIndex < 0 ? [...parts, { type: "text", text: markdown }] : [...parts.slice(0, renderIndex), { type: "text", text: markdown }, ...parts.slice(renderIndex)];
|
||||
}
|
||||
function upsertToolCall(parts: ContentParts, toolCallId: string, toolName: string, args: Record<string, string | number | boolean | null>): ContentParts {
|
||||
const index = parts.findIndex((part) => part.type === "tool-call" && part.toolCallId === toolCallId);
|
||||
if (index < 0) return [...parts, { type: "tool-call", toolCallId, toolName, args }];
|
||||
|
||||
@@ -48,6 +48,14 @@ describe("HarnessClientStore reducer", () => {
|
||||
});
|
||||
|
||||
describe("generated report events", () => {
|
||||
it("adds a generated Markdown fallback when the model returned no visible report text", () => {
|
||||
const store = new HarnessClientStore(); const base = { sessionId: "ses_report", runId: "run_report", timestamp: new Date().toISOString() };
|
||||
const accountKey = "acct_0123456789abcdef01234567", reportDate = "2026-09-06", reportId = "12345678-1234-4234-8234-123456789abc";
|
||||
const url = `/api/reports/${accountKey}/${reportDate}/${reportId}`; const artifact = { accountKey, reportDate, reportId, downloads: { json: `${url}/json`, html: `${url}/html` } };
|
||||
store.reduce({ ...base, eventId: "fallback_1", sequence: 1, type: "tool.requested", payload: { toolCallId: "render", toolName: "render_swads_daily_report", arguments: {} } });
|
||||
store.reduce({ ...base, eventId: "fallback_2", sequence: 2, type: "report.generated", payload: { assistantMessageId: "assistant", artifact, markdown: "## 自动日报摘要" } });
|
||||
const content = store.getSnapshot().messages[0]?.content; expect(content).toEqual(expect.arrayContaining([{ type: "text", text: "## 自动日报摘要" }]));
|
||||
});
|
||||
it("deduplicates images and merges provisional tool messages with the correct assistant", async () => {
|
||||
vi.stubGlobal("fetch", vi.fn(async () => new Response(new Blob(["png"], { type: "image/png" }), { headers: { "content-type": "image/png" } })));
|
||||
Object.defineProperty(URL, "createObjectURL", { configurable: true, value: vi.fn(() => "blob:report-image") });
|
||||
|
||||
Reference in New Issue
Block a user