Skip to content

Commit 0d3cfe1

Browse files
committed
feat: wip
1 parent 08c4fe1 commit 0d3cfe1

File tree

8 files changed

+34
-130
lines changed

8 files changed

+34
-130
lines changed

packages/anthropic-ai/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"dependencies": {
2626
"@anthropic-ai/sdk": "^0.40.0",
2727
"@voltagent/core": "^0.1.22",
28+
"xsschema": "0.3.0-beta.2",
2829
"zod": "3.24.2"
2930
},
3031
"devDependencies": {

packages/anthropic-ai/src/index.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import type {
1313
StreamTextOptions,
1414
VoltAgentError,
1515
} from "@voltagent/core";
16-
import type { z } from "zod";
16+
import * as xsschema from "xsschema";
1717
import type {
1818
AnthropicMessage,
1919
AnthropicProviderOptions,
@@ -29,7 +29,6 @@ import {
2929
handleStepFinish,
3030
processContent,
3131
processResponseContent,
32-
zodToJsonSchema,
3332
} from "./utils";
3433

3534
export class AnthropicProvider implements LLMProvider<string> {
@@ -80,10 +79,20 @@ export class AnthropicProvider implements LLMProvider<string> {
8079
}
8180

8281
toTool(tool: BaseTool): AnthropicTool {
82+
const jsonSchema = xsschema.toJsonSchemaSync(tool.parameters);
83+
if (jsonSchema.type !== "object") {
84+
throw new Error("Tool parameters must be an object");
85+
}
86+
8387
return {
8488
name: tool.name,
8589
description: tool.description,
86-
input_schema: zodToJsonSchema(tool.parameters),
90+
input_schema: {
91+
...jsonSchema,
92+
// Already checked that the type is object above
93+
type: "object",
94+
properties: jsonSchema.properties ?? {},
95+
},
8796
};
8897
}
8998

@@ -243,11 +252,11 @@ export class AnthropicProvider implements LLMProvider<string> {
243252
}
244253
}
245254

246-
async generateObject<TSchema extends z.ZodType>(
255+
async generateObject<TSchema extends xsschema.Schema>(
247256
options: GenerateObjectOptions<string, TSchema>,
248-
): Promise<ProviderObjectResponse<any, z.infer<TSchema>>> {
257+
): Promise<ProviderObjectResponse<any, xsschema.Infer<TSchema>>> {
249258
const { temperature = 0.2, maxTokens = 1024, topP, stopSequences } = options.provider || {};
250-
const JsonSchema = zodToJsonSchema(options.schema);
259+
const JsonSchema = await xsschema.toJsonSchema(options.schema);
251260
const systemPrompt = `${getSystemMessage(options.messages)}. Response Schema: ${JSON.stringify(JsonSchema)}. You must return the response in valid JSON Format with proper schema, nothing else `;
252261

253262
const anthropicMessages = this.getAnthropicMessages(options.messages);
@@ -283,7 +292,7 @@ export class AnthropicProvider implements LLMProvider<string> {
283292
throw new Error(`The JSON returned by Anthropic API is not valid \n ${err}`);
284293
}
285294

286-
const parsedResult = options.schema.safeParse(parsedObject);
295+
const parsedResult = await xsschema.validate(parsedObject, options.schema);
287296
if (!parsedResult.success) {
288297
throw new Error(
289298
`the response doesn't match the specified schema: ${parsedResult.error.message}`,
@@ -320,12 +329,12 @@ export class AnthropicProvider implements LLMProvider<string> {
320329
}
321330
}
322331

323-
async streamObject<TSchema extends z.ZodType>(
332+
async streamObject<TSchema extends xsschema.Schema>(
324333
options: StreamObjectOptions<string, TSchema>,
325-
): Promise<ProviderObjectStreamResponse<any, z.infer<TSchema>>> {
334+
): Promise<ProviderObjectStreamResponse<any, xsschema.Infer<TSchema>>> {
326335
try {
327336
const anthropicMessages = this.getAnthropicMessages(options.messages);
328-
const JsonSchema = zodToJsonSchema(options.schema);
337+
const JsonSchema = await xsschema.toJsonSchema(options.schema);
329338
const systemPrompt = `${getSystemMessage(options.messages)}. Response Schema: ${JSON.stringify(JsonSchema)}. You must return the response in valid JSON Format with proper schema, nothing else `;
330339
const { temperature = 0.2, maxTokens = 1024, topP, stopSequences } = options.provider || {};
331340

@@ -353,7 +362,7 @@ export class AnthropicProvider implements LLMProvider<string> {
353362
// Try to parse partial JSON as it comes in
354363
try {
355364
const partialObject = JSON.parse(accumulatedText);
356-
const parseResult = options.schema.safeParse(partialObject);
365+
const parseResult = await xsschema.validate(partialObject, options.schema);
357366

358367
if (parseResult.success) {
359368
controller.enqueue(parseResult.data);
@@ -366,7 +375,7 @@ export class AnthropicProvider implements LLMProvider<string> {
366375
if (chunk.type === "message_stop") {
367376
try {
368377
const parsedObject = JSON.parse(accumulatedText);
369-
const parsedResult = options.schema.safeParse(parsedObject);
378+
const parsedResult = await xsschema.validate(parsedObject, options.schema);
370379

371380
if (parsedResult.success) {
372381
controller.enqueue(parsedResult.data);

packages/anthropic-ai/src/utils/index.ts

Lines changed: 0 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import type {
1010
StepWithContent,
1111
VoltAgentError,
1212
} from "@voltagent/core";
13-
import { z } from "zod";
1413

1514
/**
1615
* Processes text content into a text content block
@@ -171,111 +170,6 @@ export function processContentPart(part: any): ContentBlockParam | null {
171170
return null;
172171
}
173172

174-
/**
175-
* Converts a Zod schema to JSON Schema format that Anthropic expects
176-
* @param {z.ZodType<any>} schema - The Zod schema to convert
177-
* @returns {Object} A JSON Schema object with type, properties, and required fields
178-
* @throws {Error} If the schema is not a Zod object
179-
*/
180-
export function zodToJsonSchema(schema: z.ZodType<any>): {
181-
type: "object";
182-
properties: Record<string, unknown>;
183-
required?: string[];
184-
} {
185-
// Check if it's a ZodObject by checking for the typeName property
186-
if (
187-
schema &&
188-
typeof schema === "object" &&
189-
"_def" in schema &&
190-
schema._def &&
191-
typeof schema._def === "object" &&
192-
"typeName" in schema._def &&
193-
schema._def.typeName === "ZodObject"
194-
) {
195-
// Use a safer type assertion approach
196-
const def = schema._def as unknown as { shape: () => Record<string, z.ZodTypeAny> };
197-
const shape = def.shape();
198-
const properties: Record<string, unknown> = {};
199-
const required: string[] = [];
200-
201-
for (const [key, value] of Object.entries(shape)) {
202-
const fieldSchema = convertZodField(value as z.ZodTypeAny);
203-
properties[key] = fieldSchema;
204-
205-
// Check if the field is required
206-
if (!(value instanceof z.ZodOptional)) {
207-
required.push(key);
208-
}
209-
}
210-
211-
return {
212-
type: "object" as const,
213-
properties,
214-
...(required.length > 0 ? { required } : {}),
215-
};
216-
}
217-
218-
throw new Error("Root schema must be a Zod object");
219-
}
220-
221-
/**
222-
* Helper function to create a base schema with type and optional description
223-
* @param {z.ZodType} field - The Zod field to extract description from
224-
* @param {string} type - The type string to use
225-
* @returns {Object} Schema object with type and optional description
226-
*/
227-
function getBaseSchema(field: z.ZodType, type: string) {
228-
return {
229-
type,
230-
...(field.description ? { description: field.description } : {}),
231-
};
232-
}
233-
234-
/**
235-
* Helper function to handle primitive type fields
236-
* @param {z.ZodTypeAny} field - The Zod field to process
237-
* @param {string} type - The type string to use
238-
* @returns {Object} Schema object with type and optional description
239-
*/
240-
function handlePrimitiveType(field: z.ZodTypeAny, type: string) {
241-
return getBaseSchema(field, type);
242-
}
243-
244-
/**
245-
* Converts a Zod field to a JSON Schema field
246-
* @param {z.ZodTypeAny} zodField - The Zod field to convert
247-
* @returns {any} The JSON Schema representation of the field
248-
*/
249-
export function convertZodField(zodField: z.ZodTypeAny): any {
250-
if (zodField instanceof z.ZodString) {
251-
return handlePrimitiveType(zodField, "string");
252-
}
253-
if (zodField instanceof z.ZodNumber) {
254-
return handlePrimitiveType(zodField, "number");
255-
}
256-
if (zodField instanceof z.ZodBoolean) {
257-
return handlePrimitiveType(zodField, "boolean");
258-
}
259-
if (zodField instanceof z.ZodArray) {
260-
return {
261-
type: "array",
262-
items: convertZodField(zodField.element),
263-
...(zodField.description ? { description: zodField.description } : {}),
264-
};
265-
}
266-
if (zodField instanceof z.ZodEnum) {
267-
return {
268-
type: "string",
269-
enum: zodField._def.values,
270-
...(zodField.description ? { description: zodField.description } : {}),
271-
};
272-
}
273-
if (zodField instanceof z.ZodOptional) {
274-
return convertZodField(zodField.unwrap());
275-
}
276-
return { type: "string" };
277-
}
278-
279173
/**
280174
* Creates a response object from Anthropic's response
281175
* @param {Message} response - The response from Anthropic

packages/core/src/agent/providers/base/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ export type BaseMessage = {
191191
};
192192

193193
// Schema types
194-
export type ToolSchema = xsschema.Schema;
194+
export type ToolSchema = xsschema.JsonSchema;
195195

196196
// Base tool types
197197
export type ToolExecuteOptions = {

packages/core/src/mcp/client/index.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ import {
1111
CallToolResultSchema,
1212
ListResourcesResultSchema,
1313
} from "@modelcontextprotocol/sdk/types.js";
14-
import type * as z from "zod";
15-
import { convertJsonSchemaToZod } from "zod-from-json-schema";
14+
import * as xsschema from "xsschema";
1615
import devLogger from "../../utils/internal/dev-logger";
1716
import { type Tool, createTool } from "../../tool";
1817
import type {
@@ -192,16 +191,14 @@ export class MCPClient extends EventEmitter {
192191
inputSchema: unknown;
193192
}[]) {
194193
try {
195-
const zodSchema = convertJsonSchemaToZod(
196-
toolDef.inputSchema as Record<string, unknown>,
197-
) as unknown as z.ZodType;
194+
const jsonSchema = await xsschema.toJsonSchema(toolDef.inputSchema as xsschema.Schema);
198195
const namespacedToolName = `${this.clientInfo.name}_${toolDef.name}`; // Use original separator
199196

200197
const agentTool = createTool({
201198
name: namespacedToolName,
202199
description: toolDef.description || `Executes the remote tool: ${toolDef.name}`,
203-
parameters: zodSchema,
204-
execute: async (args: Record<string, unknown>): Promise<unknown> => {
200+
parameters: jsonSchema,
201+
execute: async (args: xsschema.JsonSchema): Promise<unknown> => {
205202
try {
206203
const result = await this.callTool({
207204
// Use original method name

packages/core/src/mcp/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { ClientCapabilities } from "@modelcontextprotocol/sdk/types.js";
22
import type { Tool } from "../tool";
3+
import type * as xsschema from "xsschema";
34

45
/**
56
* Client information for MCP
@@ -170,7 +171,7 @@ export type MCPToolCall = {
170171
/**
171172
* Arguments to pass to the tool
172173
*/
173-
arguments: Record<string, unknown>;
174+
arguments: xsschema.JsonSchema;
174175
};
175176

176177
/**

packages/core/src/tool/index.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { v4 as uuidv4 } from "uuid";
22
import type { BaseTool, ToolExecuteOptions, ToolSchema } from "../agent/providers/base/types";
33
import devLogger from "../utils/internal/dev-logger";
4-
import type * as xsschema from "xsschema";
54

65
// Export ToolManager and related types
76
export { ToolManager, ToolStatus, ToolStatusInfo } from "./manager";
@@ -40,7 +39,7 @@ export type ToolOptions<T extends ToolSchema = ToolSchema> = {
4039
/**
4140
* Function to execute when the tool is called
4241
*/
43-
execute: (args: xsschema.Infer<T>, options?: ToolExecuteOptions) => Promise<unknown>;
42+
execute: (args: T, options?: ToolExecuteOptions) => Promise<unknown>;
4443
};
4544

4645
/**
@@ -70,7 +69,7 @@ export class Tool<T extends ToolSchema = ToolSchema> /* implements BaseTool<z.in
7069
/**
7170
* Function to execute when the tool is called
7271
*/
73-
readonly execute: (args: xsschema.Infer<T>, options?: ToolExecuteOptions) => Promise<unknown>;
72+
readonly execute: (args: T, options?: ToolExecuteOptions) => Promise<unknown>;
7473

7574
/**
7675
* Create a new tool

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)