Skip to content

Commit 7aa1152

Browse files
committed
fixes
1 parent dec8fd7 commit 7aa1152

File tree

3 files changed

+47
-67
lines changed

3 files changed

+47
-67
lines changed

packages/types/src/providers/gemini.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export const geminiModels = {
1010
maxTokens: 65_536,
1111
contextWindow: 1_048_576,
1212
supportsImages: true,
13+
supportsNativeTools: true,
1314
supportsPromptCache: true,
1415
supportsReasoningEffort: ["low", "high"],
1516
reasoningEffort: "low",

src/api/transform/__tests__/gemini-format.spec.ts

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -124,13 +124,10 @@ describe("convertAnthropicMessageToGemini", () => {
124124
const result = convertAnthropicMessageToGemini(anthropicMessage)
125125

126126
expect(result).toEqual([
127-
{
128-
role: "model",
129-
parts: [{ text: "Let me calculate that for you." }],
130-
},
131127
{
132128
role: "model",
133129
parts: [
130+
{ text: "Let me calculate that for you." },
134131
{
135132
functionCall: {
136133
name: "calculator",
@@ -159,13 +156,10 @@ describe("convertAnthropicMessageToGemini", () => {
159156
const result = convertAnthropicMessageToGemini(anthropicMessage)
160157

161158
expect(result).toEqual([
162-
{
163-
role: "user",
164-
parts: [{ text: "Here's the result:" }],
165-
},
166159
{
167160
role: "user",
168161
parts: [
162+
{ text: "Here's the result:" },
169163
{
170164
functionResponse: {
171165
name: "calculator",
@@ -195,12 +189,7 @@ describe("convertAnthropicMessageToGemini", () => {
195189
const result = convertAnthropicMessageToGemini(anthropicMessage)
196190

197191
// Should skip the empty tool result
198-
expect(result).toEqual([
199-
{
200-
role: "user",
201-
parts: [],
202-
},
203-
])
192+
expect(result).toEqual([])
204193
})
205194

206195
it("should convert a message with tool result as array with text only", () => {
@@ -347,6 +336,38 @@ describe("convertAnthropicMessageToGemini", () => {
347336
])
348337
})
349338

339+
it("should handle tool names with hyphens", () => {
340+
const anthropicMessage: Anthropic.Messages.MessageParam = {
341+
role: "user",
342+
content: [
343+
{
344+
type: "tool_result",
345+
tool_use_id: "search-files-123",
346+
content: "found files",
347+
},
348+
],
349+
}
350+
351+
const result = convertAnthropicMessageToGemini(anthropicMessage)
352+
353+
expect(result).toEqual([
354+
{
355+
role: "user",
356+
parts: [
357+
{
358+
functionResponse: {
359+
name: "search-files",
360+
response: {
361+
name: "search-files",
362+
content: "found files",
363+
},
364+
},
365+
},
366+
],
367+
},
368+
])
369+
})
370+
350371
it("should throw an error for unsupported content block type", () => {
351372
const anthropicMessage: Anthropic.Messages.MessageParam = {
352373
role: "user",

src/api/transform/gemini-format.ts

Lines changed: 11 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ export function convertAnthropicContentToGemini(
7979
}
8080

8181
// Extract tool name from tool_use_id (e.g., "calculator-123" -> "calculator")
82-
const toolName = block.tool_use_id.split("-")[0]
82+
const lastHyphenIndex = block.tool_use_id.lastIndexOf("-")
83+
const toolName = lastHyphenIndex >= 0 ? block.tool_use_id.slice(0, lastHyphenIndex) : block.tool_use_id
8384

8485
if (typeof block.content === "string") {
8586
return {
@@ -124,59 +125,16 @@ export function convertAnthropicMessageToGemini(
124125
message: Anthropic.Messages.MessageParam,
125126
options?: { includeThoughtSignatures?: boolean },
126127
): Content[] {
127-
const content: ExtendedContentBlockParam[] = Array.isArray(message.content)
128-
? (message.content as ExtendedContentBlockParam[])
129-
: [{ type: "text", text: message.content ?? "" }]
130-
131-
const toolUseParts = content.filter((block) => block.type === "tool_use") as Anthropic.ToolUseBlock[]
132-
const toolResultParts = content.filter((block) => block.type === "tool_result") as Anthropic.ToolResultBlockParam[]
133-
const otherParts = content.filter((block) => block.type !== "tool_use" && block.type !== "tool_result")
134-
const thoughtSignatureBlocks = content.filter((block) => isThoughtSignatureContentBlock(block))
135-
const role = message.role === "assistant" ? "model" : "user"
136-
const assistantThoughtOptions = {
137-
...options,
138-
includeThoughtSignatures: message.role === "assistant" ? options?.includeThoughtSignatures ?? true : false,
139-
}
140-
141-
// Gemini expects a flat list of Content objects. We group regular message parts,
142-
// tool uses, and tool results into separate Content entries while preserving their
143-
// relative order within each category.
144-
const contents: Content[] = []
145-
146-
if (otherParts.length > 0) {
147-
contents.push({
148-
role,
149-
parts: convertAnthropicContentToGemini(otherParts, assistantThoughtOptions),
150-
})
151-
}
152-
153-
if (toolUseParts.length > 0) {
154-
const toolUseWithSignatures =
155-
thoughtSignatureBlocks.length > 0 ? [...thoughtSignatureBlocks, ...toolUseParts] : toolUseParts
156-
contents.push({
157-
role: "model",
158-
parts: convertAnthropicContentToGemini(toolUseWithSignatures, {
159-
...options,
160-
includeThoughtSignatures: options?.includeThoughtSignatures ?? true,
161-
}),
162-
})
163-
}
164-
165-
if (toolResultParts.length > 0) {
166-
contents.push({
167-
role: "user",
168-
parts: convertAnthropicContentToGemini(toolResultParts, { ...options, includeThoughtSignatures: false }),
169-
})
170-
}
128+
const parts = convertAnthropicContentToGemini(message.content, options)
171129

172-
if (contents.length === 0) {
173-
return [
174-
{
175-
role,
176-
parts: convertAnthropicContentToGemini(message.content ?? "", assistantThoughtOptions),
177-
},
178-
]
130+
if (parts.length === 0) {
131+
return []
179132
}
180133

181-
return contents
134+
return [
135+
{
136+
role: message.role === "assistant" ? "model" : "user",
137+
parts,
138+
},
139+
]
182140
}

0 commit comments

Comments
 (0)