mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-16 12:03:23 +00:00
fix(ai): default tool call arguments to empty object for Google providers
When Google providers return tool calls without an args field (common for
no-argument tools), the arguments field was undefined. This breaks
subsequent API calls that require tool_use.input to be present.
Now defaults to {} when args is missing.
Related: clawdbot/clawdbot#1509
This commit is contained in:
parent
d74fd82673
commit
a6d878e804
4 changed files with 108 additions and 3 deletions
|
|
@ -685,7 +685,7 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli", GoogleGe
|
|||
type: "toolCall",
|
||||
id: toolCallId,
|
||||
name: part.functionCall.name || "",
|
||||
arguments: part.functionCall.args as Record<string, unknown>,
|
||||
arguments: (part.functionCall.args as Record<string, unknown>) ?? {},
|
||||
...(part.thoughtSignature && { thoughtSignature: part.thoughtSignature }),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ export const streamGoogleVertex: StreamFunction<"google-vertex", GoogleVertexOpt
|
|||
type: "toolCall",
|
||||
id: toolCallId,
|
||||
name: part.functionCall.name || "",
|
||||
arguments: part.functionCall.args as Record<string, any>,
|
||||
arguments: (part.functionCall.args as Record<string, any>) ?? {},
|
||||
...(part.thoughtSignature && { thoughtSignature: part.thoughtSignature }),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ export const streamGoogle: StreamFunction<"google-generative-ai", GoogleOptions>
|
|||
type: "toolCall",
|
||||
id: toolCallId,
|
||||
name: part.functionCall.name || "",
|
||||
arguments: part.functionCall.args as Record<string, any>,
|
||||
arguments: (part.functionCall.args as Record<string, any>) ?? {},
|
||||
...(part.thoughtSignature && { thoughtSignature: part.thoughtSignature }),
|
||||
};
|
||||
|
||||
|
|
|
|||
105
packages/ai/test/google-tool-call-missing-args.test.ts
Normal file
105
packages/ai/test/google-tool-call-missing-args.test.ts
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
import { Type } from "@sinclair/typebox";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { streamGoogleGeminiCli } from "../src/providers/google-gemini-cli.js";
|
||||
import type { Context, Model, ToolCall } from "../src/types.js";
|
||||
|
||||
const emptySchema = Type.Object({});
|
||||
|
||||
const originalFetch = global.fetch;
|
||||
|
||||
afterEach(() => {
|
||||
global.fetch = originalFetch;
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe("google providers tool call missing args", () => {
|
||||
it("defaults arguments to empty object when provider omits args field", async () => {
|
||||
// Simulate a tool call response where args is missing (no-arg tool)
|
||||
const sse = `${[
|
||||
`data: ${JSON.stringify({
|
||||
response: {
|
||||
candidates: [
|
||||
{
|
||||
content: {
|
||||
role: "model",
|
||||
parts: [
|
||||
{
|
||||
functionCall: {
|
||||
name: "get_status",
|
||||
// args intentionally omitted
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
finishReason: "STOP",
|
||||
},
|
||||
],
|
||||
usageMetadata: {
|
||||
promptTokenCount: 10,
|
||||
candidatesTokenCount: 5,
|
||||
totalTokenCount: 15,
|
||||
},
|
||||
},
|
||||
})}`,
|
||||
].join("\n\n")}\n\n`;
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
const dataStream = new ReadableStream<Uint8Array>({
|
||||
start(controller) {
|
||||
controller.enqueue(encoder.encode(sse));
|
||||
controller.close();
|
||||
},
|
||||
});
|
||||
|
||||
const fetchMock = vi.fn(async () => {
|
||||
return new Response(dataStream, {
|
||||
status: 200,
|
||||
headers: { "content-type": "text/event-stream" },
|
||||
});
|
||||
});
|
||||
|
||||
global.fetch = fetchMock as typeof fetch;
|
||||
|
||||
const model: Model<"google-gemini-cli"> = {
|
||||
id: "gemini-2.5-flash",
|
||||
name: "Gemini 2.5 Flash",
|
||||
api: "google-gemini-cli",
|
||||
provider: "google-gemini-cli",
|
||||
baseUrl: "https://cloudcode-pa.googleapis.com",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
contextWindow: 128000,
|
||||
maxTokens: 8192,
|
||||
};
|
||||
|
||||
const context: Context = {
|
||||
messages: [{ role: "user", content: "Check status", timestamp: Date.now() }],
|
||||
tools: [
|
||||
{
|
||||
name: "get_status",
|
||||
description: "Get current status",
|
||||
parameters: emptySchema,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const stream = streamGoogleGeminiCli(model, context, {
|
||||
apiKey: JSON.stringify({ token: "token", projectId: "project" }),
|
||||
});
|
||||
|
||||
for await (const _ of stream) {
|
||||
// consume stream
|
||||
}
|
||||
|
||||
const result = await stream.result();
|
||||
|
||||
expect(result.stopReason).toBe("toolUse");
|
||||
expect(result.content).toHaveLength(1);
|
||||
|
||||
const toolCall = result.content[0] as ToolCall;
|
||||
expect(toolCall.type).toBe("toolCall");
|
||||
expect(toolCall.name).toBe("get_status");
|
||||
expect(toolCall.arguments).toEqual({});
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue