



























@@ -83,63 +83,94 @@ describe("shouldRepairMalformedToolCallArguments", () => {
8383}),
8484).toBe(false);
8585});
86+87+it("does not enable the repair for direct OpenAI responses", () => {
88+expect(
89+shouldRepairMalformedToolCallArguments({
90+provider: "openai",
91+modelApi: "openai-responses",
92+}),
93+).toBe(false);
94+});
95+96+it("enables the repair for Codex and Azure Responses transports", () => {
97+expect(
98+shouldRepairMalformedToolCallArguments({
99+provider: "openai-codex",
100+modelApi: "openai-codex-responses",
101+}),
102+).toBe(true);
103+expect(
104+shouldRepairMalformedToolCallArguments({
105+provider: "azure-openai-responses",
106+modelApi: "azure-openai-responses",
107+}),
108+).toBe(true);
109+});
86110});
8711188112describe("openai-completions malformed tool-call argument repair", () => {
89-it("repairs fragmented OpenAI-compatible function-call args before tool execution", async () => {
90-const partialToolCall = { type: "functionCall", name: "read", arguments: {} };
91-const streamedToolCall = { type: "functionCall", name: "read", arguments: {} };
92-const endMessageToolCall = { type: "functionCall", name: "read", arguments: {} };
93-const finalToolCall = { type: "functionCall", name: "read", arguments: {} };
94-const partialMessage = { role: "assistant", content: [partialToolCall] };
95-const endMessage = { role: "assistant", content: [endMessageToolCall] };
96-const finalMessage = { role: "assistant", content: [finalToolCall] };
113+it.each([
114+["openai-completions", "sglang"],
115+["openai-codex-responses", "openai-codex"],
116+["azure-openai-responses", "azure-openai-responses"],
117+])(
118+"repairs fragmented %s function-call args before tool execution",
119+async (modelApi, provider) => {
120+const partialToolCall = { type: "functionCall", name: "read", arguments: {} };
121+const streamedToolCall = { type: "functionCall", name: "read", arguments: {} };
122+const endMessageToolCall = { type: "functionCall", name: "read", arguments: {} };
123+const finalToolCall = { type: "functionCall", name: "read", arguments: {} };
124+const partialMessage = { role: "assistant", content: [partialToolCall] };
125+const endMessage = { role: "assistant", content: [endMessageToolCall] };
126+const finalMessage = { role: "assistant", content: [finalToolCall] };
9712798-const stream = await invokeProviderStream({
99-provider: "sglang",
100-modelApi: "openai-completions",
101-baseFn: () =>
102-createFakeStream({
103-events: [
104-{
105-type: "toolcall_delta",
106-contentIndex: 0,
107-delta: ".functions.read:0 ",
108-partial: partialMessage,
109-},
110-{
111-type: "toolcall_delta",
112-contentIndex: 0,
113-delta: '{"path":"/tmp/report.txt"',
114-partial: partialMessage,
115-},
116-{
117-type: "toolcall_delta",
118-contentIndex: 0,
119-delta: "}x",
120-partial: partialMessage,
121-},
122-{
123-type: "toolcall_end",
124-contentIndex: 0,
125-toolCall: streamedToolCall,
126-partial: partialMessage,
127-message: endMessage,
128-},
129-],
130-resultMessage: finalMessage,
131-}),
132-});
128+ const stream = await invokeProviderStream({
129+ provider,
130+ modelApi,
131+ baseFn: () =>
132+ createFakeStream({
133+ events: [
134+ {
135+ type: "toolcall_delta",
136+ contentIndex: 0,
137+ delta: ".functions.read:0 ",
138+ partial: partialMessage,
139+ },
140+ {
141+ type: "toolcall_delta",
142+ contentIndex: 0,
143+ delta: '{"path":"/tmp/report.txt"',
144+ partial: partialMessage,
145+ },
146+ {
147+ type: "toolcall_delta",
148+ contentIndex: 0,
149+ delta: "}x",
150+ partial: partialMessage,
151+ },
152+ {
153+ type: "toolcall_end",
154+ contentIndex: 0,
155+ toolCall: streamedToolCall,
156+ partial: partialMessage,
157+ message: endMessage,
158+ },
159+ ],
160+ resultMessage: finalMessage,
161+ }),
162+ });
133163134-for await (const _item of stream) {
135-// drain
136-}
137-const result = await stream.result();
164+ for await (const _item of stream) {
165+ // drain
166+ }
167+ const result = await stream.result();
138168139-expect(partialToolCall.arguments).toEqual({ path: "/tmp/report.txt" });
140-expect(streamedToolCall.arguments).toEqual({ path: "/tmp/report.txt" });
141-expect(endMessageToolCall.arguments).toEqual({ path: "/tmp/report.txt" });
142-expect(finalToolCall.arguments).toEqual({ path: "/tmp/report.txt" });
143-expect(result).toBe(finalMessage);
144-});
169+expect(partialToolCall.arguments).toEqual({ path: "/tmp/report.txt" });
170+expect(streamedToolCall.arguments).toEqual({ path: "/tmp/report.txt" });
171+expect(endMessageToolCall.arguments).toEqual({ path: "/tmp/report.txt" });
172+expect(finalToolCall.arguments).toEqual({ path: "/tmp/report.txt" });
173+expect(result).toBe(finalMessage);
174+},
175+);
145176});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。