co-mono/packages/ai/test/cross-provider-toolcall.test.ts
Mario Zechner 6679a83b32 fix(ai): Sanitize tool call IDs for Anthropic API compatibility
- Anthropic API requires tool call IDs to match pattern ^[a-zA-Z0-9_-]+$
- OpenAI Responses API generates IDs with pipe character (|) which breaks Anthropic
- Added sanitizeToolCallId() to replace invalid characters with underscores
- Fixes cross-provider handoffs from OpenAI Responses to Anthropic
- Added test to verify the fix works
2025-09-04 05:17:08 +02:00

113 lines
3.7 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { type Context, complete, getModel } from "../src/index.js";
async function testCrossProviderToolCall() {
console.log("Testing cross-provider tool call handoff...\n");
// Define a simple tool
const tools = [
{
name: "get_weather",
description: "Get current weather for a location",
parameters: {
type: "object",
properties: {
location: { type: "string", description: "City name" },
},
required: ["location"],
},
},
];
// Create context with tools
const context: Context = {
systemPrompt: "You are a helpful assistant. Use the get_weather tool when asked about weather.",
messages: [{ role: "user", content: "What is the weather in Paris?" }],
tools,
};
try {
// Step 1: Get tool call from GPT-5
console.log("Step 1: Getting tool call from GPT-5...");
const gpt5 = getModel("openai", "gpt-5-mini");
const gpt5Response = await complete(gpt5, context);
context.messages.push(gpt5Response);
// Check for tool calls
const toolCalls = gpt5Response.content.filter((b) => b.type === "toolCall");
console.log(`GPT-5 made ${toolCalls.length} tool call(s)`);
if (toolCalls.length > 0) {
const toolCall = toolCalls[0];
console.log(`Tool call ID: ${toolCall.id}`);
console.log(`Tool call contains pipe: ${toolCall.id.includes("|")}`);
console.log(`Tool: ${toolCall.name}(${JSON.stringify(toolCall.arguments)})\n`);
// Add tool result
context.messages.push({
role: "toolResult",
toolCallId: toolCall.id,
toolName: toolCall.name,
content: JSON.stringify({
location: "Paris",
temperature: "22°C",
conditions: "Partly cloudy",
}),
isError: false,
});
// Step 2: Send to Claude Haiku for follow-up
console.log("Step 2: Sending to Claude Haiku for follow-up...");
const haiku = getModel("anthropic", "claude-3-5-haiku-20241022");
try {
const haikuResponse = await complete(haiku, context);
console.log("✅ Claude Haiku successfully processed the conversation!");
console.log("Response content types:", haikuResponse.content.map((b) => b.type).join(", "));
console.log("Number of content blocks:", haikuResponse.content.length);
console.log("Stop reason:", haikuResponse.stopReason);
if (haikuResponse.error) {
console.log("Error message:", haikuResponse.error);
}
// Print all response content
for (const block of haikuResponse.content) {
if (block.type === "text") {
console.log("\nClaude text response:", block.text);
} else if (block.type === "thinking") {
console.log("\nClaude thinking:", block.thinking);
} else if (block.type === "toolCall") {
console.log("\nClaude tool call:", block.name, block.arguments);
}
}
if (haikuResponse.content.length === 0) {
console.log("⚠️ Claude returned an empty response!");
}
} catch (error) {
console.error("❌ Claude Haiku failed to process the conversation:");
console.error("Error:", error);
// Check if it's related to the tool call ID
if (error instanceof Error && error.message.includes("tool")) {
console.error("\n⚠ This appears to be a tool call ID issue!");
console.error("The pipe character (|) in OpenAI Response API tool IDs might be causing problems.");
}
}
} else {
console.log("No tool calls were made by GPT-5");
}
} catch (error) {
console.error("Test failed:", error);
}
}
// Set API keys from environment or pass them explicitly
const openaiKey = process.env.OPENAI_API_KEY;
const anthropicKey = process.env.ANTHROPIC_API_KEY;
if (!openaiKey || !anthropicKey) {
console.error("Please set OPENAI_API_KEY and ANTHROPIC_API_KEY environment variables");
process.exit(1);
}
testCrossProviderToolCall().catch(console.error);