Skip to content

Commit c8f7bf4

Browse files
committed
fix: improve tool name resolution for gemini provider
1 parent 7aa1152 commit c8f7bf4

File tree

2 files changed

+27
-6
lines changed

2 files changed

+27
-6
lines changed

src/api/providers/gemini.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,22 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl
103103
return true
104104
})
105105

106+
// Build a map of tool IDs to names from previous messages
107+
// This is needed because Anthropic's tool_result blocks only contain the ID,
108+
// but Gemini requires the name in functionResponse
109+
const toolIdToName = new Map<string, string>()
110+
for (const message of messages) {
111+
if (Array.isArray(message.content)) {
112+
for (const block of message.content) {
113+
if (block.type === "tool_use") {
114+
toolIdToName.set(block.id, block.name)
115+
}
116+
}
117+
}
118+
}
119+
106120
const contents = geminiMessages
107-
.map((message) => convertAnthropicMessageToGemini(message, { includeThoughtSignatures }))
121+
.map((message) => convertAnthropicMessageToGemini(message, { includeThoughtSignatures, toolIdToName }))
108122
.flat()
109123

110124
const tools: GenerateContentConfig["tools"] = []

src/api/transform/gemini-format.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ function isThoughtSignatureContentBlock(block: ExtendedContentBlockParam): block
1515

1616
export function convertAnthropicContentToGemini(
1717
content: ExtendedAnthropicContent,
18-
options?: { includeThoughtSignatures?: boolean },
18+
options?: { includeThoughtSignatures?: boolean; toolIdToName?: Map<string, string> },
1919
): Part[] {
2020
const includeThoughtSignatures = options?.includeThoughtSignatures ?? true
21+
const toolIdToName = options?.toolIdToName
2122

2223
// First pass: find thoughtSignature if it exists in the content blocks
2324
let activeThoughtSignature: string | undefined
@@ -78,9 +79,15 @@ export function convertAnthropicContentToGemini(
7879
return []
7980
}
8081

81-
// Extract tool name from tool_use_id (e.g., "calculator-123" -> "calculator")
82-
const lastHyphenIndex = block.tool_use_id.lastIndexOf("-")
83-
const toolName = lastHyphenIndex >= 0 ? block.tool_use_id.slice(0, lastHyphenIndex) : block.tool_use_id
82+
// Extract tool name from tool_use_id
83+
// 1. Try to look up the name from the provided map (reliable source)
84+
// 2. Fallback: Extract from ID if it follows "name-counter" format (heuristic)
85+
// 3. Fallback: Use ID as name (likely to fail validation but better than crashing)
86+
let toolName = toolIdToName?.get(block.tool_use_id)
87+
if (!toolName) {
88+
const lastHyphenIndex = block.tool_use_id.lastIndexOf("-")
89+
toolName = lastHyphenIndex >= 0 ? block.tool_use_id.slice(0, lastHyphenIndex) : block.tool_use_id
90+
}
8491

8592
if (typeof block.content === "string") {
8693
return {
@@ -123,7 +130,7 @@ export function convertAnthropicContentToGemini(
123130

124131
export function convertAnthropicMessageToGemini(
125132
message: Anthropic.Messages.MessageParam,
126-
options?: { includeThoughtSignatures?: boolean },
133+
options?: { includeThoughtSignatures?: boolean; toolIdToName?: Map<string, string> },
127134
): Content[] {
128135
const parts = convertAnthropicContentToGemini(message.content, options)
129136

0 commit comments

Comments
 (0)