Skip to content

Commit 08c4fe1

Browse files
committed
feat(core): support valibot and other schema libraries via xsschema, not just zod
1 parent e0b1c7a commit 08c4fe1

File tree

9 files changed

+152
-106
lines changed

9 files changed

+152
-106
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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"uuid": "^9.0.1",
3939
"ws": "^8.18.1",
4040
"zod": "3.24.2",
41+
"xsschema": "0.3.0-beta.2",
4142
"zod-from-json-schema": "^0.0.5"
4243
},
4344
"devDependencies": {

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, Memory } from "../memory/types";
56
import { AgentRegistry } from "../server/registry";
@@ -245,11 +246,13 @@ class MockProvider implements LLMProvider<MockModelType> {
245246
};
246247
}
247248

248-
async generateObject<T extends z.ZodType>(options: {
249+
async generateObject<T extends xsschema.Schema>(options: {
249250
messages: BaseMessage[];
250251
model: MockModelType;
251252
schema: T;
252-
}): Promise<ProviderObjectResponse<MockGenerateObjectResult<z.infer<T>>, z.infer<T>>> {
253+
}): Promise<
254+
ProviderObjectResponse<MockGenerateObjectResult<xsschema.Infer<T>>, xsschema.Infer<T>>
255+
> {
253256
this.generateObjectCalls++;
254257
this.lastMessages = options.messages;
255258

@@ -258,7 +261,7 @@ class MockProvider implements LLMProvider<MockModelType> {
258261
name: "John Doe",
259262
age: 30,
260263
hobbies: ["reading", "gaming"],
261-
} as z.infer<T>,
264+
} as xsschema.Infer<T>,
262265
};
263266

264267
return {
@@ -273,11 +276,13 @@ class MockProvider implements LLMProvider<MockModelType> {
273276
};
274277
}
275278

276-
async streamObject<T extends z.ZodType>(options: {
279+
async streamObject<T extends xsschema.Schema>(options: {
277280
messages: BaseMessage[];
278281
model: MockModelType;
279282
schema: T;
280-
}): Promise<ProviderObjectStreamResponse<MockStreamObjectResult<z.infer<T>>, z.infer<T>>> {
283+
}): Promise<
284+
ProviderObjectStreamResponse<MockStreamObjectResult<xsschema.Infer<T>>, xsschema.Infer<T>>
285+
> {
281286
this.streamObjectCalls++;
282287
this.lastMessages = options.messages;
283288

@@ -291,9 +296,9 @@ class MockProvider implements LLMProvider<MockModelType> {
291296
},
292297
});
293298

294-
const partialObjectStream = new ReadableStream<Partial<z.infer<T>>>({
299+
const partialObjectStream = new ReadableStream<Partial<xsschema.Infer<T>>>({
295300
start(controller) {
296-
controller.enqueue({ name: "John" } as Partial<z.infer<T>>);
301+
controller.enqueue({ name: "John" } as Partial<xsschema.Infer<T>>);
297302
controller.close();
298303
},
299304
});

packages/core/src/agent/index.ts

Lines changed: 33 additions & 33 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 } from "../events";
44
import type { StandardEventData } from "../events/types";
@@ -155,7 +155,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
155155
voice?: Voice;
156156
markdown?: boolean;
157157
telemetryExporter?: VoltAgentExporter;
158-
},
158+
}
159159
) {
160160
this.id = options.id || options.name;
161161
this.name = options.name;
@@ -191,7 +191,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
191191
this.id,
192192
this.memoryManager,
193193
options.maxHistoryEntries || 0,
194-
chosenExporter,
194+
chosenExporter
195195
);
196196
}
197197

@@ -362,7 +362,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
362362
// Generate the supervisor message with the agents memory inserted
363363
finalInstructions = this.subAgentManager.generateSupervisorSystemMessage(
364364
finalInstructions,
365-
agentsMemory,
365+
agentsMemory
366366
);
367367

368368
return {
@@ -408,7 +408,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
408408
*/
409409
private async formatInputMessages(
410410
messages: BaseMessage[],
411-
input: string | BaseMessage[],
411+
input: string | BaseMessage[]
412412
): Promise<BaseMessage[]> {
413413
if (typeof input === "string") {
414414
// Add user message to the messages array
@@ -444,7 +444,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
444444
// Ensure operationContext exists before proceeding
445445
if (!operationContext) {
446446
devLogger.warn(
447-
`[Agent ${this.id}] Missing operationContext in prepareTextOptions. Tool execution context might be incomplete.`,
447+
`[Agent ${this.id}] Missing operationContext in prepareTextOptions. Tool execution context might be incomplete.`
448448
);
449449
// Potentially handle this case more gracefully, e.g., throw an error or create a default context
450450
}
@@ -479,7 +479,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
479479

480480
if (!reasoningOptions.historyEntryId || reasoningOptions.historyEntryId === "unknown") {
481481
devLogger.warn(
482-
`Executing reasoning tool '${tool.name}' without a known historyEntryId within the operation context.`,
482+
`Executing reasoning tool '${tool.name}' without a known historyEntryId within the operation context.`
483483
);
484484
}
485485
// Pass the correctly typed options
@@ -541,7 +541,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
541541
conversationId?: string;
542542
} = {
543543
operationName: "unknown",
544-
},
544+
}
545545
): Promise<OperationContext> {
546546
const otelSpan = startOperationSpan({
547547
agentId: this.id,
@@ -642,7 +642,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
642642
*/
643643
private async updateHistoryEntry(
644644
context: OperationContext,
645-
updates: Partial<AgentHistoryEntry>,
645+
updates: Partial<AgentHistoryEntry>
646646
): Promise<void> {
647647
await this.historyManager.updateEntry(context.historyEntry.id, updates);
648648
}
@@ -654,7 +654,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
654654
context: OperationContext,
655655
toolName: string,
656656
status: EventStatus,
657-
data: Partial<StandardEventData> & Record<string, unknown> = {},
657+
data: Partial<StandardEventData> & Record<string, unknown> = {}
658658
): void => {
659659
// Ensure the toolSpans map exists on the context
660660
if (!context.toolSpans) {
@@ -688,7 +688,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
688688
context: OperationContext,
689689
eventName: string,
690690
status: AgentStatus,
691-
data: Partial<StandardEventData> & Record<string, unknown> = {},
691+
data: Partial<StandardEventData> & Record<string, unknown> = {}
692692
): void => {
693693
// Retrieve the OpenTelemetry span from the context
694694
const otelSpan = context.otelSpan;
@@ -701,7 +701,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
701701
});
702702
} else {
703703
devLogger.warn(
704-
`OpenTelemetry span not found in OperationContext for agent event ${eventName} (Operation ID: ${context.operationId})`,
704+
`OpenTelemetry span not found in OperationContext for agent event ${eventName} (Operation ID: ${context.operationId})`
705705
);
706706
}
707707
};
@@ -713,7 +713,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
713713
context: OperationContext,
714714
toolCallId: string,
715715
toolName: string,
716-
resultData: { result?: any; content?: any; error?: any },
716+
resultData: { result?: any; content?: any; error?: any }
717717
): void {
718718
const toolSpan = context.toolSpans?.get(toolCallId);
719719

@@ -722,7 +722,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
722722
context.toolSpans?.delete(toolCallId); // Remove from map after ending
723723
} else {
724724
devLogger.warn(
725-
`OTEL tool span not found for toolCallId: ${toolCallId} in _endOtelToolSpan (Tool: ${toolName})`,
725+
`OTEL tool span not found for toolCallId: ${toolCallId} in _endOtelToolSpan (Tool: ${toolName})`
726726
);
727727
}
728728
}
@@ -732,7 +732,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
732732
*/
733733
async generateText(
734734
input: string | BaseMessage[],
735-
options: PublicGenerateOptions = {},
735+
options: PublicGenerateOptions = {}
736736
): Promise<InferGenerateTextResponse<TProvider>> {
737737
const internalOptions: InternalGenerateOptions = options as InternalGenerateOptions;
738738
const {
@@ -759,7 +759,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
759759
input,
760760
userId,
761761
initialConversationId,
762-
contextLimit,
762+
contextLimit
763763
);
764764

765765
if (operationContext.otelSpan) {
@@ -827,7 +827,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
827827
const onStepFinish = this.memoryManager.createStepFinishHandler(
828828
operationContext,
829829
userId,
830-
finalConversationId,
830+
finalConversationId
831831
);
832832
const { tools, maxSteps } = this.prepareTextOptions({
833833
...internalOptions,
@@ -1153,7 +1153,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
11531153
*/
11541154
async streamText(
11551155
input: string | BaseMessage[],
1156-
options: PublicGenerateOptions = {},
1156+
options: PublicGenerateOptions = {}
11571157
): Promise<InferStreamTextResponse<TProvider>> {
11581158
const internalOptions: InternalGenerateOptions = options as InternalGenerateOptions;
11591159
const {
@@ -1180,7 +1180,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
11801180
input,
11811181
userId,
11821182
initialConversationId,
1183-
contextLimit,
1183+
contextLimit
11841184
);
11851185

11861186
if (operationContext.otelSpan) {
@@ -1245,7 +1245,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
12451245
const onStepFinish = this.memoryManager.createStepFinishHandler(
12461246
operationContext,
12471247
userId,
1248-
finalConversationId,
1248+
finalConversationId
12491249
);
12501250
const { tools, maxSteps } = this.prepareTextOptions({
12511251
...internalOptions,
@@ -1407,7 +1407,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
14071407
await onStepFinish(step);
14081408
if (internalOptions.provider?.onStepFinish) {
14091409
await (internalOptions.provider.onStepFinish as (step: StepWithContent) => Promise<void>)(
1410-
step,
1410+
step
14111411
);
14121412
}
14131413
this.addStepToHistory(step, operationContext);
@@ -1536,7 +1536,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
15361536
} catch (updateError) {
15371537
devLogger.error(
15381538
`[Agent ${this.id}] Failed to update tool event to error status for ${toolName} (${toolCallId}):`,
1539-
updateError,
1539+
updateError
15401540
);
15411541
}
15421542
const tool = this.toolManager.getToolByName(toolName);
@@ -1633,10 +1633,10 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
16331633
/**
16341634
* Generate a structured object response
16351635
*/
1636-
async generateObject<T extends z.ZodType>(
1636+
async generateObject<T extends xsschema.Schema>(
16371637
input: string | BaseMessage[],
16381638
schema: T,
1639-
options: PublicGenerateOptions = {},
1639+
options: PublicGenerateOptions = {}
16401640
): Promise<InferGenerateObjectResponse<TProvider>> {
16411641
const internalOptions: InternalGenerateOptions = options as InternalGenerateOptions;
16421642
const {
@@ -1663,7 +1663,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
16631663
input,
16641664
userId,
16651665
initialConversationId,
1666-
contextLimit,
1666+
contextLimit
16671667
);
16681668

16691669
if (operationContext.otelSpan) {
@@ -1730,7 +1730,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
17301730
const onStepFinish = this.memoryManager.createStepFinishHandler(
17311731
operationContext,
17321732
userId,
1733-
finalConversationId,
1733+
finalConversationId
17341734
);
17351735

17361736
const response = await this.llm.generateObject({
@@ -1817,7 +1817,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
18171817
status: "completed" as any,
18181818
});
18191819

1820-
const standardizedOutput: StandardizedObjectResult<z.infer<T>> = {
1820+
const standardizedOutput: StandardizedObjectResult<xsschema.Infer<T>> = {
18211821
object: response.object,
18221822
usage: response.usage,
18231823
finishReason: response.finishReason,
@@ -1912,10 +1912,10 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
19121912
/**
19131913
* Stream a structured object response
19141914
*/
1915-
async streamObject<T extends z.ZodType>(
1915+
async streamObject<T extends xsschema.Schema>(
19161916
input: string | BaseMessage[],
19171917
schema: T,
1918-
options: PublicGenerateOptions = {},
1918+
options: PublicGenerateOptions = {}
19191919
): Promise<InferStreamObjectResponse<TProvider>> {
19201920
const internalOptions: InternalGenerateOptions = options as InternalGenerateOptions;
19211921
const {
@@ -1943,7 +1943,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
19431943
input,
19441944
userId,
19451945
initialConversationId,
1946-
contextLimit,
1946+
contextLimit
19471947
);
19481948

19491949
if (operationContext.otelSpan) {
@@ -2008,7 +2008,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
20082008
const onStepFinish = this.memoryManager.createStepFinishHandler(
20092009
operationContext,
20102010
userId,
2011-
finalConversationId,
2011+
finalConversationId
20122012
);
20132013

20142014
try {
@@ -2030,7 +2030,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
20302030
await (provider.onStepFinish as (step: StepWithContent) => Promise<void>)(step);
20312031
}
20322032
},
2033-
onFinish: async (result: StreamObjectFinishResult<z.infer<T>>) => {
2033+
onFinish: async (result: StreamObjectFinishResult<xsschema.Infer<T>>) => {
20342034
if (!operationContext.isActive) {
20352035
return;
20362036
}
@@ -2108,7 +2108,7 @@ export class Agent<TProvider extends { llm: LLMProvider<unknown> }> {
21082108
context: operationContext,
21092109
});
21102110
if (provider?.onFinish) {
2111-
await (provider.onFinish as StreamObjectOnFinishCallback<z.infer<T>>)(result);
2111+
await (provider.onFinish as StreamObjectOnFinishCallback<xsschema.Infer<T>>)(result);
21122112
}
21132113
},
21142114
onError: async (error: VoltAgentError) => {

0 commit comments

Comments
 (0)