mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 05:02:07 +00:00
- 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
113 lines
3.7 KiB
TypeScript
113 lines
3.7 KiB
TypeScript
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);
|