Merge pull request #599 from mcinteerj/fix/gemini-tool-schemas
fix: simplify tool schemas for Gemini compatibility
This commit is contained in:
@@ -48,6 +48,7 @@
|
|||||||
- Discord: stop provider when gateway reconnects are exhausted and surface errors. (#514) — thanks @joshp123
|
- Discord: stop provider when gateway reconnects are exhausted and surface errors. (#514) — thanks @joshp123
|
||||||
- Agents: strip empty assistant text blocks from session history to avoid Claude API 400s. (#210)
|
- Agents: strip empty assistant text blocks from session history to avoid Claude API 400s. (#210)
|
||||||
- Agents: scrub unsupported JSON Schema keywords from tool schemas for Cloud Code Assist API compatibility. (#567) — thanks @erikpr1994
|
- Agents: scrub unsupported JSON Schema keywords from tool schemas for Cloud Code Assist API compatibility. (#567) — thanks @erikpr1994
|
||||||
|
- Agents: simplify session tool schemas for Gemini compatibility. (#599) — thanks @mcinteerj
|
||||||
- Auto-reply: preserve block reply ordering with timeout fallback for streaming. (#503) — thanks @joshp123
|
- Auto-reply: preserve block reply ordering with timeout fallback for streaming. (#503) — thanks @joshp123
|
||||||
- Auto-reply: block reply ordering fix (duplicate PR superseded by #503). (#483) — thanks @AbhisekBasu1
|
- Auto-reply: block reply ordering fix (duplicate PR superseded by #503). (#483) — thanks @AbhisekBasu1
|
||||||
- Auto-reply: avoid splitting outbound chunks inside parentheses. (#499) — thanks @philipp-spiess
|
- Auto-reply: avoid splitting outbound chunks inside parentheses. (#499) — thanks @philipp-spiess
|
||||||
|
|||||||
@@ -23,6 +23,43 @@ vi.mock("../config/config.js", async (importOriginal) => {
|
|||||||
import { createClawdbotTools } from "./clawdbot-tools.js";
|
import { createClawdbotTools } from "./clawdbot-tools.js";
|
||||||
|
|
||||||
describe("sessions tools", () => {
|
describe("sessions tools", () => {
|
||||||
|
it("uses number (not integer) in tool schemas for Gemini compatibility", () => {
|
||||||
|
const tools = createClawdbotTools();
|
||||||
|
const byName = (name: string) => {
|
||||||
|
const tool = tools.find((candidate) => candidate.name === name);
|
||||||
|
expect(tool).toBeDefined();
|
||||||
|
if (!tool) throw new Error(`missing ${name} tool`);
|
||||||
|
return tool;
|
||||||
|
};
|
||||||
|
|
||||||
|
const schemaProp = (toolName: string, prop: string) => {
|
||||||
|
const tool = byName(toolName);
|
||||||
|
const schema = tool.parameters as {
|
||||||
|
anyOf?: unknown;
|
||||||
|
oneOf?: unknown;
|
||||||
|
properties?: Record<string, unknown>;
|
||||||
|
};
|
||||||
|
expect(schema.anyOf).toBeUndefined();
|
||||||
|
expect(schema.oneOf).toBeUndefined();
|
||||||
|
|
||||||
|
const properties = schema.properties ?? {};
|
||||||
|
const value = properties[prop] as { type?: unknown } | undefined;
|
||||||
|
expect(value).toBeDefined();
|
||||||
|
if (!value) throw new Error(`missing ${toolName} schema prop: ${prop}`);
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(schemaProp("sessions_history", "limit").type).toBe("number");
|
||||||
|
expect(schemaProp("sessions_list", "limit").type).toBe("number");
|
||||||
|
expect(schemaProp("sessions_list", "activeMinutes").type).toBe("number");
|
||||||
|
expect(schemaProp("sessions_list", "messageLimit").type).toBe("number");
|
||||||
|
expect(schemaProp("sessions_send", "timeoutSeconds").type).toBe("number");
|
||||||
|
expect(schemaProp("sessions_spawn", "runTimeoutSeconds").type).toBe(
|
||||||
|
"number",
|
||||||
|
);
|
||||||
|
expect(schemaProp("sessions_spawn", "timeoutSeconds").type).toBe("number");
|
||||||
|
});
|
||||||
|
|
||||||
it("sessions_list filters kinds and includes messages", async () => {
|
it("sessions_list filters kinds and includes messages", async () => {
|
||||||
callGatewayMock.mockReset();
|
callGatewayMock.mockReset();
|
||||||
callGatewayMock.mockImplementation(async (opts: unknown) => {
|
callGatewayMock.mockImplementation(async (opts: unknown) => {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
|
|
||||||
const SessionsHistoryToolSchema = Type.Object({
|
const SessionsHistoryToolSchema = Type.Object({
|
||||||
sessionKey: Type.String(),
|
sessionKey: Type.String(),
|
||||||
limit: Type.Optional(Type.Integer({ minimum: 1 })),
|
limit: Type.Optional(Type.Number({ minimum: 1 })),
|
||||||
includeTools: Type.Optional(Type.Boolean()),
|
includeTools: Type.Optional(Type.Boolean()),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -46,9 +46,9 @@ type SessionListRow = {
|
|||||||
|
|
||||||
const SessionsListToolSchema = Type.Object({
|
const SessionsListToolSchema = Type.Object({
|
||||||
kinds: Type.Optional(Type.Array(Type.String())),
|
kinds: Type.Optional(Type.Array(Type.String())),
|
||||||
limit: Type.Optional(Type.Integer({ minimum: 1 })),
|
limit: Type.Optional(Type.Number({ minimum: 1 })),
|
||||||
activeMinutes: Type.Optional(Type.Integer({ minimum: 1 })),
|
activeMinutes: Type.Optional(Type.Number({ minimum: 1 })),
|
||||||
messageLimit: Type.Optional(Type.Integer({ minimum: 0 })),
|
messageLimit: Type.Optional(Type.Number({ minimum: 0 })),
|
||||||
});
|
});
|
||||||
|
|
||||||
function resolveSandboxSessionToolsVisibility(
|
function resolveSandboxSessionToolsVisibility(
|
||||||
|
|||||||
@@ -30,25 +30,15 @@ import {
|
|||||||
resolvePingPongTurns,
|
resolvePingPongTurns,
|
||||||
} from "./sessions-send-helpers.js";
|
} from "./sessions-send-helpers.js";
|
||||||
|
|
||||||
const SessionsSendToolSchema = Type.Union([
|
const SessionsSendToolSchema = Type.Object({
|
||||||
Type.Object(
|
sessionKey: Type.Optional(Type.String()),
|
||||||
{
|
label: Type.Optional(
|
||||||
sessionKey: Type.String(),
|
Type.String({ minLength: 1, maxLength: SESSION_LABEL_MAX_LENGTH }),
|
||||||
message: Type.String(),
|
|
||||||
timeoutSeconds: Type.Optional(Type.Integer({ minimum: 0 })),
|
|
||||||
},
|
|
||||||
{ additionalProperties: false },
|
|
||||||
),
|
),
|
||||||
Type.Object(
|
agentId: Type.Optional(Type.String({ minLength: 1, maxLength: 64 })),
|
||||||
{
|
message: Type.String(),
|
||||||
label: Type.String({ minLength: 1, maxLength: SESSION_LABEL_MAX_LENGTH }),
|
timeoutSeconds: Type.Optional(Type.Number({ minimum: 0 })),
|
||||||
agentId: Type.Optional(Type.String({ minLength: 1, maxLength: 64 })),
|
});
|
||||||
message: Type.String(),
|
|
||||||
timeoutSeconds: Type.Optional(Type.Integer({ minimum: 0 })),
|
|
||||||
},
|
|
||||||
{ additionalProperties: false },
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
|
|
||||||
export function createSessionsSendTool(opts?: {
|
export function createSessionsSendTool(opts?: {
|
||||||
agentSessionKey?: string;
|
agentSessionKey?: string;
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ const SessionsSpawnToolSchema = Type.Object({
|
|||||||
label: Type.Optional(Type.String()),
|
label: Type.Optional(Type.String()),
|
||||||
agentId: Type.Optional(Type.String()),
|
agentId: Type.Optional(Type.String()),
|
||||||
model: Type.Optional(Type.String()),
|
model: Type.Optional(Type.String()),
|
||||||
runTimeoutSeconds: Type.Optional(Type.Integer({ minimum: 0 })),
|
runTimeoutSeconds: Type.Optional(Type.Number({ minimum: 0 })),
|
||||||
// Back-compat alias. Prefer runTimeoutSeconds.
|
// Back-compat alias. Prefer runTimeoutSeconds.
|
||||||
timeoutSeconds: Type.Optional(Type.Integer({ minimum: 0 })),
|
timeoutSeconds: Type.Optional(Type.Number({ minimum: 0 })),
|
||||||
cleanup: Type.Optional(
|
cleanup: Type.Optional(
|
||||||
Type.Union([Type.Literal("delete"), Type.Literal("keep")]),
|
Type.Union([Type.Literal("delete"), Type.Literal("keep")]),
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user