fix(google): scrub tool schemas for gemini
This commit is contained in:
@@ -44,7 +44,11 @@ import {
|
|||||||
} from "../skills.js";
|
} from "../skills.js";
|
||||||
import { filterBootstrapFilesForSession, loadWorkspaceBootstrapFiles } from "../workspace.js";
|
import { filterBootstrapFilesForSession, loadWorkspaceBootstrapFiles } from "../workspace.js";
|
||||||
import { buildEmbeddedExtensionPaths } from "./extensions.js";
|
import { buildEmbeddedExtensionPaths } from "./extensions.js";
|
||||||
import { logToolSchemasForGoogle, sanitizeSessionHistory } from "./google.js";
|
import {
|
||||||
|
logToolSchemasForGoogle,
|
||||||
|
sanitizeSessionHistory,
|
||||||
|
sanitizeToolsForGoogle,
|
||||||
|
} from "./google.js";
|
||||||
import { getDmHistoryLimitFromSessionKey, limitHistoryTurns } from "./history.js";
|
import { getDmHistoryLimitFromSessionKey, limitHistoryTurns } from "./history.js";
|
||||||
import { resolveGlobalLane, resolveSessionLane } from "./lanes.js";
|
import { resolveGlobalLane, resolveSessionLane } from "./lanes.js";
|
||||||
import { log } from "./logger.js";
|
import { log } from "./logger.js";
|
||||||
@@ -183,7 +187,7 @@ export async function compactEmbeddedPiSession(params: {
|
|||||||
warn: (message) => log.warn(`${message} (sessionKey=${sessionLabel})`),
|
warn: (message) => log.warn(`${message} (sessionKey=${sessionLabel})`),
|
||||||
});
|
});
|
||||||
const runAbortController = new AbortController();
|
const runAbortController = new AbortController();
|
||||||
const tools = createClawdbotCodingTools({
|
const toolsRaw = createClawdbotCodingTools({
|
||||||
exec: {
|
exec: {
|
||||||
...resolveExecToolDefaults(params.config),
|
...resolveExecToolDefaults(params.config),
|
||||||
elevated: params.bashElevated,
|
elevated: params.bashElevated,
|
||||||
@@ -200,6 +204,7 @@ export async function compactEmbeddedPiSession(params: {
|
|||||||
modelId,
|
modelId,
|
||||||
modelAuthMode: resolveModelAuthMode(model.provider, params.config),
|
modelAuthMode: resolveModelAuthMode(model.provider, params.config),
|
||||||
});
|
});
|
||||||
|
const tools = sanitizeToolsForGoogle({ tools: toolsRaw, provider });
|
||||||
logToolSchemasForGoogle({ tools, provider });
|
logToolSchemasForGoogle({ tools, provider });
|
||||||
const machineName = await getMachineDisplayName();
|
const machineName = await getMachineDisplayName();
|
||||||
const runtimeChannel = normalizeMessageChannel(
|
const runtimeChannel = normalizeMessageChannel(
|
||||||
|
|||||||
37
src/agents/pi-embedded-runner/google.test.ts
Normal file
37
src/agents/pi-embedded-runner/google.test.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
||||||
|
import { sanitizeToolsForGoogle } from "./google.js";
|
||||||
|
|
||||||
|
describe("sanitizeToolsForGoogle", () => {
|
||||||
|
it("strips unsupported schema keywords for Google providers", () => {
|
||||||
|
const tool = {
|
||||||
|
name: "test",
|
||||||
|
description: "test",
|
||||||
|
parameters: {
|
||||||
|
type: "object",
|
||||||
|
additionalProperties: false,
|
||||||
|
properties: {
|
||||||
|
foo: {
|
||||||
|
type: "string",
|
||||||
|
format: "uuid",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
execute: async () => ({ ok: true, content: [] }),
|
||||||
|
} as unknown as AgentTool;
|
||||||
|
|
||||||
|
const [sanitized] = sanitizeToolsForGoogle({
|
||||||
|
tools: [tool],
|
||||||
|
provider: "google-gemini-cli",
|
||||||
|
});
|
||||||
|
|
||||||
|
const params = sanitized.parameters as {
|
||||||
|
additionalProperties?: unknown;
|
||||||
|
properties?: Record<string, { format?: unknown }>;
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(params.additionalProperties).toBeUndefined();
|
||||||
|
expect(params.properties?.foo?.format).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { AgentMessage, AgentTool } from "@mariozechner/pi-agent-core";
|
import type { AgentMessage, AgentTool } from "@mariozechner/pi-agent-core";
|
||||||
|
import type { TSchema } from "@sinclair/typebox";
|
||||||
import type { SessionManager } from "@mariozechner/pi-coding-agent";
|
import type { SessionManager } from "@mariozechner/pi-coding-agent";
|
||||||
|
|
||||||
import { registerUnhandledRejectionHandler } from "../../infra/unhandled-rejections.js";
|
import { registerUnhandledRejectionHandler } from "../../infra/unhandled-rejections.js";
|
||||||
@@ -14,6 +15,7 @@ import { sanitizeToolUseResultPairing } from "../session-transcript-repair.js";
|
|||||||
import { log } from "./logger.js";
|
import { log } from "./logger.js";
|
||||||
import { describeUnknownError } from "./utils.js";
|
import { describeUnknownError } from "./utils.js";
|
||||||
import { isAntigravityClaude } from "../pi-embedded-helpers/google.js";
|
import { isAntigravityClaude } from "../pi-embedded-helpers/google.js";
|
||||||
|
import { cleanToolSchemaForGemini } from "../pi-tools.schema.js";
|
||||||
|
|
||||||
const GOOGLE_TURN_ORDERING_CUSTOM_TYPE = "google-turn-ordering-bootstrap";
|
const GOOGLE_TURN_ORDERING_CUSTOM_TYPE = "google-turn-ordering-bootstrap";
|
||||||
const GOOGLE_SCHEMA_UNSUPPORTED_KEYWORDS = new Set([
|
const GOOGLE_SCHEMA_UNSUPPORTED_KEYWORDS = new Set([
|
||||||
@@ -80,17 +82,36 @@ function findUnsupportedSchemaKeywords(schema: unknown, path: string): string[]
|
|||||||
return violations;
|
return violations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function sanitizeToolsForGoogle<TSchemaType extends TSchema = TSchema, TResult = unknown>(
|
||||||
|
params: {
|
||||||
|
tools: AgentTool<TSchemaType, TResult>[];
|
||||||
|
provider: string;
|
||||||
|
},
|
||||||
|
): AgentTool<TSchemaType, TResult>[] {
|
||||||
|
if (params.provider !== "google-antigravity" && params.provider !== "google-gemini-cli") {
|
||||||
|
return params.tools;
|
||||||
|
}
|
||||||
|
return params.tools.map((tool) => {
|
||||||
|
if (!tool.parameters || typeof tool.parameters !== "object") return tool;
|
||||||
|
return {
|
||||||
|
...tool,
|
||||||
|
parameters: cleanToolSchemaForGemini(tool.parameters as Record<string, unknown>) as TSchemaType,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function logToolSchemasForGoogle(params: { tools: AgentTool[]; provider: string }) {
|
export function logToolSchemasForGoogle(params: { tools: AgentTool[]; provider: string }) {
|
||||||
if (params.provider !== "google-antigravity" && params.provider !== "google-gemini-cli") {
|
if (params.provider !== "google-antigravity" && params.provider !== "google-gemini-cli") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const toolNames = params.tools.map((tool, index) => `${index}:${tool.name}`);
|
const toolNames = params.tools.map((tool, index) => `${index}:${tool.name}`);
|
||||||
|
const tools = sanitizeToolsForGoogle(params);
|
||||||
log.info("google tool schema snapshot", {
|
log.info("google tool schema snapshot", {
|
||||||
provider: params.provider,
|
provider: params.provider,
|
||||||
toolCount: params.tools.length,
|
toolCount: tools.length,
|
||||||
tools: toolNames,
|
tools: toolNames,
|
||||||
});
|
});
|
||||||
for (const [index, tool] of params.tools.entries()) {
|
for (const [index, tool] of tools.entries()) {
|
||||||
const violations = findUnsupportedSchemaKeywords(tool.parameters, `${tool.name}.parameters`);
|
const violations = findUnsupportedSchemaKeywords(tool.parameters, `${tool.name}.parameters`);
|
||||||
if (violations.length > 0) {
|
if (violations.length > 0) {
|
||||||
log.warn("google tool schema has unsupported keywords", {
|
log.warn("google tool schema has unsupported keywords", {
|
||||||
|
|||||||
@@ -45,7 +45,11 @@ import { filterBootstrapFilesForSession, loadWorkspaceBootstrapFiles } from "../
|
|||||||
import { isAbortError } from "../abort.js";
|
import { isAbortError } from "../abort.js";
|
||||||
import { buildEmbeddedExtensionPaths } from "../extensions.js";
|
import { buildEmbeddedExtensionPaths } from "../extensions.js";
|
||||||
import { applyExtraParamsToAgent } from "../extra-params.js";
|
import { applyExtraParamsToAgent } from "../extra-params.js";
|
||||||
import { logToolSchemasForGoogle, sanitizeSessionHistory } from "../google.js";
|
import {
|
||||||
|
logToolSchemasForGoogle,
|
||||||
|
sanitizeSessionHistory,
|
||||||
|
sanitizeToolsForGoogle,
|
||||||
|
} from "../google.js";
|
||||||
import { getDmHistoryLimitFromSessionKey, limitHistoryTurns } from "../history.js";
|
import { getDmHistoryLimitFromSessionKey, limitHistoryTurns } from "../history.js";
|
||||||
import { log } from "../logger.js";
|
import { log } from "../logger.js";
|
||||||
import { buildModelAliasLines } from "../model.js";
|
import { buildModelAliasLines } from "../model.js";
|
||||||
@@ -127,7 +131,7 @@ export async function runEmbeddedAttempt(
|
|||||||
|
|
||||||
const agentDir = params.agentDir ?? resolveClawdbotAgentDir();
|
const agentDir = params.agentDir ?? resolveClawdbotAgentDir();
|
||||||
|
|
||||||
const tools = createClawdbotCodingTools({
|
const toolsRaw = createClawdbotCodingTools({
|
||||||
exec: {
|
exec: {
|
||||||
...resolveExecToolDefaults(params.config),
|
...resolveExecToolDefaults(params.config),
|
||||||
elevated: params.bashElevated,
|
elevated: params.bashElevated,
|
||||||
@@ -148,6 +152,7 @@ export async function runEmbeddedAttempt(
|
|||||||
replyToMode: params.replyToMode,
|
replyToMode: params.replyToMode,
|
||||||
hasRepliedRef: params.hasRepliedRef,
|
hasRepliedRef: params.hasRepliedRef,
|
||||||
});
|
});
|
||||||
|
const tools = sanitizeToolsForGoogle({ tools: toolsRaw, provider: params.provider });
|
||||||
logToolSchemasForGoogle({ tools, provider: params.provider });
|
logToolSchemasForGoogle({ tools, provider: params.provider });
|
||||||
|
|
||||||
const machineName = await getMachineDisplayName();
|
const machineName = await getMachineDisplayName();
|
||||||
|
|||||||
Reference in New Issue
Block a user