Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@
{ retries: { limit: 3, delay: "10 seconds", backoff: "exponential" } },
async () => {
const msg = await client.messages.create({
model: "claude-sonnet-4-5-20250929",

Check warning on line 264 in src/content/docs/workflows/get-started/durable-agents.mdx

View workflow job for this annotation

GitHub Actions / Semgrep

semgrep.style-guide-potential-date-year

Potential year found. Documentation should strive to represent universal truth, not something time-bound. (add [skip style guide checks] to commit message to skip)
max_tokens: 4096,
tools: toolDefinitions,
messages,
Expand Down Expand Up @@ -342,7 +342,7 @@
This is especially important for:

- **LLM calls**: Expensive and slow, should not repeat unnecessarily
- **External APIs**: May have rate limits or side effects

Check warning on line 345 in src/content/docs/workflows/get-started/durable-agents.mdx

View workflow job for this annotation

GitHub Actions / Semgrep

semgrep.style-guide-potential-date-month

Potential month found. Documentation should strive to represent universal truth, not something time-bound. (add [skip style guide checks] to commit message to skip)
- **Idempotency**: Some tools (like sending emails) should not run twice

</Details>
Expand Down Expand Up @@ -611,6 +611,7 @@
```

```tsx
import { useState } from "react";
import { useAgent } from "agents/react";

function ResearchUI({ agentId = "default" }) {
Expand Down
59 changes: 34 additions & 25 deletions src/content/docs/workflows/python/bindings.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,17 @@ From the configuration perspective, enabling Python Workflows requires adding th
{
"$schema": "./node_modules/wrangler/config-schema.json",
"name": "workflows-starter",
"main": "src/index.ts",
"main": "src/index.py",
"compatibility_date": "$today",
"compatibility_flags": [
"python_workflows",
"python_workers"
],
"compatibility_flags": ["python_workflows", "python_workers"],
"workflows": [
{
// name of your workflow
"name": "workflows-starter",
// binding name env.MY_WORKFLOW
"binding": "MY_WORKFLOW",
// this is class that extends the Workflow class in src/index.ts
"class_name": "MyWorkflow"
// this is class that extends the Workflow class in src/index.py
"class_name": "MyWorkflow",
}
]
}
Expand All @@ -50,7 +47,7 @@ From the configuration perspective, enabling Python Workflows requires adding th
And this is how you use the payload in your workflow:

```python
from pyodide.ffi import to_js
from workers import WorkflowEntrypoint

class DemoWorkflowClass(WorkflowEntrypoint):
async def run(self, event, step):
Expand Down Expand Up @@ -110,15 +107,18 @@ Each element of the `batch` list is expected to include both `id` and `params` p
```python
from pyodide.ffi import to_js
from js import Object
from workers import WorkerEntrypoint, Response

# Create a new batch of 3 Workflow instances, each with its own ID and pass params to the Workflow instances
listOfInstances = [
to_js({ "id": "id-abc123", "params": { "hello": "world-0" } }, dict_converter=Object.fromEntries),
to_js({ "id": "id-def456", "params": { "hello": "world-1" } }, dict_converter=Object.fromEntries),
to_js({ "id": "id-ghi789", "params": { "hello": "world-2" } }, dict_converter=Object.fromEntries)
];

await env.MY_WORKFLOW.create_batch(listOfInstances);
class Default(WorkerEntrypoint):
async def fetch(self, request):
# Create a new batch of 3 Workflow instances, each with its own ID and pass params to the Workflow instances
listOfInstances = [
to_js({ "id": "id-abc123", "params": { "hello": "world-0" } }, dict_converter=Object.fromEntries),
to_js({ "id": "id-def456", "params": { "hello": "world-1" } }, dict_converter=Object.fromEntries),
to_js({ "id": "id-ghi789", "params": { "hello": "world-2" } }, dict_converter=Object.fromEntries)
]
await self.env.MY_WORKFLOW.create_batch(listOfInstances)
return Response.json({"status": "success"})
```

### `get`
Expand All @@ -130,14 +130,19 @@ Get a workflow instance by ID.
Returns a [`WorkflowInstance`](/workflows/build/workers-api/#workflowinstance) object, which can be used to query the status of the workflow instance.

```python
instance = await env.MY_WORKFLOW.get("abc-123")

# FFI methods available for WorkflowInstance
await instance.status()
await instance.pause()
await instance.resume()
await instance.restart()
await instance.terminate()
from workers import WorkerEntrypoint, Response

class Default(WorkerEntrypoint):
async def fetch(self, request):
instance = await self.env.MY_WORKFLOW.get("abc-123")

# FFI methods available for WorkflowInstance
await instance.status()
await instance.pause()
await instance.resume()
await instance.restart()
await instance.terminate()
return Response.json({"status": "success"})
```

### `send_event`
Expand All @@ -150,8 +155,12 @@ Send an event to a workflow instance.
```python
from pyodide.ffi import to_js
from js import Object
from workers import WorkerEntrypoint, Response

await env.MY_WORKFLOW.send_event(to_js({ "type": "my-event-type", "payload": { "foo": "bar" } }, dict_converter=Object.fromEntries))
class Default(WorkerEntrypoint):
async def fetch(self, request):
await self.env.MY_WORKFLOW.send_event(to_js({ "type": "my-event-type", "payload": { "foo": "bar" } }, dict_converter=Object.fromEntries))
return Response.json({"status": "success"})
```

:::note
Expand Down
5 changes: 2 additions & 3 deletions src/content/docs/workflows/python/dag.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ title: DAG Workflows
pcx_content_type: concept
sidebar:
order: 4

---

The Python Workflows SDK supports DAG workflows in a declarative way, using the `step.do` decorator with the `depends` parameter to define dependencies (other steps that must complete before this step can run).
Expand All @@ -19,7 +18,7 @@ class PythonWorkflowStarter(WorkflowEntrypoint):
except TypeError as e:
print(f"Successfully caught {type(e).__name__}: {e}")

step.sleep('demo sleep', '10 seconds')
await step.sleep('demo sleep', '10 seconds')

@step.do('dependency1')
async def dep_1():
Expand Down Expand Up @@ -47,4 +46,4 @@ On this example, `dep_1` and `dep_2` are run concurrently before execution of `f

Having `concurrent=True` allows the dependencies to be resolved concurrently. If one of the callables passed to `depends` has already completed, it will be skipped and its return value will be reused.

This pattern is useful for diamond shaped workflows, where a step depends on two or more other steps that can run concurrently.
This pattern is useful for diamond shaped workflows, where a step depends on two or more other steps that can run concurrently.
3 changes: 2 additions & 1 deletion src/content/docs/workflows/python/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ class PythonWorkflowStarter(WorkflowEntrypoint):
# does stuff
print('executing step2')

await await_step(step_1,step_2)
await step_1()
await step_2()

async def on_fetch(request, env):
await env.MY_WORKFLOW.create()
Expand Down
10 changes: 7 additions & 3 deletions src/content/docs/workflows/python/python-workers-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ title: Python Workers API
pcx_content_type: concept
sidebar:
order: 2

---

This guide covers the Python Workflows SDK, with instructions on how to build and create workflows using Python.
Expand All @@ -16,7 +15,7 @@ The `WorkflowEntrypoint` is the main entrypoint for a Python workflow. It extend
from workers import WorkflowEntrypoint

class MyWorkflow(WorkflowEntrypoint):
def run(self, event, step):
async def run(self, event, step):
# steps here
```

Expand Down Expand Up @@ -62,6 +61,8 @@ async def run(self, event, step):
* `timestamp` - a `datetime.datetime` object or seconds from the Unix epoch to sleep the workflow instance until.

```python
import datetime

async def run(self, event, step):
await step.sleep_until("my-sleep-step", datetime.datetime.now() + datetime.timedelta(seconds=10))
```
Expand All @@ -77,7 +78,6 @@ async def run(self, event, step):
await step.wait_for_event("my-wait-for-event-step", "my-event-type")
```


### `event` parameter

The `event` parameter is a dictionary that contains the payload passed to the workflow instance, along with other metadata:
Expand Down Expand Up @@ -131,6 +131,8 @@ You can bind a step to a specific retry policy by passing a `WorkflowStepConfig`
With Python Workflows, you need to make sure that your `dict` respects the [`WorkflowStepConfig`](/workflows/build/workers-api/#workflowstepconfig) type.

```python
from workers import WorkflowEntrypoint

class DemoWorkflowClass(WorkflowEntrypoint):
async def run(self, event, step):
@step.do('step-name', config={"retries": {"limit": 1, "delay": "10 seconds"}})
Expand All @@ -146,6 +148,8 @@ Note that `env` is a JavaScript object exposed to the Python script via [JsProxy
Let's consider the previous binding called `MY_WORKFLOW`. Here's how you would create a new instance:

```python
from workers import Response, WorkerEntrypoint

class Default(WorkerEntrypoint):
async def fetch(self, request):
instance = await self.env.MY_WORKFLOW.create()
Expand Down
4 changes: 2 additions & 2 deletions src/content/docs/workflows/reference/limits.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ type Env = {

export class MyWorkflow extends WorkflowEntrypoint<Env> {
async run(event: WorkflowEvent<unknown>, step: WorkflowStep) {
const apiResponse = await step.do("initial work", async () => {
await step.do("initial work", async () => {
let resp = await fetch("https://api.cloudflare.com/client/v4/ips");
return await resp.json<any>();
});
Expand All @@ -86,7 +86,7 @@ export class MyWorkflow extends WorkflowEntrypoint<Env> {
{
retries: {
limit: 5,
delay: "5 second",
delay: "5 seconds",
backoff: "exponential",
},
timeout: "15 minutes",
Expand Down