workflow 5.0.0-beta.5 → 5.0.0-beta.50

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 +29 -26
  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 +50 -45
  32. package/docs/ai/message-queueing.mdx +16 -16
  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 +24 -0
  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 +48 -86
  53. package/docs/api-reference/workflow-ai/index.mdx +3 -3
  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 +376 -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 +28 -20
  154. package/docs/cookbook/advanced/upgrading-workflows.mdx +199 -0
  155. package/docs/cookbook/agent-patterns/agent-cancellation.mdx +27 -19
  156. package/docs/cookbook/agent-patterns/durable-agent.mdx +14 -142
  157. package/docs/cookbook/agent-patterns/human-in-the-loop.mdx +30 -22
  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 +85 -47
  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 +29 -18
  174. package/docs/errors/deployment-mismatch.mdx +71 -0
  175. package/docs/errors/fetch-in-workflow.mdx +11 -7
  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 +107 -59
  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 +135 -40
  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 +98 -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 +93 -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
@@ -10,48 +10,46 @@ 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()`. 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() # [!code highlight]
49
46
  ```
50
47
 
51
- ```python filename="app/workflows/ai_content_workflow.py" {3}
48
+ ```python filename="app/workflows/ai_content_workflow.py"
52
49
  from app.workflow import wf
50
+ from app.steps.generate_draft import generate_draft, summarize_draft
53
51
 
54
- @wf.workflow
52
+ @wf.workflow # [!code highlight]
55
53
  async def ai_content_workflow(*, topic: str):
56
54
  draft = await generate_draft(topic=topic)
57
55
  summary = await summarize_draft(draft=draft)
@@ -62,21 +60,30 @@ async def ai_content_workflow(*, topic: str):
62
60
  }
63
61
  ```
64
62
 
63
+ Export the registry from the workflow package and import the module containing your workflow so its definitions are registered:
64
+
65
+ ```python filename="app/workflows/__init__.py"
66
+ from app.workflow import wf
67
+ from app.workflows import ai_content_workflow
68
+
69
+ __all__ = ["ai_content_workflow", "wf"]
70
+ ```
71
+
65
72
  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
73
 
67
74
  ## Steps
68
75
 
69
76
  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
77
 
71
- ```python filename="app/steps/generate_draft.py" {4,8}
78
+ ```python filename="app/steps/generate_draft.py"
72
79
  import random
73
80
  from app.workflow import wf
74
81
 
75
- @wf.step
82
+ @wf.step # [!code highlight]
76
83
  async def generate_draft(*, topic: str):
77
84
  return await ai_generate(prompt=f"Write a blog post about {topic}")
78
85
 
79
- @wf.step
86
+ @wf.step # [!code highlight]
80
87
  async def summarize_draft(*, draft: str):
81
88
  summary = await ai_summarize(text=draft)
82
89
 
@@ -87,20 +94,44 @@ async def summarize_draft(*, draft: str):
87
94
  return summary
88
95
  ```
89
96
 
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.
97
+ 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.
98
+
99
+ ## Starting a workflow
100
+
101
+ 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:
102
+
103
+ ```python filename="app/api/generate.py"
104
+ from app.workflows.ai_content_workflow import ai_content_workflow
105
+ from vercel import workflow
106
+
107
+ @app.post("/api/generate")
108
+ async def generate_content(*, topic: str):
109
+ run = await workflow.start(ai_content_workflow, topic=topic) # [!code highlight]
110
+
111
+ print(run.run_id)
112
+ print(await run.status()) # [!code highlight]
113
+
114
+ # Wait until the workflow completes and return its result.
115
+ return await run.return_value() # [!code highlight]
116
+ ```
117
+
118
+ 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
119
 
92
120
  ## Sleep
93
121
 
94
122
  Sleep pauses a workflow for a specified duration without consuming compute resources:
95
123
 
96
- ```python filename="app/workflows/ai_refine.py" {7}
124
+ ```python filename="app/workflows/ai_refine.py"
125
+ from datetime import timedelta
126
+
127
+ from app.workflow import wf
97
128
  from vercel import workflow
98
129
 
99
130
  @wf.workflow
100
131
  async def ai_refine_workflow(*, draft_id: str):
101
132
  draft = await fetch_draft(draft_id)
102
133
 
103
- await workflow.sleep("7 days") # Wait 7 days to gather more signals.
134
+ await workflow.sleep(timedelta(days=7)) # Wait 7 days to gather more signals. # [!code highlight]
104
135
 
105
136
  refined = await refine_draft(draft)
106
137
 
@@ -110,7 +141,31 @@ async def ai_refine_workflow(*, draft_id: str):
110
141
  }
111
142
  ```
112
143
 
113
- The sleep call pauses the workflow and consumes no resources. The workflow resumes automatically when the time expires.
144
+ The parameter accepts four forms:
145
+
146
+ | Form | Description | Example |
147
+ | --- | --- | --- |
148
+ | `str` | Human-readable duration string | `"2 days"`, `"1w"`, `"1h 30m"` |
149
+ | `int` or `float` | Seconds from now | `5` (5 seconds) |
150
+ | `datetime.timedelta` | Duration from now | `timedelta(days=7)` |
151
+ | `datetime.datetime` | Absolute wake-up time (must be timezone-aware) | `datetime(2025, 1, 1, tzinfo=UTC)` |
152
+
153
+ The string form accepts one or more `<value><unit>` pairs. Supported units:
154
+
155
+ | Duration | Unit |
156
+ | --- | --- |
157
+ | Milliseconds | `ms` |
158
+ | Seconds | `s`, `second`, `seconds` |
159
+ | Minutes | `m`, `minute`, `minutes` |
160
+ | Hours | `h`, `hour`, `hours` |
161
+ | Days | `d`, `day`, `days` |
162
+ | Weeks | `w`, `week`, `weeks` |
163
+
164
+ <Callout>
165
+ `sleep()` must be called from the workflow body, not from inside a step. Calling it from a step raises a `RuntimeError`.
166
+ </Callout>
167
+
168
+ The sleep consumes no resources. The workflow resumes automatically when the time expires.
114
169
 
115
170
  ## Hooks
116
171
 
@@ -118,13 +173,17 @@ A hook lets a workflow wait for external events such as user actions, webhooks,
118
173
 
119
174
  Define a hook model with Pydantic and `workflow.BaseHook`:
120
175
 
121
- ```python filename="app/workflows/approval.py" {3,14}
176
+ ```python filename="app/workflows/approval.py"
177
+ import typing
178
+
179
+ import pydantic
180
+ from app.workflow import wf
122
181
  from vercel import workflow
123
182
 
124
- class Approval(BaseModel, workflow.BaseHook):
183
+ class Approval(pydantic.BaseModel, workflow.BaseHook): # [!code highlight]
125
184
  """Human approval for AI-generated drafts"""
126
185
 
127
- decision: Literal["approved", "changes"]
186
+ decision: typing.Literal["approved", "changes"]
128
187
  notes: str | None = None
129
188
 
130
189
  @wf.workflow
@@ -132,7 +191,7 @@ async def ai_approval_workflow(*, topic: str):
132
191
  draft = await generate_draft(topic=topic)
133
192
 
134
193
  # Wait for human approval events
135
- async for event in Approval.wait(token="draft-123"):
194
+ async for event in Approval.wait(token="draft-123"): # [!code highlight]
136
195
  if event.decision == "approved":
137
196
  await publish_draft(draft)
138
197
  break
@@ -143,22 +202,58 @@ async def ai_approval_workflow(*, topic: str):
143
202
 
144
203
  Resume the workflow when data arrives:
145
204
 
146
- ```python filename="app/api/resume.py" {5}
205
+ ```python filename="app/api/resume.py"
206
+ from app.workflows.approval import Approval
207
+
147
208
  @app.post("/api/resume")
148
- async def resume(approval: Approval):
209
+ async def resume(approval: Approval): # [!code highlight]
149
210
  """Resume the workflow when an approval is received"""
150
211
 
151
- await approval.resume("draft-123")
212
+ await approval.resume("draft-123") # [!code highlight]
152
213
  return {"ok": True}
153
214
  ```
154
215
 
155
216
  When a hook receives data, the workflow resumes automatically. You don&apos;t need polling, message queues, or manual state management.
156
217
 
157
- ## Learn More
218
+ ## Streaming
219
+
220
+ Steps can stream progress while a workflow is running. Get the run&apos;s writable stream inside a step, write values to it, and close it when no more values will be sent:
221
+
222
+ ```python filename="app/workflows/streaming.py"
223
+ from app.workflow import wf
224
+ from vercel import workflow
225
+
226
+ @wf.step
227
+ async def write_progress():
228
+ writable = workflow.get_writable() # [!code highlight]
229
+
230
+ for message in ["Drafting", "Reviewing", "Complete"]:
231
+ await writable.write(message) # [!code highlight]
232
+
233
+ await writable.close()
234
+
235
+ @wf.workflow
236
+ async def streaming_workflow():
237
+ await write_progress()
238
+ ```
239
+
240
+ Read the values from the returned `Run` as they arrive:
241
+
242
+ ```python filename="app/api/stream.py"
243
+ from app.workflows.streaming import streaming_workflow
244
+ from vercel import workflow
245
+
246
+ @app.post("/api/stream")
247
+ async def stream_progress():
248
+ run = await workflow.start(streaming_workflow)
249
+
250
+ async for message in run.readable(): # [!code highlight]
251
+ print(message)
252
+ ```
158
253
 
159
- For comprehensive documentation, examples, and the latest updates, visit the [official Vercel Workflow Python documentation](https://vercel.com/docs/workflow/python).
254
+ Streams are not closed automatically. Close the writable in the last step that writes to it so readers know when the stream is complete.
160
255
 
161
- ## Next Steps
256
+ ## Next steps
162
257
 
163
258
  - Learn more about the [Foundations](/docs/foundations).
164
259
  - 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
+ }
@@ -0,0 +1,237 @@
1
+ ---
2
+ title: React Router v7
3
+ description: Add durable workflows to a React Router v7 framework-mode app using Nitro v3.
4
+ type: guide
5
+ summary: Enable the Vite Environment API and configure React Router v7, Nitro v3, and Workflow SDK.
6
+ prerequisites:
7
+ - /docs/getting-started/react-router
8
+ related:
9
+ - /docs/getting-started/nitro
10
+ - /docs/foundations/workflows-and-steps
11
+ ---
12
+
13
+ This guide starts with an existing React Router v7 framework-mode app. It is verified with v7.18.1; if your config does not recognize `v8_viteEnvironmentApi`, update to the latest v7 release.
14
+
15
+ <Steps>
16
+
17
+ <Step>
18
+
19
+ ## Install Nitro and Workflow SDK
20
+
21
+ <Tabs items={["npm", "pnpm", "bun", "yarn"]} defaultValue="pnpm">
22
+
23
+ <Tab value="npm">
24
+
25
+ ```bash
26
+ npm install nitro workflow
27
+ ```
28
+
29
+ </Tab>
30
+
31
+ <Tab value="pnpm">
32
+
33
+ ```bash
34
+ pnpm add nitro workflow
35
+ ```
36
+
37
+ </Tab>
38
+
39
+ <Tab value="bun">
40
+
41
+ ```bash
42
+ bun add nitro workflow
43
+ ```
44
+
45
+ </Tab>
46
+
47
+ <Tab value="yarn">
48
+
49
+ ```bash
50
+ yarn add nitro workflow
51
+ ```
52
+
53
+ </Tab>
54
+
55
+ </Tabs>
56
+
57
+ This integration requires Nitro v3.
58
+
59
+ </Step>
60
+
61
+ <Step>
62
+
63
+ ## Enable the Vite Environment API
64
+
65
+ React Router v7 keeps the Vite Environment API behind a future flag. Enable the required flag and set an explicit build directory:
66
+
67
+ ```typescript title="react-router.config.ts" lineNumbers
68
+ import type { Config } from "@react-router/dev/config";
69
+
70
+ export default {
71
+ ssr: true,
72
+ buildDirectory: "build", // [!code highlight]
73
+ future: {
74
+ v8_viteEnvironmentApi: true, // [!code highlight]
75
+ },
76
+ } satisfies Config;
77
+ ```
78
+
79
+ </Step>
80
+
81
+ <Step>
82
+
83
+ ## Create the React Router server handler
84
+
85
+ Create `server/ssr.ts`:
86
+
87
+ ```typescript title="server/ssr.ts" lineNumbers
88
+ import { createRequestHandler } from "react-router";
89
+
90
+ export default {
91
+ fetch: createRequestHandler(
92
+ () => import("virtual:react-router/server-build"),
93
+ import.meta.env.MODE,
94
+ ),
95
+ };
96
+ ```
97
+
98
+ This adapts React Router's generated server build to the Fetch API handler Nitro expects.
99
+
100
+ </Step>
101
+
102
+ <Step>
103
+
104
+ ## Configure Vite
105
+
106
+ Update `vite.config.ts`:
107
+
108
+ ```typescript title="vite.config.ts" lineNumbers
109
+ import { reactRouter } from "@react-router/dev/vite";
110
+ import { nitro } from "nitro/vite";
111
+ import { defineConfig } from "vite";
112
+ import { workflow } from "workflow/vite";
113
+ import reactRouterConfig from "./react-router.config";
114
+
115
+ export default defineConfig({
116
+ plugins: [
117
+ reactRouter(),
118
+ nitro({
119
+ serverDir: "./server",
120
+ output: {
121
+ dir: reactRouterConfig.buildDirectory,
122
+ serverDir: `${reactRouterConfig.buildDirectory}/server`,
123
+ publicDir: `${reactRouterConfig.buildDirectory}/client`,
124
+ },
125
+ }),
126
+ workflow({ dirs: ["workflows"] }),
127
+ ],
128
+ environments: {
129
+ ssr: {
130
+ build: {
131
+ rollupOptions: {
132
+ input: "./server/ssr.ts",
133
+ },
134
+ },
135
+ },
136
+ },
137
+ });
138
+ ```
139
+
140
+ Keep `dirs: ["workflows"]` so subsequent builds do not scan generated files under `build`. Place `reactRouter()` before `nitro()` in the plugin array.
141
+
142
+ </Step>
143
+
144
+ <Step>
145
+
146
+ ## Create a workflow
147
+
148
+ Create `workflows/greeting.ts`:
149
+
150
+ ```typescript title="workflows/greeting.ts" lineNumbers
151
+ export async function greetingWorkflow(name: string) {
152
+ "use workflow";
153
+
154
+ return greet(name);
155
+ }
156
+
157
+ async function greet(name: string) {
158
+ "use step";
159
+
160
+ return `Hello, ${name}!`;
161
+ }
162
+ ```
163
+
164
+ </Step>
165
+
166
+ <Step>
167
+
168
+ ## Start the workflow from a Nitro route
169
+
170
+ Create `server/routes/api/greeting.post.ts`:
171
+
172
+ ```typescript title="server/routes/api/greeting.post.ts" lineNumbers
173
+ import { defineHandler } from "nitro";
174
+ import { start } from "workflow/api";
175
+ import { greetingWorkflow } from "../../../workflows/greeting";
176
+
177
+ export default defineHandler(async (event) => {
178
+ const { name } = (await event.req.json()) as { name: string };
179
+ const run = await start(greetingWorkflow, [name]);
180
+
181
+ return { runId: run.runId };
182
+ });
183
+ ```
184
+
185
+ React Router continues to handle your application routes. Nitro handles this server route at `POST /api/greeting`, as well as Workflow SDK's internal routes.
186
+
187
+ </Step>
188
+
189
+ <Step>
190
+
191
+ ## Run the app
192
+
193
+ Start the development server:
194
+
195
+ ```bash
196
+ pnpm vite dev
197
+ ```
198
+
199
+ Then start a workflow:
200
+
201
+ ```bash
202
+ curl -X POST \
203
+ -H "content-type: application/json" \
204
+ -d '{"name":"Workflow"}' \
205
+ http://localhost:3000/api/greeting
206
+ ```
207
+
208
+ Build and start the production server:
209
+
210
+ ```bash
211
+ pnpm vite build
212
+ node ./build/server/index.mjs
213
+ ```
214
+
215
+ You can inspect local runs with `pnpm workflow web`.
216
+
217
+ </Step>
218
+
219
+ </Steps>
220
+
221
+ ## Troubleshooting
222
+
223
+ ### Vite reports an invalid SSR input or `path.replace is not a function`
224
+
225
+ Set `future.v8_viteEnvironmentApi` to `true` in `react-router.config.ts`.
226
+
227
+ ### React Router pages return 404
228
+
229
+ Check that the `ssr` environment input points to `./server/ssr.ts`.
230
+
231
+ ### A second build tries to compile files under `build/server`
232
+
233
+ Use `workflow({ dirs: ["workflows"] })`, remove the existing `build` directory once, and rebuild.
234
+
235
+ ### `vite build` finishes output but does not exit
236
+
237
+ Use `workflow@5.0.0` or later with Nitro v3.