fix(ai): drop streaming parse internals before persistence
Addresses review feedback on #1507.
This commit is contained in:
@@ -1234,6 +1234,8 @@ function handleToolCallArgumentsDone(
|
||||
if (typeof args === "string") {
|
||||
currentBlock.partialJson = args;
|
||||
currentBlock.arguments = parseStreamingJson(currentBlock.partialJson);
|
||||
delete (currentBlock as { partialJson?: string }).partialJson;
|
||||
delete (currentBlock as { lastParseLen?: number }).lastParseLen;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1312,6 +1314,10 @@ function handleOutputItemDone(
|
||||
name: item.name,
|
||||
arguments: parseStreamingJson(item.arguments || "{}"),
|
||||
};
|
||||
if (runtime.currentBlock?.type === "toolCall") {
|
||||
delete (runtime.currentBlock as { partialJson?: string }).partialJson;
|
||||
delete (runtime.currentBlock as { lastParseLen?: number }).lastParseLen;
|
||||
}
|
||||
runtime.canSafelyReplayWebsocketOverSse = false;
|
||||
stream.push({ type: "toolcall_end", contentIndex: blockIndex(), toolCall, partial: output });
|
||||
return;
|
||||
|
||||
@@ -560,6 +560,8 @@ export async function processResponsesStream<TApi extends Api>(
|
||||
if (currentItem?.type === "function_call" && currentBlock?.type === "toolCall") {
|
||||
currentBlock.partialJson = event.arguments;
|
||||
currentBlock.arguments = parseStreamingJson(currentBlock.partialJson);
|
||||
delete (currentBlock as { partialJson?: string }).partialJson;
|
||||
delete (currentBlock as { lastParseLen?: number }).lastParseLen;
|
||||
}
|
||||
} else if (event.type === "response.custom_tool_call_input.delta") {
|
||||
if (currentItem?.type === "custom_tool_call" && currentBlock?.type === "toolCall") {
|
||||
@@ -625,6 +627,10 @@ export async function processResponsesStream<TApi extends Api>(
|
||||
name: item.name,
|
||||
arguments: args,
|
||||
};
|
||||
if (currentBlock?.type === "toolCall") {
|
||||
delete (currentBlock as { partialJson?: string }).partialJson;
|
||||
delete (currentBlock as { lastParseLen?: number }).lastParseLen;
|
||||
}
|
||||
currentBlock = null;
|
||||
stream.push({ type: "toolcall_end", contentIndex: blockIndex(), toolCall, partial: output });
|
||||
} else if (item.type === "custom_tool_call") {
|
||||
|
||||
@@ -225,6 +225,56 @@ describe("custom_tool_call stream receive", () => {
|
||||
for (const e of events) yield e as ResponseStreamEvent;
|
||||
}
|
||||
|
||||
test("strips streaming parse bookkeeping from function-call output blocks", async () => {
|
||||
const output: AssistantMessage = {
|
||||
role: "assistant",
|
||||
content: [],
|
||||
timestamp: Date.now(),
|
||||
provider: "openai",
|
||||
model: "gpt-5",
|
||||
api: "openai-responses",
|
||||
usage: {
|
||||
input: 0,
|
||||
output: 0,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
totalTokens: 0,
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
||||
},
|
||||
stopReason: "stop",
|
||||
};
|
||||
const emitted: unknown[] = [];
|
||||
const stream = {
|
||||
push: (e: unknown) => emitted.push(e),
|
||||
end: () => {},
|
||||
} as never;
|
||||
const args = JSON.stringify({ command: "x".repeat(300) });
|
||||
|
||||
await processResponsesStream(
|
||||
makeStream([
|
||||
{
|
||||
type: "response.output_item.added",
|
||||
item: { type: "function_call", id: "fc_1", call_id: "call_1", name: "bash", arguments: "" },
|
||||
},
|
||||
{ type: "response.function_call_arguments.delta", delta: args },
|
||||
{ type: "response.function_call_arguments.done", arguments: args },
|
||||
{
|
||||
type: "response.output_item.done",
|
||||
item: { type: "function_call", id: "fc_1", call_id: "call_1", name: "bash", arguments: args },
|
||||
},
|
||||
]),
|
||||
output,
|
||||
stream,
|
||||
makeModel(),
|
||||
);
|
||||
|
||||
const block = output.content[0] as Record<string, unknown>;
|
||||
expect(block.type).toBe("toolCall");
|
||||
expect(block.arguments).toEqual({ command: "x".repeat(300) });
|
||||
expect("partialJson" in block).toBe(false);
|
||||
expect("lastParseLen" in block).toBe(false);
|
||||
});
|
||||
|
||||
test("aggregates delta events into a ToolCall with input arg", async () => {
|
||||
const output: AssistantMessage = {
|
||||
role: "assistant",
|
||||
|
||||
Reference in New Issue
Block a user