workflow 5.0.0-beta.5 → 5.0.0-beta.51

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 (256) hide show
  1. package/README.md +68 -23
  2. package/dist/api-workflow.d.ts +1 -1
  3. package/dist/api-workflow.d.ts.map +1 -1
  4. package/dist/api-workflow.js +1 -1
  5. package/dist/api.d.ts +3 -3
  6. package/dist/api.d.ts.map +1 -1
  7. package/dist/api.js +5 -7
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +6 -1
  11. package/dist/internal/builtins.d.ts +20 -3
  12. package/dist/internal/builtins.d.ts.map +1 -1
  13. package/dist/internal/builtins.js +68 -4
  14. package/dist/internal/errors.d.ts +1 -1
  15. package/dist/internal/errors.d.ts.map +1 -1
  16. package/dist/internal/errors.js +2 -2
  17. package/dist/nest-builder.d.ts +2 -0
  18. package/dist/nest-builder.d.ts.map +1 -0
  19. package/dist/nest-builder.js +2 -0
  20. package/dist/nest-vercel-builder.d.ts +2 -0
  21. package/dist/nest-vercel-builder.d.ts.map +1 -0
  22. package/dist/nest-vercel-builder.js +2 -0
  23. package/dist/observability.d.ts +1 -1
  24. package/dist/observability.js +2 -2
  25. package/dist/runtime.d.ts +2 -1
  26. package/dist/runtime.d.ts.map +1 -1
  27. package/dist/runtime.js +4 -1
  28. package/docs/ai/chat-session-modeling.mdx +176 -422
  29. package/docs/ai/defining-tools.mdx +6 -7
  30. package/docs/ai/human-in-the-loop.mdx +11 -11
  31. package/docs/ai/index.mdx +71 -75
  32. package/docs/ai/message-queueing.mdx +71 -110
  33. package/docs/ai/meta.json +1 -0
  34. package/docs/ai/resumable-streams.mdx +40 -28
  35. package/docs/ai/sleep-and-delays.mdx +10 -10
  36. package/docs/ai/streaming-updates-from-tools.mdx +6 -6
  37. package/docs/api-reference/index.mdx +25 -1
  38. package/docs/api-reference/meta.json +8 -0
  39. package/docs/api-reference/vitest/index.mdx +9 -15
  40. package/docs/api-reference/workflow/create-hook.mdx +89 -10
  41. package/docs/api-reference/workflow/create-webhook.mdx +16 -15
  42. package/docs/api-reference/workflow/define-hook.mdx +35 -33
  43. package/docs/api-reference/workflow/fatal-error.mdx +30 -8
  44. package/docs/api-reference/workflow/fetch.mdx +14 -10
  45. package/docs/api-reference/workflow/get-step-metadata.mdx +2 -2
  46. package/docs/api-reference/workflow/get-workflow-metadata.mdx +3 -3
  47. package/docs/api-reference/workflow/get-writable.mdx +7 -7
  48. package/docs/api-reference/workflow/index.mdx +4 -1
  49. package/docs/api-reference/workflow/retryable-error.mdx +1 -1
  50. package/docs/api-reference/workflow/set-attributes.mdx +61 -0
  51. package/docs/api-reference/workflow/sleep.mdx +4 -4
  52. package/docs/api-reference/workflow-ai/durable-agent.mdx +63 -101
  53. package/docs/api-reference/workflow-ai/index.mdx +5 -5
  54. package/docs/api-reference/workflow-ai/workflow-chat-transport.mdx +67 -24
  55. package/docs/api-reference/workflow-api/get-hook-by-token.mdx +26 -12
  56. package/docs/api-reference/workflow-api/get-run.mdx +43 -8
  57. package/docs/api-reference/workflow-api/index.mdx +6 -10
  58. package/docs/api-reference/workflow-api/resume-hook.mdx +73 -12
  59. package/docs/api-reference/workflow-api/resume-webhook.mdx +11 -9
  60. package/docs/api-reference/workflow-api/start.mdx +60 -13
  61. package/docs/api-reference/workflow-astro/index.mdx +18 -0
  62. package/docs/api-reference/workflow-astro/meta.json +4 -0
  63. package/docs/api-reference/workflow-astro/workflow.mdx +45 -0
  64. package/docs/api-reference/workflow-errors/entity-conflict-error.mdx +4 -4
  65. package/docs/api-reference/workflow-errors/hook-conflict-error.mdx +60 -0
  66. package/docs/api-reference/workflow-errors/hook-not-found-error.mdx +8 -8
  67. package/docs/api-reference/workflow-errors/index.mdx +88 -0
  68. package/docs/api-reference/workflow-errors/meta.json +6 -0
  69. package/docs/api-reference/workflow-errors/precondition-failed-error.mdx +68 -0
  70. package/docs/api-reference/workflow-errors/run-expired-error.mdx +2 -2
  71. package/docs/api-reference/workflow-errors/run-not-supported-error.mdx +58 -0
  72. package/docs/api-reference/workflow-errors/step-not-registered-error.mdx +5 -5
  73. package/docs/api-reference/workflow-errors/throttle-error.mdx +2 -2
  74. package/docs/api-reference/workflow-errors/too-early-error.mdx +2 -2
  75. package/docs/api-reference/workflow-errors/workflow-error.mdx +52 -0
  76. package/docs/api-reference/workflow-errors/workflow-not-registered-error.mdx +5 -6
  77. package/docs/api-reference/workflow-errors/workflow-run-cancelled-error.mdx +6 -6
  78. package/docs/api-reference/workflow-errors/workflow-run-failed-error.mdx +5 -5
  79. package/docs/api-reference/workflow-errors/workflow-run-not-completed-error.mdx +58 -0
  80. package/docs/api-reference/workflow-errors/workflow-run-not-found-error.mdx +4 -4
  81. package/docs/api-reference/workflow-errors/workflow-runtime-error.mdx +58 -0
  82. package/docs/api-reference/workflow-errors/workflow-world-error.mdx +8 -8
  83. package/docs/api-reference/workflow-globals.mdx +14 -10
  84. package/docs/api-reference/workflow-nest/configure-workflow-controller.mdx +33 -0
  85. package/docs/api-reference/workflow-nest/index.mdx +31 -0
  86. package/docs/api-reference/workflow-nest/meta.json +9 -0
  87. package/docs/api-reference/workflow-nest/nest-local-builder.mdx +64 -0
  88. package/docs/api-reference/workflow-nest/workflow-controller.mdx +40 -0
  89. package/docs/api-reference/workflow-nest/workflow-module.mdx +74 -0
  90. package/docs/api-reference/workflow-next/with-workflow.mdx +39 -17
  91. package/docs/api-reference/workflow-nitro/index.mdx +60 -0
  92. package/docs/api-reference/workflow-nuxt/index.mdx +48 -0
  93. package/docs/api-reference/workflow-observability/hydrate-data.mdx +35 -0
  94. package/docs/api-reference/workflow-observability/hydrate-resource-io.mdx +62 -0
  95. package/docs/api-reference/workflow-observability/index.mdx +62 -0
  96. package/docs/api-reference/workflow-observability/meta.json +11 -0
  97. package/docs/api-reference/workflow-observability/observability-revivers.mdx +50 -0
  98. package/docs/api-reference/workflow-observability/parse-class-name.mdx +41 -0
  99. package/docs/api-reference/workflow-observability/parse-step-name.mdx +40 -0
  100. package/docs/api-reference/workflow-observability/parse-workflow-name.mdx +55 -0
  101. package/docs/api-reference/workflow-runtime/create-world.mdx +39 -0
  102. package/docs/api-reference/workflow-runtime/get-world-handlers.mdx +44 -0
  103. package/docs/api-reference/{workflow-api → workflow-runtime}/get-world.mdx +11 -14
  104. package/docs/api-reference/workflow-runtime/health-check.mdx +50 -0
  105. package/docs/api-reference/workflow-runtime/index.mdx +41 -0
  106. package/docs/api-reference/workflow-runtime/meta.json +12 -0
  107. package/docs/api-reference/workflow-runtime/set-world.mdx +51 -0
  108. package/docs/api-reference/workflow-runtime/workflow-entrypoint.mdx +43 -0
  109. package/docs/api-reference/workflow-runtime/world/analytics.mdx +315 -0
  110. package/docs/api-reference/workflow-runtime/world/index.mdx +60 -0
  111. package/docs/api-reference/workflow-runtime/world/meta.json +4 -0
  112. package/docs/api-reference/workflow-runtime/world/queue.mdx +88 -0
  113. package/docs/api-reference/{workflow-api → workflow-runtime}/world/storage.mdx +98 -34
  114. package/docs/api-reference/{workflow-api → workflow-runtime}/world/streams.mdx +8 -8
  115. package/docs/api-reference/workflow-serde/index.mdx +1 -2
  116. package/docs/api-reference/workflow-serde/workflow-deserialize.mdx +3 -4
  117. package/docs/api-reference/workflow-serde/workflow-serialize.mdx +8 -8
  118. package/docs/api-reference/workflow-sveltekit/index.mdx +18 -0
  119. package/docs/api-reference/workflow-sveltekit/meta.json +4 -0
  120. package/docs/api-reference/workflow-sveltekit/workflow-plugin.mdx +42 -0
  121. package/docs/api-reference/workflow-vite/index.mdx +18 -0
  122. package/docs/api-reference/workflow-vite/meta.json +4 -0
  123. package/docs/api-reference/workflow-vite/workflow.mdx +48 -0
  124. package/docs/changelog/attributes-mvp.mdx +380 -0
  125. package/docs/changelog/batched-event-writes.mdx +79 -0
  126. package/docs/changelog/eager-processing.mdx +110 -436
  127. package/docs/changelog/index.mdx +4 -2
  128. package/docs/changelog/lazy-event-creation.md +127 -0
  129. package/docs/changelog/lazy-hook-resume.mdx +78 -0
  130. package/docs/changelog/meta.json +11 -1
  131. package/docs/changelog/resilient-resume.mdx +32 -0
  132. package/docs/changelog/resilient-start.mdx +33 -285
  133. package/docs/changelog/step-message-ownership.mdx +360 -0
  134. package/docs/changelog/turbo-mode.md +87 -0
  135. package/docs/comparisons/index.mdx +66 -0
  136. package/docs/comparisons/meta.json +11 -0
  137. package/docs/comparisons/workflow-sdk-vs-aws-agentcore.mdx +55 -0
  138. package/docs/comparisons/workflow-sdk-vs-aws-step-functions.mdx +111 -0
  139. package/docs/comparisons/workflow-sdk-vs-cloudflare-workflows.mdx +71 -0
  140. package/docs/comparisons/workflow-sdk-vs-inngest.mdx +102 -0
  141. package/docs/comparisons/workflow-sdk-vs-temporal.mdx +123 -0
  142. package/docs/comparisons/workflow-sdk-vs-trigger-dev.mdx +104 -0
  143. package/docs/configuration/build-and-diagnostics.mdx +70 -0
  144. package/docs/configuration/cli-and-web-ui.mdx +241 -0
  145. package/docs/configuration/framework-options.mdx +165 -0
  146. package/docs/configuration/index.mdx +32 -0
  147. package/docs/configuration/meta.json +12 -0
  148. package/docs/configuration/runtime-tuning.mdx +381 -0
  149. package/docs/configuration/worlds.mdx +313 -0
  150. package/docs/cookbook/advanced/child-workflows.mdx +211 -264
  151. package/docs/cookbook/advanced/meta.json +6 -1
  152. package/docs/cookbook/advanced/publishing-libraries.mdx +65 -56
  153. package/docs/cookbook/advanced/serializable-steps.mdx +48 -68
  154. package/docs/cookbook/advanced/upgrading-workflows.mdx +199 -0
  155. package/docs/cookbook/agent-patterns/agent-cancellation.mdx +78 -60
  156. package/docs/cookbook/agent-patterns/durable-agent.mdx +23 -131
  157. package/docs/cookbook/agent-patterns/human-in-the-loop.mdx +180 -195
  158. package/docs/cookbook/common-patterns/batching.mdx +18 -14
  159. package/docs/cookbook/common-patterns/idempotency.mdx +41 -53
  160. package/docs/cookbook/common-patterns/rate-limiting.mdx +8 -4
  161. package/docs/cookbook/common-patterns/saga.mdx +23 -19
  162. package/docs/cookbook/common-patterns/scheduling.mdx +34 -22
  163. package/docs/cookbook/common-patterns/sequential-and-parallel.mdx +29 -25
  164. package/docs/cookbook/common-patterns/timeouts.mdx +26 -21
  165. package/docs/cookbook/common-patterns/webhooks.mdx +10 -6
  166. package/docs/cookbook/common-patterns/workflow-composition.mdx +30 -27
  167. package/docs/cookbook/index.mdx +22 -21
  168. package/docs/cookbook/integrations/ai-sdk.mdx +86 -48
  169. package/docs/cookbook/integrations/chat-sdk.mdx +50 -33
  170. package/docs/cookbook/integrations/sandbox.mdx +62 -45
  171. package/docs/deploying.mdx +95 -0
  172. package/docs/errors/abort-signal-timeout-in-workflow.mdx +16 -12
  173. package/docs/errors/corrupted-event-log.mdx +39 -18
  174. package/docs/errors/deployment-mismatch.mdx +71 -0
  175. package/docs/errors/fetch-in-workflow.mdx +15 -14
  176. package/docs/errors/hook-conflict.mdx +69 -13
  177. package/docs/errors/index.mdx +2 -36
  178. package/docs/errors/node-js-module-in-workflow.mdx +9 -5
  179. package/docs/errors/replay-divergence.mdx +27 -0
  180. package/docs/errors/run-expired.mdx +85 -0
  181. package/docs/errors/runtime-decryption-failed.mdx +77 -0
  182. package/docs/errors/serialization-failed.mdx +44 -12
  183. package/docs/errors/start-invalid-workflow-function.mdx +9 -5
  184. package/docs/errors/step-executed-multiple-times.mdx +23 -0
  185. package/docs/errors/step-not-registered.mdx +6 -6
  186. package/docs/errors/timeout-in-workflow.mdx +12 -8
  187. package/docs/errors/webhook-invalid-respond-with-value.mdx +18 -18
  188. package/docs/errors/webhook-response-not-sent.mdx +20 -16
  189. package/docs/errors/workflow-not-registered.mdx +5 -5
  190. package/docs/foundations/cancellation.mdx +31 -32
  191. package/docs/foundations/errors-and-retries.mdx +42 -11
  192. package/docs/foundations/hooks.mdx +64 -35
  193. package/docs/foundations/idempotency.mdx +244 -12
  194. package/docs/foundations/index.mdx +1 -23
  195. package/docs/foundations/meta.json +2 -1
  196. package/docs/foundations/serialization.mdx +21 -22
  197. package/docs/foundations/starting-workflows.mdx +106 -30
  198. package/docs/foundations/streaming.mdx +108 -60
  199. package/docs/foundations/versioning.mdx +263 -0
  200. package/docs/foundations/workflows-and-steps.mdx +9 -9
  201. package/docs/getting-started/astro.mdx +22 -18
  202. package/docs/getting-started/express.mdx +15 -11
  203. package/docs/getting-started/fastify.mdx +15 -11
  204. package/docs/getting-started/hono.mdx +15 -11
  205. package/docs/getting-started/index.mdx +10 -3
  206. package/docs/getting-started/meta.json +3 -1
  207. package/docs/getting-started/nestjs.mdx +87 -20
  208. package/docs/getting-started/next.mdx +22 -16
  209. package/docs/getting-started/nitro.mdx +22 -18
  210. package/docs/getting-started/nuxt.mdx +15 -11
  211. package/docs/getting-started/python.mdx +190 -41
  212. package/docs/getting-started/react-router/index.mdx +33 -0
  213. package/docs/getting-started/react-router/meta.json +5 -0
  214. package/docs/getting-started/react-router/v7.mdx +237 -0
  215. package/docs/getting-started/react-router/v8.mdx +232 -0
  216. package/docs/getting-started/sveltekit.mdx +20 -16
  217. package/docs/getting-started/tanstack-start.mdx +17 -13
  218. package/docs/getting-started/vite.mdx +15 -11
  219. package/docs/how-it-works/cancellation.mdx +63 -63
  220. package/docs/how-it-works/code-transform.mdx +83 -67
  221. package/docs/how-it-works/encryption.mdx +30 -26
  222. package/docs/how-it-works/event-sourcing.mdx +125 -34
  223. package/docs/how-it-works/framework-integrations.mdx +96 -337
  224. package/docs/how-it-works/understanding-directives.mdx +22 -22
  225. package/docs/internal/index.mdx +6 -4
  226. package/docs/internal/meta.json +6 -1
  227. package/docs/internal/nitro-native-build.mdx +38 -0
  228. package/docs/internal/nitro-web-ui.mdx +24 -0
  229. package/docs/internal/serializable-abort-controller.mdx +7 -7
  230. package/docs/meta.json +3 -2
  231. package/docs/observability/attributes.mdx +134 -0
  232. package/docs/observability/index.mdx +32 -10
  233. package/docs/observability/meta.json +1 -1
  234. package/docs/observability/retention.mdx +95 -0
  235. package/docs/observability/tracing.mdx +124 -0
  236. package/docs/testing/index.mdx +36 -36
  237. package/docs/testing/server-based.mdx +10 -10
  238. package/docs/whats-new.mdx +186 -0
  239. package/package.json +17 -14
  240. package/docs/api-reference/workflow-api/world/index.mdx +0 -58
  241. package/docs/api-reference/workflow-api/world/meta.json +0 -4
  242. package/docs/api-reference/workflow-api/world/observability.mdx +0 -164
  243. package/docs/api-reference/workflow-api/world/queue.mdx +0 -86
  244. package/docs/deploying/building-a-world.mdx +0 -251
  245. package/docs/deploying/index.mdx +0 -95
  246. package/docs/deploying/meta.json +0 -4
  247. package/docs/deploying/world/local-world.mdx +0 -84
  248. package/docs/deploying/world/meta.json +0 -4
  249. package/docs/deploying/world/postgres-world.mdx +0 -224
  250. package/docs/deploying/world/vercel-world.mdx +0 -179
  251. package/docs/migration-guides/index.mdx +0 -34
  252. package/docs/migration-guides/meta.json +0 -9
  253. package/docs/migration-guides/migrating-from-aws-step-functions.mdx +0 -363
  254. package/docs/migration-guides/migrating-from-inngest.mdx +0 -314
  255. package/docs/migration-guides/migrating-from-temporal.mdx +0 -318
  256. package/docs/migration-guides/migrating-from-trigger-dev.mdx +0 -337
@@ -9,10 +9,14 @@ related:
9
9
  - /docs/foundations/workflows-and-steps
10
10
  ---
11
11
 
12
+ <CopyPrompt
13
+ text="In this Nuxt app, run `npm i workflow`. In `nuxt.config.ts`, add `modules: [&quot;workflow/nuxt&quot;]` and keep `compatibilityDate: &quot;latest&quot;`. Create `workflows/user-signup.ts` exporting `handleUserSignup(email)` with `&quot;use workflow&quot;`, `sleep` from `workflow`, and `&quot;use step&quot;` helpers that create a user and send emails. Add `server/api/signup.post.ts` using `defineEventHandler` from `h3` or `nitro/h3` and `start` from `workflow/api` to read `{ email }` and start the workflow. Run `npm run dev`, call `curl -X POST --json '{&quot;email&quot;:&quot;hello@example.com&quot;}' http://localhost:3000/api/signup`, and inspect with `npx workflow web` or `npx workflow inspect runs`."
14
+ />
15
+
12
16
  <Steps>
13
17
 
14
18
  <Step>
15
- ## Create Your Nuxt Project
19
+ ## Create your Nuxt project
16
20
 
17
21
  Start by creating a new Nuxt project. This command will create a new directory named `nuxt-app` and setup a Nuxt project inside it.
18
22
 
@@ -75,7 +79,7 @@ export default defineNuxtConfig({
75
79
 
76
80
  <Step>
77
81
 
78
- ## Create Your First Workflow
82
+ ## Create your first workflow
79
83
 
80
84
  Create a new file for our first workflow:
81
85
 
@@ -97,14 +101,14 @@ export async function handleUserSignup(email: string) {
97
101
  }
98
102
  ```
99
103
 
100
- We'll fill in those functions next, but let's take a look at this code:
104
+ We'll fill in those functions next. The current code does the following:
101
105
 
102
106
  - We define a **workflow** function with the directive `"use workflow"`. Think of the workflow function as the _orchestrator_ of individual **steps**.
103
107
  - The Workflow SDK's `sleep` function allows us to suspend execution of the workflow without using up any resources. A sleep can be a few seconds, hours, days, or even months long.
104
108
 
105
- ## Create Your Workflow Steps
109
+ ## Create your workflow steps
106
110
 
107
- Let's now define those missing functions.
111
+ Define the missing functions.
108
112
 
109
113
  ```typescript title="server/workflows/user-signup.ts" lineNumbers
110
114
  import { FatalError } from "workflow";
@@ -145,7 +149,7 @@ async function sendOnboardingEmail(user: { id: string; email: string }) {
145
149
 
146
150
  Taking a look at this code:
147
151
 
148
- - Business logic lives inside **steps**. When a step is invoked inside a **workflow**, it gets enqueued to run on a separate request while the workflow is suspended, just like `sleep`.
152
+ - Business logic lives inside **steps**. When a workflow invokes a step, the workflow suspends while the step runs with full Node.js access. The combined handler may execute it inline; queued retries and continuations return through the same flow route.
149
153
  - If a step throws an error, like in `sendWelcomeEmail`, the step will automatically be retried until it succeeds (or hits the step's max retry count).
150
154
  - Steps can throw a `FatalError` if an error is intentional and should not be retried.
151
155
 
@@ -158,7 +162,7 @@ Taking a look at this code:
158
162
 
159
163
  <Step>
160
164
 
161
- ## Create Your API Route
165
+ ## Create your API route
162
166
 
163
167
  To invoke your new workflow, we'll create a new API route handler at `server/api/signup.post.ts` with the following code:
164
168
 
@@ -223,7 +227,7 @@ npx workflow inspect runs
223
227
 
224
228
  ## Deploying to production
225
229
 
226
- Workflow SDK apps currently work best when deployed to [Vercel](https://vercel.com/home) and needs no special configuration.
230
+ Workflow SDK apps currently work best when deployed to [Vercel](https://vercel.com/home) and need no special configuration.
227
231
 
228
232
  <FluidComputeCallout />
229
233
 
@@ -235,8 +239,8 @@ Check the [Deploying](/docs/deploying) section to learn how your workflows can b
235
239
 
236
240
  If you see this error:
237
241
 
238
- ```
239
- 'start' received an invalid workflow function. Ensure the Workflow Development Kit is configured correctly and the function includes a 'use workflow' directive.
242
+ ```text
243
+ 'start' received an invalid workflow function. Ensure the Workflow SDK is configured correctly and the function includes a 'use workflow' directive.
240
244
  ```
241
245
 
242
246
  Check both of these first:
@@ -246,7 +250,7 @@ Check both of these first:
246
250
 
247
251
  See [start-invalid-workflow-function](/docs/errors/start-invalid-workflow-function) for full examples and fixes.
248
252
 
249
- ## Next Steps
253
+ ## Next steps
250
254
 
251
255
  - Learn more about the [Foundations](/docs/foundations).
252
256
  - Check [Errors](/docs/errors) if you encounter issues.
@@ -10,48 +10,50 @@ related:
10
10
  - /docs/foundations/workflows-and-steps
11
11
  ---
12
12
 
13
+ <CopyPrompt
14
+ text="Set up Workflow in this Python project. In `pyproject.toml`, add `requires-python = &quot;&gt;=3.12&quot;` and `dependencies = [&quot;vercel-workflow&quot;]` under `[project]`, then add `[[tool.vercel.workflows]]` with `entrypoint = &quot;app.workflows:wf&quot;`. Create `app/workflow.py` with `from vercel import workflow` and `wf = workflow.Workflows(sandbox_policy=workflow.SandboxPolicy(share_sandboxes=True))`. Create `app/steps/generate_draft.py`, import `wf`, and define async step functions such as `generate_draft` and `summarize_draft`, decorating each with `@wf.step`. Then create `app/workflows/ai_content_workflow.py`, import `wf` and those step functions, and define `@wf.workflow async def ai_content_workflow(*, topic: str)` to orchestrate them and return the result. In `app/workflows/__init__.py`, export `wf` and import the workflow module so its definitions are registered. From server-side code, start it with `await workflow.start(ai_content_workflow, topic=...)`; use the returned `Run` to access its ID, check its status, or await its return value. Where the workflow needs a durable delay, use `await workflow.sleep(timedelta(days=7))` after importing `timedelta` from `datetime`. Where it needs an external approval event, define a Pydantic model that also extends `workflow.BaseHook`, wait with `.wait(token=...)`, and resume it from server-side code with `.resume(token)`."
15
+ />
16
+
13
17
  <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).
18
+ The Python SDK is currently in **beta**. APIs and behavior may change.
15
19
  </Callout>
16
20
 
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
21
+ You can build durable workflows in Python using the [`vercel-workflow` SDK](https://pypi.org/project/vercel-workflow/). Your workflow code can pause, resume, and maintain state, just like the JavaScript and TypeScript Workflow SDK.
20
22
 
21
- Install the `vercel` package:
23
+ ## Getting started
22
24
 
23
- ```bash filename="Terminal"
24
- pip install vercel
25
- ```
25
+ Add the `vercel-workflow` package and workflow entrypoint to `pyproject.toml`:
26
26
 
27
- Configure `experimentalServices` in your `vercel.json`:
27
+ ```toml filename="pyproject.toml"
28
+ [project]
29
+ requires-python = ">=3.12"
30
+ dependencies = ["vercel-workflow"]
28
31
 
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
- }
32
+ [[tool.vercel.workflows]]
33
+ entrypoint = "app.workflows:wf"
39
34
  ```
40
35
 
36
+ The workflow `entrypoint` uses the `module:object` format and points to the exported `Workflows` registry.
37
+
41
38
  ## Workflows
42
39
 
43
40
  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
41
 
45
- ```python filename="app/workflow.py" {3}
42
+ ```python filename="app/workflow.py"
46
43
  from vercel import workflow
47
44
 
48
- wf = workflow.Workflows()
45
+ wf = workflow.Workflows(
46
+ sandbox_policy=workflow.SandboxPolicy(share_sandboxes=True), # [!code highlight]
47
+ )
49
48
  ```
50
49
 
51
- ```python filename="app/workflows/ai_content_workflow.py" {3}
50
+ `share_sandboxes=True` reuses a sandbox across workflow runs for faster startup. Module globals are shared between those runs, so workflow code should not mutate global state.
51
+
52
+ ```python filename="app/workflows/ai_content_workflow.py"
52
53
  from app.workflow import wf
54
+ from app.steps.generate_draft import generate_draft, summarize_draft
53
55
 
54
- @wf.workflow
56
+ @wf.workflow # [!code highlight]
55
57
  async def ai_content_workflow(*, topic: str):
56
58
  draft = await generate_draft(topic=topic)
57
59
  summary = await summarize_draft(draft=draft)
@@ -62,21 +64,30 @@ async def ai_content_workflow(*, topic: str):
62
64
  }
63
65
  ```
64
66
 
67
+ Export the registry from the workflow package and import the module containing your workflow so its definitions are registered:
68
+
69
+ ```python filename="app/workflows/__init__.py"
70
+ from app.workflow import wf
71
+ from app.workflows import ai_content_workflow
72
+
73
+ __all__ = ["ai_content_workflow", "wf"]
74
+ ```
75
+
65
76
  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
77
 
67
78
  ## Steps
68
79
 
69
80
  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
81
 
71
- ```python filename="app/steps/generate_draft.py" {4,8}
82
+ ```python filename="app/steps/generate_draft.py"
72
83
  import random
73
84
  from app.workflow import wf
74
85
 
75
- @wf.step
86
+ @wf.step # [!code highlight]
76
87
  async def generate_draft(*, topic: str):
77
88
  return await ai_generate(prompt=f"Write a blog post about {topic}")
78
89
 
79
- @wf.step
90
+ @wf.step # [!code highlight]
80
91
  async def summarize_draft(*, draft: str):
81
92
  summary = await ai_summarize(text=draft)
82
93
 
@@ -87,20 +98,71 @@ async def summarize_draft(*, draft: str):
87
98
  return summary
88
99
  ```
89
100
 
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.
101
+ Each step executes separately from the workflow orchestrator. While the step executes, the workflow suspends without consuming resources. When the step completes, the workflow resumes automatically where it left off.
102
+
103
+ ### Cancellable steps
104
+
105
+ Pass `cancellable=True` when a workflow may need to stop a running step. This makes normal Python `asyncio` cancellation apply to the step: if a task awaiting the step is cancelled in the workflow, then the task running the step will be cancelled as well. For example, `asyncio.timeout()` cancels the step if it does not finish within the allotted time:
106
+
107
+ ```python filename="app/workflows/report.py"
108
+ import asyncio
109
+ from datetime import timedelta
110
+
111
+ from app.workflow import wf
112
+
113
+
114
+ @wf.step(cancellable=True) # [!code highlight]
115
+ async def generate_report(*, account_id: str) -> dict[str, str]:
116
+ return await reporting_service.generate(account_id=account_id)
117
+
118
+
119
+ @wf.workflow
120
+ async def report_workflow(*, account_id: str) -> dict[str, str]:
121
+ try:
122
+ async with asyncio.timeout(timedelta(minutes=10).total_seconds()): # [!code highlight]
123
+ return await generate_report(account_id=account_id)
124
+ except TimeoutError:
125
+ return {"status": "timed_out"}
126
+ ```
127
+
128
+ Cancellation is a request, so the workflow waits for the step to terminate before continuing. If the step suppresses its cancellation, it can still return normally.
129
+
130
+ ## Starting a workflow
131
+
132
+ Call `workflow.start()` from server-side code to start a workflow. It returns a `Run` that you can use to identify the run, check its status, and wait for its result:
133
+
134
+ ```python filename="app/api/generate.py"
135
+ from app.workflows.ai_content_workflow import ai_content_workflow
136
+ from vercel import workflow
137
+
138
+ @app.post("/api/generate")
139
+ async def generate_content(*, topic: str):
140
+ run = await workflow.start(ai_content_workflow, topic=topic) # [!code highlight]
141
+
142
+ print(run.run_id)
143
+ print(await run.status()) # [!code highlight]
144
+
145
+ # Wait until the workflow completes and return its result.
146
+ return await run.return_value() # [!code highlight]
147
+ ```
148
+
149
+ Starting a workflow only waits until the run has been created and queued. Await `return_value()` to wait for the workflow to finish, or save its `run_id` and recreate the handle later with `workflow.Run(run_id)`.
91
150
 
92
151
  ## Sleep
93
152
 
94
153
  Sleep pauses a workflow for a specified duration without consuming compute resources:
95
154
 
96
- ```python filename="app/workflows/ai_refine.py" {7}
155
+ ```python filename="app/workflows/ai_refine.py"
156
+ from datetime import timedelta
157
+
158
+ from app.workflow import wf
97
159
  from vercel import workflow
98
160
 
99
161
  @wf.workflow
100
162
  async def ai_refine_workflow(*, draft_id: str):
101
163
  draft = await fetch_draft(draft_id)
102
164
 
103
- await workflow.sleep("7 days") # Wait 7 days to gather more signals.
165
+ await workflow.sleep(timedelta(days=7)) # Wait 7 days to gather more signals. # [!code highlight]
104
166
 
105
167
  refined = await refine_draft(draft)
106
168
 
@@ -110,7 +172,43 @@ async def ai_refine_workflow(*, draft_id: str):
110
172
  }
111
173
  ```
112
174
 
113
- The sleep call pauses the workflow and consumes no resources. The workflow resumes automatically when the time expires.
175
+ The parameter accepts four forms:
176
+
177
+ | Form | Description | Example |
178
+ | --- | --- | --- |
179
+ | `str` | Human-readable duration string | `"2 days"`, `"1w"`, `"1h 30m"` |
180
+ | `int` or `float` | Seconds from now | `5` (5 seconds) |
181
+ | `datetime.timedelta` | Duration from now | `timedelta(days=7)` |
182
+ | `datetime.datetime` | Absolute wake-up time (must be timezone-aware) | `datetime(2025, 1, 1, tzinfo=UTC)` |
183
+
184
+ The string form accepts one or more `<value><unit>` pairs. Supported units:
185
+
186
+ | Duration | Unit |
187
+ | --- | --- |
188
+ | Milliseconds | `ms` |
189
+ | Seconds | `s`, `second`, `seconds` |
190
+ | Minutes | `m`, `minute`, `minutes` |
191
+ | Hours | `h`, `hour`, `hours` |
192
+ | Days | `d`, `day`, `days` |
193
+ | Weeks | `w`, `week`, `weeks` |
194
+
195
+ <Callout>
196
+ `sleep()` must be called from the workflow body, not from inside a step. Calling it from a step raises a `RuntimeError`.
197
+ </Callout>
198
+
199
+ The sleep consumes no resources. The workflow resumes automatically when the time expires.
200
+
201
+ `asyncio.sleep()` may also be used to sleep.
202
+
203
+ ## Deterministic workflow helpers
204
+
205
+ Workflow bodies must be fully deterministic, and so are not allowed to perform operations like reading the system clock or generating randomness using the default generator. Deterministic replacements are provided for use in workflow bodies:
206
+
207
+ - `workflow.now()` is a deterministic substitute for `datetime.datetime.now`. It returns the time that the last workflow event occurred at.
208
+ - `workflow.time_ns()` is a deterministic substitute for `time.time_ns`.
209
+ - `workflow.random()` returns a `random.Random` instance with a seed based on the run id.
210
+
211
+ These helpers can only be called from a workflow body. Steps should use the normal system functions.
114
212
 
115
213
  ## Hooks
116
214
 
@@ -118,21 +216,25 @@ A hook lets a workflow wait for external events such as user actions, webhooks,
118
216
 
119
217
  Define a hook model with Pydantic and `workflow.BaseHook`:
120
218
 
121
- ```python filename="app/workflows/approval.py" {3,14}
219
+ ```python filename="app/workflows/approval.py"
220
+ import typing
221
+
222
+ import pydantic
223
+ from app.workflow import wf
122
224
  from vercel import workflow
123
225
 
124
- class Approval(BaseModel, workflow.BaseHook):
226
+ class Approval(pydantic.BaseModel, workflow.BaseHook): # [!code highlight]
125
227
  """Human approval for AI-generated drafts"""
126
228
 
127
- decision: Literal["approved", "changes"]
229
+ decision: typing.Literal["approved", "changes"]
128
230
  notes: str | None = None
129
231
 
130
232
  @wf.workflow
131
- async def ai_approval_workflow(*, topic: str):
233
+ async def ai_approval_workflow(*, topic: str) -> None:
132
234
  draft = await generate_draft(topic=topic)
133
235
 
134
236
  # Wait for human approval events
135
- async for event in Approval.wait(token="draft-123"):
237
+ async for event in Approval.wait(token="draft-123"): # [!code highlight]
136
238
  if event.decision == "approved":
137
239
  await publish_draft(draft)
138
240
  break
@@ -143,22 +245,69 @@ async def ai_approval_workflow(*, topic: str):
143
245
 
144
246
  Resume the workflow when data arrives:
145
247
 
146
- ```python filename="app/api/resume.py" {5}
248
+ ```python filename="app/api/resume.py"
249
+ from app.workflows.approval import Approval
250
+
147
251
  @app.post("/api/resume")
148
- async def resume(approval: Approval):
252
+ async def resume(approval: Approval): # [!code highlight]
149
253
  """Resume the workflow when an approval is received"""
150
254
 
151
- await approval.resume("draft-123")
255
+ hook = await approval.resume("draft-123") # [!code highlight]
256
+ print(f"Resumed workflow run: {hook.run_id}") # [!code highlight]
152
257
  return {"ok": True}
153
258
  ```
154
259
 
155
260
  When a hook receives data, the workflow resumes automatically. You don&apos;t need polling, message queues, or manual state management.
156
261
 
157
- ## Learn More
262
+ ## Streaming
263
+
264
+ Steps can stream progress while a workflow is running. Streams are associated with workflow runs, can be acquired from inside a workflow or inside a step, and can be passed to workflows and steps as arguments. If a type is specified when getting the stream (or on the type annotation of a step or workflow it is passed to), then Pydantic will be used to encode and decode the type.
265
+
266
+ ```python filename="app/workflows/streaming.py"
267
+ import pydantic
268
+
269
+ from app.workflow import wf
270
+ from vercel import workflow
271
+
272
+
273
+ class Progress(pydantic.BaseModel):
274
+ message: str
275
+
276
+
277
+ @wf.step
278
+ async def write_progress(
279
+ writable: workflow.WorkflowWritable[Progress], # [!code highlight]
280
+ ):
281
+ for message in ["Drafting", "Reviewing", "Complete"]:
282
+ await writable.write(Progress(message=message)) # [!code highlight]
283
+
284
+ await writable.close()
285
+
286
+ @wf.workflow
287
+ async def streaming_workflow():
288
+ writable = workflow.get_writable(type=Progress)
289
+ await write_progress(writable) # [!code highlight]
290
+ ```
291
+
292
+ Read and validate the typed values from the returned `Run` as they arrive:
293
+
294
+ ```python filename="app/api/stream.py"
295
+ from app.workflows.streaming import Progress, streaming_workflow
296
+ from vercel import workflow
297
+
298
+ @app.post("/api/stream")
299
+ async def stream_progress():
300
+ run = await workflow.start(streaming_workflow)
301
+
302
+ async for progress in run.readable(type=Progress): # [!code highlight]
303
+ print(progress.message)
304
+ ```
305
+
306
+ Streams are not closed automatically. It can be manually closed when it is finished so that readers know to terminate.
158
307
 
159
- For comprehensive documentation, examples, and the latest updates, visit the [official Vercel Workflow Python documentation](https://vercel.com/docs/workflow/python).
308
+ `readable` and `get_writable` also take a `namespace` parameter, allowing each run to have many distinct streams.
160
309
 
161
- ## Next Steps
310
+ ## Next steps
162
311
 
163
312
  - Learn more about the [Foundations](/docs/foundations).
164
313
  - Check [Errors](/docs/errors) if you encounter issues.
@@ -0,0 +1,33 @@
1
+ ---
2
+ title: React Router
3
+ description: Run durable workflows in a React Router framework-mode app using Nitro.
4
+ type: overview
5
+ summary: Choose your React Router version and connect React Router, Nitro, and Workflow SDK.
6
+ related:
7
+ - /docs/getting-started/nitro
8
+ - /docs/getting-started/vite
9
+ - /docs/foundations/workflows-and-steps
10
+ ---
11
+
12
+ React Router framework mode builds the browser application and its server-rendering code, but it still needs a server to receive requests. [Nitro](https://v3.nitro.build) provides that server. Workflow SDK integrates with Nitro to add the durable workflow routes and build artifacts.
13
+
14
+ The three pieces share one Vite build:
15
+
16
+ 1. **React Router** builds your routes, loaders, actions, and browser assets.
17
+ 2. **Nitro** runs the React Router request handler and any routes in `server/routes`.
18
+ 3. **Workflow SDK** finds files with `"use workflow"` and `"use step"`, then adds its runtime routes to Nitro.
19
+
20
+ Choose the guide that matches your React Router major version:
21
+
22
+ <AutoCards />
23
+
24
+ <Callout>
25
+ These guides require **Nitro v3**. Nitro v2 does not provide the Vite
26
+ environment integration used by this setup.
27
+ </Callout>
28
+
29
+ ## What the bridge does
30
+
31
+ The setup adds a small `server/ssr.ts` file. It turns React Router's generated server build into a standard Fetch API handler that Nitro can run. The Vite config then points Nitro's server and public output at the same `build` directory React Router uses.
32
+
33
+ This is configuration in your application, not a separate React Router adapter. Your React Router routes remain React Router routes, while Nitro owns the HTTP server and Workflow SDK uses Nitro's lifecycle and routing.
@@ -0,0 +1,5 @@
1
+ {
2
+ "title": "React Router",
3
+ "pages": ["v7", "v8"],
4
+ "defaultOpen": true
5
+ }