feat: add support for zod v4 (#151)

Co-authored-by: Thuc Pham <thuc@lingble.com>
This commit is contained in:
Marcus Schiesser
2025-08-28 10:18:59 +08:00
committed by GitHub
parent cbe6d5ebe7
commit 00724d2907
8 changed files with 53 additions and 19 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@llamaindex/workflow-core": patch
---
feat: add support for zod v4
+2
View File
@@ -39,6 +39,8 @@ wrappedWorkflow.handle(
server.tool(
"list directory",
{
// ModelContextProtocol SDK doesn't support zod v4 yet
// https://github.com/modelcontextprotocol/typescript-sdk/issues/555
filePath: z.string(),
},
mcpTool(wrappedWorkflow, startEvent, stopEvent),
+2 -2
View File
@@ -154,7 +154,7 @@
"stream-chain": "^3.4.0",
"tsdown": "^0.12.9",
"typescript": "^5.8.3",
"zod": "^3.25.67"
"zod": "^3.25.0 || ^4.0.0"
},
"peerDependencies": {
"@modelcontextprotocol/sdk": "^1.7.0",
@@ -162,7 +162,7 @@
"next": "^15.2.2",
"p-retry": "^6.2.1",
"rxjs": "^7.8.2",
"zod": "^3.24.2"
"zod": "^3.25.0 || ^4.0.0"
},
"license": "MIT",
"peerDependenciesMeta": {
+2 -4
View File
@@ -1,6 +1,5 @@
import { AsyncContext } from "@llamaindex/workflow-core/async-context";
import { z, type ZodRawShape, type ZodTypeAny } from "zod";
import type { Workflow, WorkflowEvent } from "@llamaindex/workflow-core";
import { type Workflow, type WorkflowEvent } from "@llamaindex/workflow-core";
import { runWorkflow } from "./stream/run";
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
@@ -18,8 +17,7 @@ export const getReqHandlerExtra = () => {
};
export function mcpTool<
Args extends ZodRawShape,
Start extends z.objectOutputType<Args, ZodTypeAny>,
Start extends { [x: string]: any },
Stop extends CallToolResult,
>(
workflow: Workflow,
+15 -4
View File
@@ -1,17 +1,28 @@
import { z } from "zod";
import * as z4 from "zod/v4/core";
import * as z3 from "zod/v3";
import {
workflowEvent,
type WorkflowEvent,
type WorkflowEventConfig,
} from "@llamaindex/workflow-core";
// Union type to support both Zod 3 and Zod 4 schemas
type ZodSchema<T> = z3.ZodType<T> | z4.$ZodType<T>;
export const zodEvent = <T, DebugLabel extends string>(
schema: z.ZodType<T>,
schema: ZodSchema<T>,
config?: WorkflowEventConfig<DebugLabel>,
): WorkflowEvent<T, DebugLabel> & { readonly schema: z.ZodType<T> } => {
): WorkflowEvent<T, DebugLabel> & { readonly schema: ZodSchema<T> } => {
const event = workflowEvent<T, DebugLabel>(config);
event.onInit(({ data }) => {
schema.parse(data);
// Runtime detection to handle both Zod 3 and Zod 4
if ("_zod" in schema) {
// Zod 4 schema
z4.parse(schema as z4.$ZodType<T>, data);
} else {
// Zod 3 schema
(schema as z3.ZodType<T>).parse(data);
}
});
return Object.assign(event, {
+22 -4
View File
@@ -1,12 +1,30 @@
import { describe, expect, test } from "vitest";
import { z } from "zod";
import * as z4 from "zod/v4";
import * as z3 from "zod/v3";
import { zodEvent } from "../../src/util/zod";
describe("zodEvent", () => {
test("can use zod schema to infer types and validate", () => {
const UserSchema = z.object({
name: z.string(),
age: z.number(),
const UserSchema = z3.object({
name: z3.string(),
age: z3.number(),
});
const event = zodEvent(UserSchema);
const schema = event.schema;
expect(schema).toBe(UserSchema);
const validData = { name: "John", age: 30 };
const invalidData = { name: "John", age: "30" };
expect(() => event.with(validData)).not.toThrow();
expect(() => event.with(invalidData as any)).toThrow();
});
test("can use zod v4 schema to infer types and validate", () => {
const UserSchema = z4.object({
name: z4.string(),
age: z4.number(),
});
const event = zodEvent(UserSchema);
+4 -4
View File
@@ -3,7 +3,7 @@ import tsconfigPaths from "vite-tsconfig-paths";
export default defineConfig({
test: {
workspace: [
projects: [
{
plugins: [
tsconfigPaths({
@@ -11,7 +11,7 @@ export default defineConfig({
}),
],
test: {
exclude: ["**/dist/**", "**/lib/**"],
exclude: ["**/dist/**", "**/lib/**", "**/node_modules/**"],
name: "DOM",
environment: "happy-dom",
},
@@ -23,7 +23,7 @@ export default defineConfig({
}),
],
test: {
exclude: ["**/dist/**", "**/lib/**"],
exclude: ["**/dist/**", "**/lib/**", "**/node_modules/**"],
name: "Node.js",
environment: "node",
},
@@ -35,7 +35,7 @@ export default defineConfig({
}),
],
test: {
exclude: ["**/dist/**", "**/lib/**"],
exclude: ["**/dist/**", "**/lib/**", "**/node_modules/**"],
name: "Edge Runtime",
environment: "edge-runtime",
},
+1 -1
View File
@@ -293,7 +293,7 @@ importers:
specifier: ^5.8.3
version: 5.8.3
zod:
specifier: ^3.25.67
specifier: ^3.25.0 || ^4.0.0
version: 3.25.67
packages/http: