Skip to content

Commit bc16d6a

Browse files
Merge pull request #446 from cloudflare/fix/remove-sanitize-tool-call-id
fix(workers-ai-provider): remove tool_call_id sanitization
2 parents 76e5b0c + 3c35051 commit bc16d6a

3 files changed

Lines changed: 23 additions & 74 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"workers-ai-provider": patch
3+
---
4+
5+
Remove tool_call_id sanitization that truncated IDs to 9 alphanumeric chars, which caused all tool call IDs to collide after round-trip

packages/workers-ai-provider/src/utils.ts

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,11 @@ import type { WorkersAIChatPrompt } from "./workersai-chat-prompt";
66
// Workers AI quirk workarounds
77
// ---------------------------------------------------------------------------
88

9-
/**
10-
* Strip non-alphanumeric characters and ensure the ID is exactly 9 chars,
11-
* matching Workers AI binding's `[a-zA-Z0-9]{9}` validation pattern.
12-
*
13-
* The Workers AI binding validates `tool_call_id` with a strict regex, but
14-
* generates IDs like `chatcmpl-tool-875d3ec6179676ae` (with dashes, >9 chars).
15-
* Those IDs are rejected when sent back in a follow-up request.
16-
*
17-
* Once Workers AI fixes the validation, this becomes an idempotent no-op for
18-
* IDs that already match the pattern.
19-
*/
20-
export function sanitizeToolCallId(id: string): string {
21-
const alphanumeric = id.replace(/[^a-zA-Z0-9]/g, "");
22-
return alphanumeric.slice(0, 9).padEnd(9, "0");
23-
}
24-
259
/**
2610
* Normalize messages before passing to the Workers AI binding.
2711
*
2812
* The binding has strict schema validation that differs from the OpenAI API:
2913
* - `content` must be a string (not null)
30-
* - `tool_call_id` must match `[a-zA-Z0-9]{9}` pattern
31-
*
32-
* This patches fields so the full tool-call round-trip works even though
33-
* the binding's own generated IDs may not pass its own validation.
3414
*/
3515
export function normalizeMessagesForBinding(messages: WorkersAIChatPrompt): WorkersAIChatPrompt {
3616
return messages.map((msg) => {
@@ -41,19 +21,6 @@ export function normalizeMessagesForBinding(messages: WorkersAIChatPrompt): Work
4121
(normalized as { content: string }).content = "";
4222
}
4323

44-
// Normalize tool_call_id on tool messages
45-
if ("tool_call_id" in normalized && typeof normalized.tool_call_id === "string") {
46-
normalized.tool_call_id = sanitizeToolCallId(normalized.tool_call_id);
47-
}
48-
49-
// Normalize tool_calls[].id on assistant messages
50-
if ("tool_calls" in normalized && Array.isArray(normalized.tool_calls)) {
51-
normalized.tool_calls = normalized.tool_calls.map((tc) => ({
52-
...tc,
53-
id: sanitizeToolCallId(tc.id),
54-
}));
55-
}
56-
5724
return normalized;
5825
});
5926
}

packages/workers-ai-provider/test/utils.test.ts

Lines changed: 18 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,42 +3,11 @@ import {
33
processPartialToolCalls,
44
processToolCalls,
55
processText,
6-
sanitizeToolCallId,
76
normalizeMessagesForBinding,
87
prepareToolsAndToolChoice,
98
createRun,
109
} from "../src/utils";
1110

12-
// ---------------------------------------------------------------------------
13-
// sanitizeToolCallId
14-
// ---------------------------------------------------------------------------
15-
16-
describe("sanitizeToolCallId", () => {
17-
it("should strip non-alphanumeric characters and truncate to 9 chars", () => {
18-
expect(sanitizeToolCallId("chatcmpl-tool-875d3ec6179676ae")).toBe("chatcmplt");
19-
});
20-
21-
it("should pad short IDs with zeros", () => {
22-
expect(sanitizeToolCallId("abc")).toBe("abc000000");
23-
});
24-
25-
it("should pass through already-valid 9-char alphanumeric IDs", () => {
26-
expect(sanitizeToolCallId("abcdef123")).toBe("abcdef123");
27-
});
28-
29-
it("should handle empty string", () => {
30-
expect(sanitizeToolCallId("")).toBe("000000000");
31-
});
32-
33-
it("should handle IDs with only special characters", () => {
34-
expect(sanitizeToolCallId("---!!!---")).toBe("000000000");
35-
});
36-
37-
it("should handle mixed content", () => {
38-
expect(sanitizeToolCallId("call_abc_123")).toBe("callabc12");
39-
});
40-
});
41-
4211
// ---------------------------------------------------------------------------
4312
// normalizeMessagesForBinding
4413
// ---------------------------------------------------------------------------
@@ -53,7 +22,18 @@ describe("normalizeMessagesForBinding", () => {
5322
expect(result).toEqual(messages);
5423
});
5524

56-
it("should sanitize tool_call_id on tool messages", () => {
25+
it("should convert null content to empty string", () => {
26+
const messages = [
27+
{
28+
role: "assistant" as const,
29+
content: null as unknown as string,
30+
},
31+
];
32+
const result = normalizeMessagesForBinding(messages);
33+
expect(result[0]).toHaveProperty("content", "");
34+
});
35+
36+
it("should pass through tool_call_id unchanged", () => {
5737
const messages = [
5838
{
5939
role: "tool" as const,
@@ -63,10 +43,10 @@ describe("normalizeMessagesForBinding", () => {
6343
},
6444
];
6545
const result = normalizeMessagesForBinding(messages);
66-
expect(result[0]).toHaveProperty("tool_call_id", "chatcmplt");
46+
expect(result[0]).toHaveProperty("tool_call_id", "chatcmpl-tool-875d3ec6179676ae");
6747
});
6848

69-
it("should sanitize tool_calls[].id on assistant messages", () => {
49+
it("should pass through tool_calls[].id unchanged", () => {
7050
const messages = [
7151
{
7252
role: "assistant" as const,
@@ -84,21 +64,18 @@ describe("normalizeMessagesForBinding", () => {
8464
const assistant = result[0] as {
8565
tool_calls?: Array<{ id: string }>;
8666
};
87-
expect(assistant.tool_calls?.[0].id).toBe("chatcmplt");
67+
expect(assistant.tool_calls?.[0].id).toBe("chatcmpl-tool-abc123def456");
8868
});
8969

9070
it("should not mutate the original messages array", () => {
9171
const original = [
9272
{
93-
role: "tool" as const,
94-
name: "fn",
95-
content: "result",
96-
tool_call_id: "chatcmpl-tool-abc",
73+
role: "assistant" as const,
74+
content: null as unknown as string,
9775
},
9876
];
99-
const originalId = original[0].tool_call_id;
10077
normalizeMessagesForBinding(original);
101-
expect(original[0].tool_call_id).toBe(originalId);
78+
expect(original[0].content).toBeNull();
10279
});
10380
});
10481

0 commit comments

Comments
 (0)