mirror of
https://github.com/run-llama/workflows-ts.git
synced 2026-07-21 06:05:23 -04:00
feat: add support for zod v4 (#151)
Co-authored-by: Thuc Pham <thuc@lingble.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@llamaindex/workflow-core": patch
|
||||
---
|
||||
|
||||
feat: add support for zod v4
|
||||
@@ -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),
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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, {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
|
||||
Generated
+1
-1
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user