Skip to content

Commit 4fffe9e

Browse files
committed
feat(core): support valibot and other schema libraries via xsschema, not just zod
1 parent 22993c1 commit 4fffe9e

File tree

9 files changed

+210
-93
lines changed

9 files changed

+210
-93
lines changed

.changeset/fair-memes-open.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@voltagent/core": minor
3+
---
4+
5+
feat(core): support valibot and other schema libraries via xsschema, not just zod

packages/core/package.json

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@
3838
"npm-check-updates": "^17.1.18",
3939
"uuid": "^9.0.1",
4040
"ws": "^8.18.1",
41-
"zod": "^3.24.2"
41+
"xsschema": "0.3.0-beta.2",
42+
"zod": "^3.25.30",
43+
"zod-to-openapi": "^0.2.1"
4244
},
4345
"devDependencies": {
4446
"@types/jest": "^29.5.0",
@@ -48,8 +50,5 @@
4850
"ts-jest": "^29.1.0",
4951
"tsup": "^6.7.0",
5052
"typescript": "^5.0.4"
51-
},
52-
"peerDependencies": {
53-
"zod": "^3.24.2"
5453
}
5554
}

packages/core/src/agent/index.spec.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// @ts-ignore - To prevent errors when loading Jest mocks
22
import { z } from "zod";
3+
import type * as xsschema from "xsschema";
34
import { AgentEventEmitter } from "../events";
45
import type { MemoryMessage } from "../memory/types";
56
import { AgentRegistry } from "../server/registry";
@@ -232,11 +233,13 @@ class MockProvider implements LLMProvider<MockModelType> {
232233
};
233234
}
234235

235-
async generateObject<T extends z.ZodType>(options: {
236+
async generateObject<T extends xsschema.Schema>(options: {
236237
messages: BaseMessage[];
237238
model: MockModelType;
238239
schema: T;
239-
}): Promise<ProviderObjectResponse<MockGenerateObjectResult<z.infer<T>>, z.infer<T>>> {
240+
}): Promise<
241+
ProviderObjectResponse<MockGenerateObjectResult<xsschema.Infer<T>>, xsschema.Infer<T>>
242+
> {
240243
this.generateObjectCalls++;
241244
this.lastMessages = options.messages;
242245

@@ -245,7 +248,7 @@ class MockProvider implements LLMProvider<MockModelType> {
245248
name: "John Doe",
246249
age: 30,
247250
hobbies: ["reading", "gaming"],
248-
} as z.infer<T>,
251+
} as xsschema.Infer<T>,
249252
};
250253

251254
return {
@@ -260,11 +263,13 @@ class MockProvider implements LLMProvider<MockModelType> {
260263
};
261264
}
262265

263-
async streamObject<T extends z.ZodType>(options: {
266+
async streamObject<T extends xsschema.Schema>(options: {
264267
messages: BaseMessage[];
265268
model: MockModelType;
266269
schema: T;
267-
}): Promise<ProviderObjectStreamResponse<MockStreamObjectResult<z.infer<T>>, z.infer<T>>> {
270+
}): Promise<
271+
ProviderObjectStreamResponse<MockStreamObjectResult<xsschema.Infer<T>>, xsschema.Infer<T>>
272+
> {
268273
this.streamObjectCalls++;
269274
this.lastMessages = options.messages;
270275

@@ -278,9 +283,9 @@ class MockProvider implements LLMProvider<MockModelType> {
278283
},
279284
});
280285

281-
const partialObjectStream = new ReadableStream<Partial<z.infer<T>>>({
286+
const partialObjectStream = new ReadableStream<Partial<xsschema.Infer<T>>>({
282287
start(controller) {
283-
controller.enqueue({ name: "John" } as Partial<z.infer<T>>);
288+
controller.enqueue({ name: "John" } as Partial<xsschema.Infer<T>>);
284289
controller.close();
285290
},
286291
});

packages/core/src/agent/index.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { z } from "zod";
1+
import type * as xsschema from "xsschema";
22
import { AgentEventEmitter } from "../events";
33
import type { EventStatus, EventUpdater } from "../events";
44
import { MemoryManager } from "../memory";
@@ -1295,7 +1295,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
12951295
/**
12961296
* Generate a structured object response
12971297
*/
1298-
async generateObject<T extends z.ZodType>(
1298+
async generateObject<T extends xsschema.Schema>(
12991299
input: string | BaseMessage[],
13001300
schema: T,
13011301
options: PublicGenerateOptions = {},
@@ -1400,7 +1400,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
14001400
status: "completed" as any,
14011401
});
14021402
operationContext.isActive = false;
1403-
const standardizedOutput: StandardizedObjectResult<z.infer<T>> = {
1403+
const standardizedOutput: StandardizedObjectResult<xsschema.Infer<T>> = {
14041404
object: response.object,
14051405
usage: response.usage,
14061406
finishReason: response.finishReason,
@@ -1448,7 +1448,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
14481448
/**
14491449
* Stream a structured object response
14501450
*/
1451-
async streamObject<T extends z.ZodType>(
1451+
async streamObject<T extends xsschema.Schema>(
14521452
input: string | BaseMessage[],
14531453
schema: T,
14541454
options: PublicGenerateOptions = {},
@@ -1535,7 +1535,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
15351535
await (provider.onStepFinish as (step: StepWithContent) => Promise<void>)(step);
15361536
}
15371537
},
1538-
onFinish: async (result: StreamObjectFinishResult<z.infer<T>>) => {
1538+
onFinish: async (result: StreamObjectFinishResult<xsschema.Infer<T>>) => {
15391539
if (!operationContext.isActive) {
15401540
return;
15411541
}
@@ -1565,7 +1565,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
15651565
context: operationContext,
15661566
});
15671567
if (provider?.onFinish) {
1568-
await (provider.onFinish as StreamObjectOnFinishCallback<z.infer<T>>)(result);
1568+
await (provider.onFinish as StreamObjectOnFinishCallback<xsschema.Infer<T>>)(result);
15691569
}
15701570
},
15711571
onError: async (error: VoltAgentError) => {

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

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { z } from "zod";
1+
import type * as xsschema from "xsschema";
22
import type {
33
ProviderOptions,
44
ToolExecutionContext,
@@ -191,7 +191,7 @@ export type BaseMessage = {
191191
};
192192

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

196196
// Base tool types
197197
export type ToolExecuteOptions = {
@@ -274,7 +274,7 @@ export interface StreamTextOptions<TModel> {
274274
toolExecutionContext?: ToolExecutionContext;
275275
}
276276

277-
export interface GenerateObjectOptions<TModel, TSchema extends z.ZodType> {
277+
export interface GenerateObjectOptions<TModel, TSchema extends xsschema.Schema> {
278278
messages: BaseMessage[];
279279
model: TModel;
280280
schema: TSchema;
@@ -284,13 +284,13 @@ export interface GenerateObjectOptions<TModel, TSchema extends z.ZodType> {
284284
toolExecutionContext?: ToolExecutionContext;
285285
}
286286

287-
export interface StreamObjectOptions<TModel, TSchema extends z.ZodType> {
287+
export interface StreamObjectOptions<TModel, TSchema extends xsschema.Schema> {
288288
messages: BaseMessage[];
289289
model: TModel;
290290
schema: TSchema;
291291
provider?: ProviderOptions;
292292
onStepFinish?: StepFinishCallback;
293-
onFinish?: StreamObjectOnFinishCallback<z.infer<TSchema>>;
293+
onFinish?: StreamObjectOnFinishCallback<xsschema.Infer<TSchema>>;
294294
onError?: StreamOnErrorCallback;
295295
signal?: AbortSignal;
296296
toolExecutionContext?: ToolExecutionContext;
@@ -368,13 +368,15 @@ export type LLMProvider<TProvider> = {
368368
* Implementers should catch underlying SDK/API errors and throw a VoltAgentError.
369369
* @throws {VoltAgentError} If an error occurs during generation.
370370
*/
371-
generateObject<TSchema extends z.ZodType>(
371+
generateObject<TSchema extends xsschema.Schema>(
372372
options: GenerateObjectOptions<InferModel<TProvider>, TSchema>,
373-
): Promise<ProviderObjectResponse<InferGenerateObjectResponse<TProvider>, z.infer<TSchema>>>;
373+
): Promise<
374+
ProviderObjectResponse<InferGenerateObjectResponse<TProvider>, xsschema.Infer<TSchema>>
375+
>;
374376

375-
streamObject<TSchema extends z.ZodType>(
377+
streamObject<TSchema extends xsschema.Schema>(
376378
options: StreamObjectOptions<InferModel<TProvider>, TSchema>,
377-
): Promise<ProviderObjectStreamResponse<InferStreamResponse<TProvider>, z.infer<TSchema>>>;
379+
): Promise<ProviderObjectStreamResponse<InferStreamResponse<TProvider>, xsschema.Infer<TSchema>>>;
378380

379381
// Message conversion methods
380382
toMessage(message: BaseMessage): InferMessage<TProvider>;

packages/core/src/server/api.ts

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import { Hono } from "hono";
21
import { cors } from "hono/cors";
32
import { WebSocketServer } from "ws";
43
import type { WebSocket } from "ws";
5-
import { z } from "zod";
4+
import type * as xsschema from "xsschema";
65
import { OpenAPIHono } from "@hono/zod-openapi";
76
import { swaggerUI } from "@hono/swagger-ui";
87
import type { AgentHistoryEntry } from "../agent/history";
@@ -25,7 +24,6 @@ import {
2524
type ErrorSchema,
2625
type TextResponseSchema,
2726
type ObjectResponseSchema,
28-
type AgentResponseSchema,
2927
type TextRequestSchema,
3028
type ObjectRequestSchema,
3129
} from "./api.routes";
@@ -164,7 +162,7 @@ app.openapi(getAgentsRoute, (c) => {
164162
});
165163

166164
// Define the exact success response type based on the route schema
167-
type SuccessResponse = z.infer<
165+
type SuccessResponse = xsschema.Infer<
168166
(typeof getAgentsRoute.responses)[200]["content"]["application/json"]["schema"]
169167
>;
170168

@@ -177,7 +175,9 @@ app.openapi(getAgentsRoute, (c) => {
177175
} catch (error) {
178176
console.error("Failed to get agents:", error);
179177
return c.json(
180-
{ success: false, error: "Failed to retrieve agents" } satisfies z.infer<typeof ErrorSchema>,
178+
{ success: false, error: "Failed to retrieve agents" } satisfies xsschema.Infer<
179+
typeof ErrorSchema
180+
>,
181181
500,
182182
);
183183
}
@@ -259,22 +259,24 @@ app.openapi(textRoute, async (c) => {
259259

260260
if (!agent) {
261261
return c.json(
262-
{ success: false, error: "Agent not found" } satisfies z.infer<typeof ErrorSchema>,
262+
{ success: false, error: "Agent not found" } satisfies xsschema.Infer<typeof ErrorSchema>,
263263
404,
264264
);
265265
}
266266

267267
try {
268-
const { input, options = {} } = c.req.valid("json") as z.infer<typeof TextRequestSchema>;
268+
const { input, options = {} } = c.req.valid("json") as xsschema.Infer<typeof TextRequestSchema>;
269269

270270
const response = await agent.generateText(input, options);
271-
return c.json({ success: true, data: response } satisfies z.infer<typeof TextResponseSchema>);
271+
return c.json({ success: true, data: response } satisfies xsschema.Infer<
272+
typeof TextResponseSchema
273+
>);
272274
} catch (error) {
273275
return c.json(
274276
{
275277
success: false,
276278
error: error instanceof Error ? error.message : "Failed to generate text",
277-
} satisfies z.infer<typeof ErrorSchema>,
279+
} satisfies xsschema.Infer<typeof ErrorSchema>,
278280
500,
279281
);
280282
}
@@ -288,7 +290,7 @@ app.openapi(streamRoute, async (c) => {
288290

289291
if (!agent) {
290292
return c.json(
291-
{ success: false, error: "Agent not found" } satisfies z.infer<typeof ErrorSchema>,
293+
{ success: false, error: "Agent not found" } satisfies xsschema.Infer<typeof ErrorSchema>,
292294
404,
293295
);
294296
}
@@ -300,7 +302,7 @@ app.openapi(streamRoute, async (c) => {
300302
maxTokens: 4000,
301303
temperature: 0.7,
302304
},
303-
} = c.req.valid("json") as z.infer<typeof TextRequestSchema>;
305+
} = c.req.valid("json") as xsschema.Infer<typeof TextRequestSchema>;
304306

305307
const stream = new ReadableStream({
306308
async start(controller) {
@@ -367,7 +369,7 @@ app.openapi(streamRoute, async (c) => {
367369
{
368370
success: false,
369371
error: error instanceof Error ? error.message : "Failed to initiate text stream",
370-
} satisfies z.infer<typeof ErrorSchema>,
372+
} satisfies xsschema.Infer<typeof ErrorSchema>,
371373
500,
372374
);
373375
}
@@ -381,7 +383,7 @@ app.openapi(objectRoute, async (c) => {
381383

382384
if (!agent) {
383385
return c.json(
384-
{ success: false, error: "Agent not found" } satisfies z.infer<typeof ErrorSchema>,
386+
{ success: false, error: "Agent not found" } satisfies xsschema.Infer<typeof ErrorSchema>,
385387
404,
386388
);
387389
}
@@ -391,16 +393,19 @@ app.openapi(objectRoute, async (c) => {
391393
input,
392394
schema,
393395
options = {},
394-
} = c.req.valid("json") as z.infer<typeof ObjectRequestSchema>;
396+
} = c.req.valid("json") as xsschema.Infer<typeof ObjectRequestSchema>;
395397

396398
const response = await agent.generateObject(input, schema, options);
397-
return c.json({ success: true, data: response } satisfies z.infer<typeof ObjectResponseSchema>);
399+
return c.json(
400+
{ success: true, data: response } satisfies xsschema.Infer<typeof ObjectResponseSchema>,
401+
200,
402+
);
398403
} catch (error) {
399404
return c.json(
400405
{
401406
success: false,
402407
error: error instanceof Error ? error.message : "Failed to generate object",
403-
} satisfies z.infer<typeof ErrorSchema>,
408+
} satisfies xsschema.Infer<typeof ErrorSchema>,
404409
500,
405410
);
406411
}
@@ -414,7 +419,7 @@ app.openapi(streamObjectRoute, async (c) => {
414419

415420
if (!agent) {
416421
return c.json(
417-
{ success: false, error: "Agent not found" } satisfies z.infer<typeof ErrorSchema>,
422+
{ success: false, error: "Agent not found" } satisfies xsschema.Infer<typeof ErrorSchema>,
418423
404,
419424
);
420425
}
@@ -424,7 +429,7 @@ app.openapi(streamObjectRoute, async (c) => {
424429
input,
425430
schema,
426431
options = {},
427-
} = c.req.valid("json") as z.infer<typeof ObjectRequestSchema>;
432+
} = c.req.valid("json") as xsschema.Infer<typeof ObjectRequestSchema>;
428433

429434
const agentStream = await agent.streamObject(input, schema, options);
430435

@@ -487,7 +492,7 @@ app.openapi(streamObjectRoute, async (c) => {
487492
{
488493
success: false,
489494
error: error instanceof Error ? error.message : "Failed to initiate object stream",
490-
} satisfies z.infer<typeof ErrorSchema>,
495+
} satisfies xsschema.Infer<typeof ErrorSchema>,
491496
500,
492497
);
493498
}

packages/core/src/tool/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { v4 as uuidv4 } from "uuid";
22
import type { BaseTool, ToolExecuteOptions, ToolSchema } from "../agent/providers/base/types";
3-
import type { z } from "zod";
3+
import type * as xsschema from "xsschema";
44

55
// Export ToolManager and related types
66
export { ToolManager, ToolStatus, ToolStatusInfo } from "./manager";
@@ -39,7 +39,7 @@ export type ToolOptions<T extends ToolSchema = ToolSchema> = {
3939
/**
4040
* Function to execute when the tool is called
4141
*/
42-
execute: (args: z.infer<T>, options?: ToolExecuteOptions) => Promise<unknown>;
42+
execute: (args: xsschema.Infer<T>, options?: ToolExecuteOptions) => Promise<unknown>;
4343
};
4444

4545
/**
@@ -69,7 +69,7 @@ export class Tool<T extends ToolSchema = ToolSchema> /* implements BaseTool<z.in
6969
/**
7070
* Function to execute when the tool is called
7171
*/
72-
readonly execute: (args: z.infer<T>, options?: ToolExecuteOptions) => Promise<unknown>;
72+
readonly execute: (args: xsschema.Infer<T>, options?: ToolExecuteOptions) => Promise<unknown>;
7373

7474
/**
7575
* Create a new tool

packages/core/src/tool/reasoning/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { z } from "zod";
2+
import type * as xsschema from "xsschema";
23
import type { ToolExecuteOptions } from "../../agent/providers/base/types";
34

45
/**
@@ -30,7 +31,7 @@ export const ReasoningStepSchema = z.object({
3031
/**
3132
* TypeScript type inferred from the ReasoningStepSchema.
3233
*/
33-
export type ReasoningStep = z.infer<typeof ReasoningStepSchema>;
34+
export type ReasoningStep = xsschema.Infer<typeof ReasoningStepSchema>;
3435

3536
/**
3637
* Options specific to reasoning tool execution, extending base ToolExecuteOptions.

0 commit comments

Comments
 (0)