diff --git a/src/extra/observability/streaming.ts b/src/extra/observability/streaming.ts index d9ec7dc3..b4c8f785 100644 --- a/src/extra/observability/streaming.ts +++ b/src/extra/observability/streaming.ts @@ -15,6 +15,7 @@ import { CompletionChunk, completionChunkFromJSON, } from "../../models/components/completionchunk.js"; +import { ContentChunk } from "../../models/components/contentchunk.js"; import { UsageInfo, UsageInfo$outboundSchema, @@ -125,9 +126,7 @@ export function accumulateChunksToResponseDict( if (typeof delta.role === "string") { msg.role = delta.role; } - if (typeof delta.content === "string" && delta.content) { - msg.content += delta.content; - } + msg.content += extractOutputText(delta.content); if (typeof choice.finishReason === "string") { accumulated.finish_reason = choice.finishReason; } @@ -172,3 +171,18 @@ export function accumulateChunksToResponseDict( } return result; } + +function extractOutputText( + content: string | Array | null | undefined +): string { + if (typeof content === "string") { + return content; + } + if (!Array.isArray(content)) { + return ""; + } + return content + .filter((block): block is ContentChunk & { type: "text" } => block.type === "text") + .map((block) => block.text) + .join(""); +} diff --git a/tests/extra/observability/streaming.test.ts b/tests/extra/observability/streaming.test.ts index f97d1e29..943e3a96 100644 --- a/tests/extra/observability/streaming.test.ts +++ b/tests/extra/observability/streaming.test.ts @@ -88,6 +88,30 @@ describe("accumulateChunksToResponseDict", () => { ]); }); + test("accumulates content when delta.content is an array of ContentChunk objects", () => { + const arrayContentChunk: Record = { + id: "id-1", + model: "m", + choices: [{ + index: 0, + delta: { + role: "assistant", + content: [{ type: "text", text: "Bonjour" }], + }, + finish_reason: null, + }], + }; + const chunks = parseSseChunks(toSse([ + arrayContentChunk, + chunk({ content: " monde", finishReason: "stop" }), + ])); + const result = accumulateChunksToResponseDict(chunks); + expect(result.choices).toEqual([{ + message: { role: "assistant", content: "Bonjour monde" }, + finish_reason: "stop", + }]); + }); + test("handles empty chunks", () => { expect(accumulateChunksToResponseDict([])).toEqual({ id: undefined, model: undefined, choices: [] }); });