test(ai): pin gateway tool schema conversion (#2063)

This commit is contained in:
maxpetrusenkoagent
2026-07-16 19:54:06 -07:00
committed by GitHub
parent 9ceca6063b
commit 323d7d6336
2 changed files with 23 additions and 21 deletions
+17 -15
View File
@@ -2791,6 +2791,22 @@ async function classifyGatewayGuardrail(input: {
}
}
export function toAISDKTools(tools: ChatToolDef[] | undefined): Record<string, any> | undefined {
if (!tools || tools.length === 0) return undefined;
return tools.reduce((acc, t) => {
acc[t.name] = {
description: t.description,
// AI SDK v6 requires a Schema (carrying the schema symbol), not a plain
// `{jsonSchema}` object — the bare object makes asSchema() treat it as a
// thunk and call schema(), throwing "schema is not a function". Wrap the
// raw JSON Schema with the SDK's jsonSchema() helper so tool calls work
// through the real toolLoop (skillopt rollouts + subagent jobs).
inputSchema: jsonSchema(t.inputSchema as any),
};
return acc;
}, {} as Record<string, any>);
}
export async function chat(opts: ChatOpts): Promise<ChatResult> {
const tracker = __budgetStore.getStore() ?? null;
const modelStrEarly = opts.model ?? getChatModel();
@@ -2878,21 +2894,7 @@ export async function chat(opts: ChatOpts): Promise<ChatResult> {
const supportsCache = recipe.touchpoints.chat?.supports_prompt_cache === true;
const useCache = !!opts.cacheSystem && supportsCache;
// Build messages. Anthropic prompt-cache markers ride on system + last tool
// via providerOptions; the AI SDK accepts the system as a string for
// generateText, so cache markers go through providerOptions.anthropic.
const tools = (opts.tools ?? []).reduce((acc, t) => {
acc[t.name] = {
description: t.description,
// AI SDK v6 requires a Schema (carrying the schema symbol), not a plain
// `{jsonSchema}` object — the bare object makes asSchema() treat it as a
// thunk and call schema(), throwing "schema is not a function". Wrap the
// raw JSON Schema with the SDK's jsonSchema() helper so tool calls work
// through the real toolLoop (skillopt rollouts + subagent jobs).
inputSchema: jsonSchema(t.inputSchema as any),
};
return acc;
}, {} as Record<string, any>);
const tools = toAISDKTools(opts.tools);
const providerOptions: Record<string, any> = {};
if (useCache) {
+6 -6
View File
@@ -1,7 +1,7 @@
import { describe, expect, it } from 'bun:test';
import { generateText, jsonSchema } from 'ai';
import { MockLanguageModelV3 } from 'ai/test';
import { toModelMessages, type ChatMessage } from '../../src/core/ai/gateway.ts';
import { toAISDKTools, toModelMessages, type ChatMessage } from '../../src/core/ai/gateway.ts';
// v0.42 AI SDK v6 fix — the regression guard that the original bug evaded.
// Every gateway/toolLoop test stubs the chat transport, which short-circuits
@@ -36,13 +36,13 @@ const internalMessages: ChatMessage[] = [
describe('gateway tool schema + message shape (real AI SDK v6)', () => {
it('jsonSchema()-wrapped tools + adapted messages pass generateText without throwing', async () => {
const model = mockModel();
// Built exactly as gateway.chat() builds it (the primary fix).
const tools = {
search: {
const tools = toAISDKTools([
{
name: 'search',
description: 'search the brain',
inputSchema: jsonSchema({ type: 'object', properties: { q: { type: 'string' } } } as any),
inputSchema: { type: 'object', properties: { q: { type: 'string' } } },
},
};
]);
const result = await generateText({
model: model as any,