Fix gemini message conversion

This commit is contained in:
Saoud Rizwan
2024-09-12 11:49:32 -04:00
parent fbb7620fa1
commit 3b004aed37
5 changed files with 121 additions and 49 deletions

View File

@@ -1,5 +1,15 @@
import { Anthropic } from "@anthropic-ai/sdk"
import { Content, EnhancedGenerateContentResponse, FunctionDeclaration, Part, SchemaType } from "@google/generative-ai"
import {
Content,
EnhancedGenerateContentResponse,
FunctionCallPart,
FunctionDeclaration,
FunctionResponsePart,
InlineDataPart,
Part,
SchemaType,
TextPart,
} from "@google/generative-ai"
export function convertAnthropicContentToGemini(
content:
@@ -12,12 +22,12 @@ export function convertAnthropicContentToGemini(
>
): Part[] {
if (typeof content === "string") {
return [{ text: content }]
return [{ text: content } as TextPart]
}
return content.map((block) => {
return content.flatMap((block) => {
switch (block.type) {
case "text":
return { text: block.text }
return { text: block.text } as TextPart
case "image":
if (block.source.type !== "base64") {
throw new Error("Unsupported image source type")
@@ -27,22 +37,55 @@ export function convertAnthropicContentToGemini(
data: block.source.data,
mimeType: block.source.media_type,
},
}
} as InlineDataPart
case "tool_use":
return {
functionCall: {
name: block.name,
args: block.input,
},
} as Part
} as FunctionCallPart
case "tool_result":
return {
functionResponse: {
name: block.tool_use_id,
response: {
content: block.content,
const name = block.tool_use_id.split("-")[0]
if (!block.content) {
return []
}
if (typeof block.content === "string") {
return {
functionResponse: {
name,
response: {
name,
content: block.content,
},
},
},
} as FunctionResponsePart
} else {
// The only case when tool_result could be array is when the tool failed and we're providing ie user feedback potentially with images
const textParts = block.content.filter((part) => part.type === "text")
const imageParts = block.content.filter((part) => part.type === "image")
const text = textParts.length > 0 ? textParts.map((part) => part.text).join("\n\n") : ""
const imageText = imageParts.length > 0 ? "\n\n(See next part for image)" : ""
return [
{
functionResponse: {
name,
response: {
name,
content: text + imageText,
},
},
} as FunctionResponsePart,
...imageParts.map(
(part) =>
({
inlineData: {
data: part.source.data,
mimeType: part.source.media_type,
},
} as InlineDataPart)
),
]
}
default:
throw new Error(`Unsupported content block type: ${(block as any).type}`)
@@ -52,7 +95,7 @@ export function convertAnthropicContentToGemini(
export function convertAnthropicMessageToGemini(message: Anthropic.Messages.MessageParam): Content {
return {
role: message.role === "assistant" ? "model" : message.role,
role: message.role === "assistant" ? "model" : "user",
parts: convertAnthropicContentToGemini(message.content),
}
}
@@ -77,6 +120,13 @@ export function convertAnthropicToolToGemini(tool: Anthropic.Messages.Tool): Fun
}
}
/*
It looks like gemini likes to double escape certain characters when writing file contents: https://discuss.ai.google.dev/t/function-call-string-property-is-double-escaped/37867
*/
export function unescapeGeminiContent(content: string) {
return content.replace(/\\n/g, "\n").replace(/\\'/g, "'").replace(/\\"/g, '"')
}
export function convertGeminiResponseToAnthropic(
response: EnhancedGenerateContentResponse
): Anthropic.Messages.Message {
@@ -92,9 +142,12 @@ export function convertGeminiResponseToAnthropic(
const functionCalls = response.functionCalls()
if (functionCalls) {
functionCalls.forEach((call, index) => {
if ("content" in call.args && typeof call.args.content === "string") {
call.args.content = unescapeGeminiContent(call.args.content)
}
content.push({
type: "tool_use",
id: `tool_${index}`,
id: `${call.name}-${index}-${Date.now()}`,
name: call.name,
input: call.args,
})