workflow 5.0.0-beta.0 → 5.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +4 -4
  2. package/dist/api-workflow.d.ts +1 -3
  3. package/dist/api-workflow.d.ts.map +1 -1
  4. package/dist/api-workflow.js +2 -6
  5. package/dist/api.js +1 -1
  6. package/dist/astro.js +1 -1
  7. package/dist/index.js +1 -1
  8. package/dist/internal/builtins.js +1 -1
  9. package/dist/internal/class-serialization.js +1 -1
  10. package/dist/internal/errors.js +1 -1
  11. package/dist/nest.js +1 -1
  12. package/dist/next.cjs +4 -2
  13. package/dist/next.d.cts +1 -1
  14. package/dist/next.d.cts.map +1 -1
  15. package/dist/nitro.js +1 -1
  16. package/dist/nuxt.js +1 -1
  17. package/dist/observability.d.ts +1 -1
  18. package/dist/observability.js +2 -2
  19. package/dist/runtime.js +1 -1
  20. package/dist/stdlib.js +1 -1
  21. package/dist/sveltekit.js +1 -1
  22. package/dist/typescript-plugin.cjs +1 -1
  23. package/dist/vite.js +1 -1
  24. package/dist/workflow.js +1 -1
  25. package/docs/ai/resumable-streams.mdx +1 -1
  26. package/docs/api-reference/workflow/create-webhook.mdx +37 -18
  27. package/docs/api-reference/workflow/get-workflow-metadata.mdx +61 -0
  28. package/docs/api-reference/workflow-ai/durable-agent.mdx +0 -4
  29. package/docs/api-reference/workflow-ai/index.mdx +0 -5
  30. package/docs/api-reference/workflow-ai/workflow-chat-transport.mdx +0 -4
  31. package/docs/api-reference/workflow-api/get-world.mdx +6 -6
  32. package/docs/api-reference/workflow-api/index.mdx +1 -1
  33. package/docs/api-reference/workflow-api/world/index.mdx +2 -2
  34. package/docs/api-reference/workflow-api/world/observability.mdx +1 -1
  35. package/docs/api-reference/workflow-api/world/queue.mdx +1 -1
  36. package/docs/api-reference/workflow-api/world/storage.mdx +8 -8
  37. package/docs/api-reference/workflow-api/world/streams.mdx +38 -36
  38. package/docs/cookbook/advanced/custom-serialization.mdx +168 -0
  39. package/docs/cookbook/advanced/durable-objects.mdx +148 -0
  40. package/docs/cookbook/advanced/isomorphic-packages.mdx +145 -0
  41. package/docs/cookbook/advanced/meta.json +10 -0
  42. package/docs/cookbook/advanced/publishing-libraries.mdx +279 -0
  43. package/docs/cookbook/advanced/serializable-steps.mdx +135 -0
  44. package/docs/cookbook/agent-patterns/durable-agent.mdx +191 -0
  45. package/docs/cookbook/agent-patterns/human-in-the-loop.mdx +278 -0
  46. package/docs/cookbook/agent-patterns/meta.json +10 -0
  47. package/docs/cookbook/agent-patterns/stop-workflow.mdx +216 -0
  48. package/docs/cookbook/agent-patterns/tool-orchestration.mdx +255 -0
  49. package/docs/cookbook/agent-patterns/tool-streaming.mdx +181 -0
  50. package/docs/cookbook/common-patterns/batching.mdx +179 -0
  51. package/docs/cookbook/common-patterns/child-workflows.mdx +372 -0
  52. package/docs/cookbook/common-patterns/content-router.mdx +207 -0
  53. package/docs/cookbook/common-patterns/fan-out.mdx +208 -0
  54. package/docs/cookbook/common-patterns/idempotency.mdx +107 -0
  55. package/docs/cookbook/common-patterns/meta.json +15 -0
  56. package/docs/cookbook/common-patterns/rate-limiting.mdx +228 -0
  57. package/docs/cookbook/common-patterns/saga.mdx +152 -0
  58. package/docs/cookbook/common-patterns/scheduling.mdx +249 -0
  59. package/docs/cookbook/common-patterns/webhooks.mdx +185 -0
  60. package/docs/cookbook/index.mdx +41 -0
  61. package/docs/cookbook/integrations/ai-sdk.mdx +204 -0
  62. package/docs/cookbook/integrations/chat-sdk.mdx +203 -0
  63. package/docs/cookbook/integrations/meta.json +4 -0
  64. package/docs/cookbook/integrations/sandbox.mdx +128 -0
  65. package/docs/cookbook/meta.json +5 -0
  66. package/docs/deploying/building-a-world.mdx +45 -43
  67. package/docs/deploying/world/local-world.mdx +1 -1
  68. package/docs/deploying/world/postgres-world.mdx +10 -5
  69. package/docs/deploying/world/vercel-world.mdx +1 -1
  70. package/docs/errors/start-invalid-workflow-function.mdx +1 -1
  71. package/docs/getting-started/index.mdx +8 -1
  72. package/docs/getting-started/meta.json +2 -1
  73. package/docs/getting-started/next.mdx +24 -0
  74. package/docs/getting-started/python.mdx +165 -0
  75. package/docs/how-it-works/code-transform.mdx +6 -5
  76. package/docs/meta.json +1 -0
  77. package/docs/migration-guides/index.mdx +34 -0
  78. package/docs/migration-guides/meta.json +9 -0
  79. package/docs/migration-guides/migrating-from-aws-step-functions.mdx +311 -0
  80. package/docs/migration-guides/migrating-from-inngest.mdx +282 -0
  81. package/docs/migration-guides/migrating-from-temporal.mdx +284 -0
  82. package/docs/migration-guides/migrating-from-trigger-dev.mdx +296 -0
  83. package/package.json +13 -14
  84. package/dist/internal/private.d.ts +0 -6
  85. package/dist/internal/private.d.ts.map +0 -1
  86. package/dist/internal/private.js +0 -6
@@ -53,7 +53,8 @@ Create an `instrumentation.ts` file in your project root:
53
53
  export async function register() {
54
54
  if (process.env.NEXT_RUNTIME !== "edge") {
55
55
  const { getWorld } = await import("workflow/runtime");
56
- await getWorld().start?.();
56
+ const world = await getWorld();
57
+ await world.start?.();
57
58
  }
58
59
  }
59
60
  ```
@@ -73,7 +74,8 @@ import type { ServerInit } from "@sveltejs/kit";
73
74
 
74
75
  export const init: ServerInit = async () => {
75
76
  const { getWorld } = await import("workflow/runtime");
76
- await getWorld().start?.();
77
+ const world = await getWorld();
78
+ await world.start?.();
77
79
  };
78
80
  ```
79
81
 
@@ -92,7 +94,8 @@ import { defineNitroPlugin } from "nitro/~internal/runtime/plugin";
92
94
 
93
95
  export default defineNitroPlugin(async () => {
94
96
  const { getWorld } = await import("workflow/runtime");
95
- await getWorld().start?.();
97
+ const world = await getWorld();
98
+ await world.start?.();
96
99
  });
97
100
  ```
98
101
 
@@ -138,7 +141,7 @@ If `WORKFLOW_POSTGRES_URL` is not set, the CLI defaults to `postgres://world:wor
138
141
 
139
142
  Learn more in the [Observability](/docs/observability) documentation.
140
143
 
141
- ## Testing & Performance
144
+ ## Testing & Compatibility
142
145
 
143
146
  <WorldTestingPerformance />
144
147
 
@@ -168,7 +171,8 @@ For higher worker concurrency, Graphile Worker recommends setting `maxPoolSize`
168
171
 
169
172
  ### Programmatic configuration
170
173
 
171
- {/* @skip-typecheck: incomplete code sample */}
174
+ {/*@skip-typecheck: incomplete code sample*/}
175
+
172
176
  ```typescript title="workflow.config.ts" lineNumbers
173
177
  import { createWorld } from "@workflow/world-postgres";
174
178
 
@@ -200,6 +204,7 @@ Deploy your application to any cloud that supports long-running servers:
200
204
  - Platform-as-a-Service providers (Railway, Render, Fly.io, etc.)
201
205
 
202
206
  Ensure your deployment has:
207
+
203
208
  1. Network access to your PostgreSQL database
204
209
  2. Environment variables configured correctly
205
210
  3. The `start()` function called on server initialization
@@ -83,7 +83,7 @@ npx workflow inspect runs \
83
83
 
84
84
  Learn more in the [Observability](/docs/observability) documentation.
85
85
 
86
- ## Testing & Performance
86
+ ## Testing & Compatibility
87
87
 
88
88
  <WorldTestingPerformance />
89
89
 
@@ -123,7 +123,7 @@ Before calling `start()`:
123
123
  1. Confirm the function includes `"use workflow"` as its first statement.
124
124
  2. Confirm your framework integration is enabled (for Next.js, wrap `next.config.ts` with [`withWorkflow()`](/docs/api-reference/workflow-next/with-workflow)).
125
125
  3. Pass the imported workflow function directly to `start()`, not a wrapper callback.
126
- 4. Keep the function in a file that goes through Workflow DevKit's transform step.
126
+ 4. Keep the function in a file that goes through Workflow SDK's transform step.
127
127
 
128
128
  ## Related
129
129
 
@@ -8,7 +8,7 @@ related:
8
8
  - /docs/foundations/workflows-and-steps
9
9
  ---
10
10
 
11
- import { Next, Nitro, SvelteKit, Nuxt, Hono, Bun, AstroDark, AstroLight, TanStack, Vite, Express, Nest, Fastify } from "@/app/[lang]/(home)/components/frameworks";
11
+ import { Next, Nitro, SvelteKit, Nuxt, Hono, Bun, AstroDark, AstroLight, TanStack, Vite, Express, Nest, Fastify, Python } from "@/app/[lang]/(home)/components/frameworks";
12
12
 
13
13
  <Cards>
14
14
  <Card href="/docs/getting-started/next">
@@ -63,6 +63,13 @@ import { Next, Nitro, SvelteKit, Nuxt, Hono, Bun, AstroDark, AstroLight, TanStac
63
63
  <span className="font-medium">SvelteKit</span>
64
64
  </div>
65
65
  </Card>
66
+ <Card href="/docs/getting-started/python">
67
+ <div className="flex flex-col items-center justify-center gap-2">
68
+ <Python className="size-16" />
69
+ <span className="font-medium">Python</span>
70
+ <Badge variant="secondary">Beta</Badge>
71
+ </div>
72
+ </Card>
66
73
  <Card className="opacity-50">
67
74
  <div className="flex flex-col items-center justify-center gap-2">
68
75
  <Nest className="size-16 dark:invert grayscale" />
@@ -9,7 +9,8 @@
9
9
  "nitro",
10
10
  "nuxt",
11
11
  "sveltekit",
12
- "vite"
12
+ "vite",
13
+ "python"
13
14
  ],
14
15
  "defaultOpen": true
15
16
  }
@@ -281,6 +281,30 @@ Upgrade to `workflow@4.0.1-beta.26` or later:
281
281
  workflow@latest
282
282
  ```
283
283
 
284
+ ### Turborepo caching
285
+
286
+ If you're using [Turborepo](https://turbo.build/repo) in a monorepo, you need to include the generated Workflow routes in your cache outputs. The Workflow SDK generates route handlers at `app/.well-known/workflow/` (or `src/app/.well-known/workflow/` if your project uses the `src` directory) during the build process, and these files must be cached alongside your Next.js build output.
287
+
288
+ Add the following to your `turbo.json`:
289
+
290
+ ```jsonc title="turbo.json"
291
+ {
292
+ "tasks": {
293
+ "build": {
294
+ "outputs": [
295
+ ".next/**",
296
+ "!.next/cache/**",
297
+ // Include whichever path matches your project layout
298
+ "app/.well-known/workflow/**",
299
+ "src/app/.well-known/workflow/**"
300
+ ]
301
+ }
302
+ }
303
+ }
304
+ ```
305
+
306
+ Without this configuration, you may experience intermittent issues where workflows fail to register properly on cache hits, while working correctly on cache misses.
307
+
284
308
  ### `start()` says it received an invalid workflow function
285
309
 
286
310
  If you see this error:
@@ -0,0 +1,165 @@
1
+ ---
2
+ title: Python
3
+ description: Build durable workflows and AI agents in Python with the Vercel SDK.
4
+ type: guide
5
+ summary: Set up the Workflow Python SDK in your Python application.
6
+ prerequisites:
7
+ - /docs/getting-started
8
+ related:
9
+ - /docs/foundations
10
+ - /docs/foundations/workflows-and-steps
11
+ ---
12
+
13
+ <Callout type="warn">
14
+ The Python SDK is currently in **beta**. APIs and behavior may change. For the latest documentation and updates, see the [official Vercel Workflow Python documentation](https://vercel.com/docs/workflow/python?language=py).
15
+ </Callout>
16
+
17
+ You can build durable workflows in Python using the [`vercel` Python SDK](https://pypi.org/project/vercel/). Your workflow code can pause, resume, and maintain state, just like the JavaScript and TypeScript Workflow SDK.
18
+
19
+ ## Getting Started
20
+
21
+ Install the `vercel` package:
22
+
23
+ ```bash filename="Terminal"
24
+ pip install vercel
25
+ ```
26
+
27
+ Configure `experimentalServices` in your `vercel.json`:
28
+
29
+ ```json filename="vercel.json"
30
+ {
31
+ "experimentalServices": {
32
+ "ai_content_workflow": {
33
+ "type": "worker",
34
+ "entrypoint": "app/workflows/ai_content_workflow.py",
35
+ "topics": ["__wkf_*"]
36
+ }
37
+ }
38
+ }
39
+ ```
40
+
41
+ ## Workflows
42
+
43
+ A workflow is a stateful function that coordinates multi-step logic over time. Create a `Workflows` instance and use the `@wf.workflow` decorator to mark a function as durable:
44
+
45
+ ```python filename="app/workflow.py" {3}
46
+ from vercel import workflow
47
+
48
+ wf = workflow.Workflows()
49
+ ```
50
+
51
+ ```python filename="app/workflows/ai_content_workflow.py" {3}
52
+ from app.workflow import wf
53
+
54
+ @wf.workflow
55
+ async def ai_content_workflow(*, topic: str):
56
+ draft = await generate_draft(topic=topic)
57
+ summary = await summarize_draft(draft=draft)
58
+
59
+ return {
60
+ "draft": draft,
61
+ "summary": summary,
62
+ }
63
+ ```
64
+
65
+ Under the hood, the workflow compiles into a route that orchestrates execution. All inputs and outputs are recorded in an event log. If a deploy or crash happens, the system replays execution deterministically from where it stopped.
66
+
67
+ ## Steps
68
+
69
+ A step is a stateless function that runs a unit of durable work inside a workflow. Use `@wf.step` to mark a function as a step:
70
+
71
+ ```python filename="app/steps/generate_draft.py" {4,8}
72
+ import random
73
+ from app.workflow import wf
74
+
75
+ @wf.step
76
+ async def generate_draft(*, topic: str):
77
+ return await ai_generate(prompt=f"Write a blog post about {topic}")
78
+
79
+ @wf.step
80
+ async def summarize_draft(*, draft: str):
81
+ summary = await ai_summarize(text=draft)
82
+
83
+ # Simulate a transient error. The step automatically retries.
84
+ if random.random() < 0.3:
85
+ raise Exception("Transient AI provider error")
86
+
87
+ return summary
88
+ ```
89
+
90
+ Each step compiles into an isolated route. While the step executes, the workflow suspends without consuming resources. When the step completes, the workflow resumes automatically where it left off.
91
+
92
+ ## Sleep
93
+
94
+ Sleep pauses a workflow for a specified duration without consuming compute resources:
95
+
96
+ ```python filename="app/workflows/ai_refine.py" {7}
97
+ from vercel import workflow
98
+
99
+ @wf.workflow
100
+ async def ai_refine_workflow(*, draft_id: str):
101
+ draft = await fetch_draft(draft_id)
102
+
103
+ await workflow.sleep("7 days") # Wait 7 days to gather more signals.
104
+
105
+ refined = await refine_draft(draft)
106
+
107
+ return {
108
+ "draft_id": draft_id,
109
+ "refined": refined,
110
+ }
111
+ ```
112
+
113
+ The sleep call pauses the workflow and consumes no resources. The workflow resumes automatically when the time expires.
114
+
115
+ ## Hooks
116
+
117
+ A hook lets a workflow wait for external events such as user actions, webhooks, or third-party API responses.
118
+
119
+ Define a hook model with Pydantic and `workflow.BaseHook`:
120
+
121
+ ```python filename="app/workflows/approval.py" {3,14}
122
+ from vercel import workflow
123
+
124
+ class Approval(BaseModel, workflow.BaseHook):
125
+ """Human approval for AI-generated drafts"""
126
+
127
+ decision: Literal["approved", "changes"]
128
+ notes: str | None = None
129
+
130
+ @wf.workflow
131
+ async def ai_approval_workflow(*, topic: str):
132
+ draft = await generate_draft(topic=topic)
133
+
134
+ # Wait for human approval events
135
+ async for event in Approval.wait(token="draft-123"):
136
+ if event.decision == "approved":
137
+ await publish_draft(draft)
138
+ break
139
+
140
+ revised = await refine_draft(draft, event.notes)
141
+ await publish_draft(revised)
142
+ ```
143
+
144
+ Resume the workflow when data arrives:
145
+
146
+ ```python filename="app/api/resume.py" {5}
147
+ @app.post("/api/resume")
148
+ async def resume(approval: Approval):
149
+ """Resume the workflow when an approval is received"""
150
+
151
+ await approval.resume("draft-123")
152
+ return {"ok": True}
153
+ ```
154
+
155
+ When a hook receives data, the workflow resumes automatically. You don&apos;t need polling, message queues, or manual state management.
156
+
157
+ ## Learn More
158
+
159
+ For comprehensive documentation, examples, and the latest updates, visit the [official Vercel Workflow Python documentation](https://vercel.com/docs/workflow/python).
160
+
161
+ ## Next Steps
162
+
163
+ - Learn more about the [Foundations](/docs/foundations).
164
+ - Check [Errors](/docs/errors) if you encounter issues.
165
+ - Explore the [API Reference](/docs/api-reference).
@@ -89,20 +89,21 @@ export async function createUser(email: string) {
89
89
 
90
90
  {/* @skip-typecheck: incomplete code sample */}
91
91
  ```typescript
92
- import { registerStepFunction } from "workflow/internal/private"; // [!code highlight]
93
-
94
92
  export async function createUser(email: string) {
95
93
  return { id: crypto.randomUUID(), email };
96
94
  }
97
-
98
- registerStepFunction("step//workflows/user.js//createUser", createUser); // [!code highlight]
95
+ (function(__wf_fn, __wf_id) { // [!code highlight]
96
+ var __wf_sym = Symbol.for("@workflow/core//registeredSteps"), __wf_reg = globalThis[__wf_sym] || (globalThis[__wf_sym] = new Map()); // [!code highlight]
97
+ __wf_reg.set(__wf_id, __wf_fn); // [!code highlight]
98
+ __wf_fn.stepId = __wf_id; // [!code highlight]
99
+ })(createUser, "step//workflows/user.js//createUser"); // [!code highlight]
99
100
  ```
100
101
 
101
102
  **What happens:**
102
103
 
103
104
  - The `"use step"` directive is removed
104
105
  - The function body is kept completely intact (no transformation)
105
- - The function is registered with the runtime using `registerStepFunction()`
106
+ - The function is registered with the runtime via an inline IIFE (no imports needed)
106
107
  - Step functions run with full Node.js/Deno/Bun access
107
108
 
108
109
  **Why no transformation?** Step functions execute in your main runtime with full access to Node.js APIs, file system, databases, etc. They don't need any special handling—they just run normally.
package/docs/meta.json CHANGED
@@ -10,6 +10,7 @@
10
10
  "testing",
11
11
  "deploying",
12
12
  "errors",
13
+ "migration-guides",
13
14
  "api-reference"
14
15
  ]
15
16
  }
@@ -0,0 +1,34 @@
1
+ ---
2
+ title: Migration Guides
3
+ description: Move your existing durable workflow system to the Workflow SDK with side-by-side code comparisons and a realistic migration example.
4
+ type: overview
5
+ summary: Migrate from Temporal, Inngest, AWS Step Functions, or trigger.dev to the Workflow SDK.
6
+ related:
7
+ - /docs/foundations/workflows-and-steps
8
+ - /docs/getting-started
9
+ ---
10
+
11
+ <Callout type="info">
12
+ Install the Workflow SDK migration skill:
13
+
14
+ ```bash
15
+ npx skills add https://github.com/vercel/workflow --skill migrating-to-workflow-sdk
16
+ ```
17
+ </Callout>
18
+
19
+ Move an existing orchestration system to the Workflow SDK. Each guide pairs a concept-mapping table with side-by-side code, so you can translate one piece of your codebase at a time.
20
+
21
+ <Cards>
22
+ <Card href="/docs/migration-guides/migrating-from-temporal" title="Migrating from Temporal">
23
+ Map Activities, Workers, Signals, and Child Workflows onto workflows, steps, hooks, and `start()` / `getRun()`.
24
+ </Card>
25
+ <Card href="/docs/migration-guides/migrating-from-inngest" title="Migrating from Inngest">
26
+ Map `createFunction`, `step.run`, `step.sleep`, `step.waitForEvent`, and `step.invoke` onto workflows, steps, and hooks.
27
+ </Card>
28
+ <Card href="/docs/migration-guides/migrating-from-aws-step-functions" title="Migrating from AWS Step Functions">
29
+ Replace ASL JSON states, Task / Choice / Wait / Parallel states, and `.waitForTaskToken` callbacks with TypeScript.
30
+ </Card>
31
+ <Card href="/docs/migration-guides/migrating-from-trigger-dev" title="Migrating from trigger.dev">
32
+ Map `task()`, `schemaTask()`, `wait.for` / `wait.forToken`, `triggerAndWait`, and `metadata.stream` onto workflows, steps, hooks, and `start()` / `getRun()`.
33
+ </Card>
34
+ </Cards>
@@ -0,0 +1,9 @@
1
+ {
2
+ "title": "Migration Guides",
3
+ "pages": [
4
+ "migrating-from-temporal",
5
+ "migrating-from-inngest",
6
+ "migrating-from-aws-step-functions",
7
+ "migrating-from-trigger-dev"
8
+ ]
9
+ }