feat: upgrade to AI SDK v6 beta and Zod v4 to fix tool schema errors
OpenAI was rejecting tool definitions with error "schema must be a JSON Schema of 'type: "object"'". This was caused by AI SDK v5 not properly converting Zod schemas to JSON Schema format. **Changes:** - Upgrade AI SDK from v5.0.104 to v6.0.0-beta.124 - Upgrade @ai-sdk/openai from v2.0.74 to v3.0.0-beta.22 - Upgrade Zod from v3.25.76 to v4.1.13 across all packages **AI SDK v6 breaking changes:** - Tool definition API: `parameters` renamed to `inputSchema` - Removed `aiTool()` wrapper - use plain object with description, inputSchema, execute - Streaming API: Use `textStream` async iterator instead of onChunk callback - Zod schemas now properly converted to JSON Schema for OpenAI **Zod v4 breaking changes:** - `z.record()` now requires two arguments: `z.record(keySchema, valueSchema)` - `z.enum()` params changed: `errorMap` removed, use `message` instead - Type system improvements require explicit type parameters - Fixed type errors in @tpmjs/env, @tpmjs/npm-client, @tpmjs/types **Files changed:** - apps/web/src/lib/ai-agent/tool-executor-agent.ts - Updated tool definition to use `inputSchema` instead of `parameters` - Removed `aiTool()` wrapper - Fixed streaming to use `textStream` iterator - packages/env/src/index.ts - Updated type constraint from `z.ZodRawShape` to `Record<string, z.ZodTypeAny>` - packages/npm-client/src/package.ts - Fixed `z.record()` calls to include both key and value schemas - Added type assertions for record indexing - packages/types/src/tpmjs.ts - Changed `errorMap` to `message` in z.enum() calls **Testing:** - ✅ Type-check passes - ✅ Production build succeeds - ✅ All routes compile correctly This fixes the tool execution error where OpenAI rejected tool schemas with invalid format. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
fa6ba5b6cd
commit
a92c2c250c
22 changed files with 172 additions and 106 deletions
|
|
@ -1,12 +1,12 @@
|
|||
/**
|
||||
* AI Agent service for executing TPMJS tools
|
||||
* Converts TPMJS metadata to Zod schemas and executes with AI SDK
|
||||
* Converts TPMJS metadata to Zod schemas and executes with AI SDK v6
|
||||
*/
|
||||
|
||||
import { openai } from '@ai-sdk/openai';
|
||||
import type { Tool } from '@tpmjs/db';
|
||||
import { executePackage } from '@tpmjs/package-executor';
|
||||
import { type CoreMessage, tool as aiTool, streamText } from 'ai';
|
||||
import { type CoreMessage, streamText } from 'ai';
|
||||
import { z } from 'zod';
|
||||
|
||||
/**
|
||||
|
|
@ -89,7 +89,7 @@ export function tpmjsParamsToZodSchema(parameters: TPMJSParameter[]): z.ZodObjec
|
|||
}
|
||||
|
||||
/**
|
||||
* Create AI SDK tool definition from TPMJS Tool
|
||||
* Create AI SDK v6 tool definition from TPMJS Tool
|
||||
*/
|
||||
export function createToolDefinition(tool: Tool) {
|
||||
const parameters = Array.isArray(tool.parameters)
|
||||
|
|
@ -100,7 +100,7 @@ export function createToolDefinition(tool: Tool) {
|
|||
console.log('[createToolDefinition] Parameters array:', JSON.stringify(parameters));
|
||||
console.log('[createToolDefinition] Parameters length:', parameters.length);
|
||||
|
||||
// Ensure we have a valid schema - if no parameters, use an empty object with explicit additionalProperties
|
||||
// Ensure we have a valid schema - if no parameters, use an empty object
|
||||
const schema =
|
||||
parameters.length > 0
|
||||
? tpmjsParamsToZodSchema(parameters)
|
||||
|
|
@ -110,9 +110,10 @@ export function createToolDefinition(tool: Tool) {
|
|||
console.log('[createToolDefinition] Schema type:', typeof schema);
|
||||
console.log('[createToolDefinition] Schema constructor:', schema.constructor.name);
|
||||
|
||||
const toolDef = aiTool({
|
||||
// AI SDK v6 beta tool definition - uses inputSchema instead of parameters
|
||||
return {
|
||||
description: tool.description,
|
||||
parameters: schema,
|
||||
inputSchema: schema, // Changed from 'parameters' to 'inputSchema' in v6
|
||||
execute: async (params: Record<string, unknown>) => {
|
||||
// Execute the actual npm package in a sandbox
|
||||
const result = await executePackage(
|
||||
|
|
@ -128,12 +129,7 @@ export function createToolDefinition(tool: Tool) {
|
|||
|
||||
return result.output;
|
||||
},
|
||||
// biome-ignore lint/suspicious/noExplicitAny: AI SDK v5 type compatibility workaround
|
||||
} as any);
|
||||
|
||||
console.log('[createToolDefinition] Tool definition created:', JSON.stringify(toolDef, null, 2));
|
||||
|
||||
return toolDef;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -223,22 +219,16 @@ export async function executeToolWithAgent(
|
|||
model: openai('gpt-4-turbo'),
|
||||
messages,
|
||||
tools: toolsConfig,
|
||||
// biome-ignore lint/suspicious/noExplicitAny: AI SDK v5 chunk type compatibility
|
||||
onChunk: ({ chunk }: { chunk: any }) => {
|
||||
if (chunk.type === 'text-delta') {
|
||||
const text = chunk.text || '';
|
||||
fullOutput += text;
|
||||
onChunk?.(text);
|
||||
}
|
||||
},
|
||||
onFinish: () => {
|
||||
agentSteps++;
|
||||
},
|
||||
// biome-ignore lint/suspicious/noExplicitAny: AI SDK v5 streaming configuration workaround
|
||||
} as any);
|
||||
});
|
||||
|
||||
// Wait for completion
|
||||
await result.text;
|
||||
// Stream and collect text
|
||||
for await (const chunk of result.textStream) {
|
||||
fullOutput += chunk;
|
||||
onChunk?.(chunk);
|
||||
}
|
||||
|
||||
// Calculate final token breakdown
|
||||
const parameters = Array.isArray(tool.parameters)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue