fix(ai): default Gemini function args (#43111)

This commit is contained in:
Aiden Cline
2026-08-17 13:43:52 -05:00
committed by GitHub
parent 3d13b6c5b6
commit 43dd33842e
2 changed files with 27 additions and 2 deletions
+2 -2
View File
@@ -92,7 +92,7 @@ const GeminiFunctionCallPart = Schema.Struct({
functionCall: Schema.Struct({
id: Schema.optional(Schema.String),
name: Schema.String,
args: Schema.Unknown,
args: Schema.optional(Schema.Unknown),
}),
thoughtSignature: Schema.optional(Schema.String),
})
@@ -586,7 +586,7 @@ const step = (state: ParserState, event: GeminiEvent) => {
}
if ("functionCall" in part) {
const input = part.functionCall.args
const input = part.functionCall.args === undefined ? {} : part.functionCall.args
const id = `tool_${nextToolCallId++}`
const metadata = {
...(part.functionCall.id === undefined ? {} : { functionCallId: part.functionCall.id }),
+25
View File
@@ -767,6 +767,31 @@ describe("Gemini route", () => {
}),
)
it.effect("defaults omitted function call args to an empty object", () =>
Effect.gen(function* () {
const response = yield* LLMClient.generate(
LLMRequest.update(request, {
tools: [ToolDefinition.make({ name: "ping", description: "Ping", inputSchema: { type: "object" } })],
}),
).pipe(
Effect.provide(
fixedResponse(
sseEvents({
candidates: [
{
content: { role: "model", parts: [{ functionCall: { name: "ping" } }] },
finishReason: "STOP",
},
],
}),
),
),
)
expect(response.toolCalls).toEqual([{ type: "tool-call", id: "tool_0", name: "ping", input: {} }])
}),
)
it.effect("maps tool calls without a finish reason", () =>
Effect.gen(function* () {
const response = yield* LLMClient.generate(