diff --git a/dapr/ext/rag/AGENTS.md b/dapr/ext/rag/AGENTS.md index df3bff570..c7f6fc98d 100644 --- a/dapr/ext/rag/AGENTS.md +++ b/dapr/ext/rag/AGENTS.md @@ -217,7 +217,7 @@ without one document's failure discarding its siblings' outcomes. **History size**: after each batch, the orchestrator calls `ctx.continue_as_new(...)` with a small, flat cursor state (`_IngestionState`) rather than accumulating the manifest or every batch's -results in workflow history -- the same pattern `examples/workflow/monitor.py` uses for an eternal +results in workflow history -- the same pattern `examples/workflow/monitor/monitor.py` uses for an eternal polling workflow. History size per generation stays bounded regardless of total corpus size. **Dataclasses, not pydantic, across the activity boundary.** `dapr.ext.workflow`'s automatic diff --git a/dapr/ext/rag/pipeline.py b/dapr/ext/rag/pipeline.py index 7bac2e23d..9db1e8168 100644 --- a/dapr/ext/rag/pipeline.py +++ b/dapr/ext/rag/pipeline.py @@ -36,7 +36,7 @@ # orchestrator calls `ctx.continue_as_new(...)` with a small, flat "cursor" # state (`_IngestionState`) rather than accumulating the manifest or every # batch's results in workflow history, so history size stays bounded -# regardless of corpus size (see `examples/workflow/monitor.py` for the same +# regardless of corpus size (see `examples/workflow/monitor/monitor.py` for the same # continue_as_new pattern applied to an eternal polling workflow). from __future__ import annotations diff --git a/dapr/ext/workflow/AGENTS.md b/dapr/ext/workflow/AGENTS.md index 4874a576c..edae82167 100644 --- a/dapr/ext/workflow/AGENTS.md +++ b/dapr/ext/workflow/AGENTS.md @@ -205,12 +205,12 @@ Two example directories exercise workflows: - **`examples/workflow/`** — primary, comprehensive examples: - `simple.py` — activities, retries, child workflows, external events, pause/resume - - `task_chaining.py` — sequential activity chaining with error handling - - `fan_out_fan_in.py` — parallel execution with `when_all()` - - `human_approval.py` — external event waiting with timeouts - - `monitor.py` — eternal polling workflow with `continue_as_new()` - - `child_workflow.py` — child workflow orchestration - - `cross-app1.py`, `cross-app2.py`, `cross-app3.py` — cross-app calls + - `task-chaining/task_chaining.py` — sequential activity chaining with error handling + - `fan-out-fan-in/fan_out_fan_in.py` — parallel execution with `when_all()` + - `human-interaction/human_approval.py` — external event waiting with timeouts + - `monitor/monitor.py` — eternal polling workflow with `continue_as_new()` + - `child-workflow/child_workflow.py` — child workflow orchestration + - `multi-app/multi-app1.py`, `multi-app/multi-app2.py`, `multi-app/multi-app3.py` — cross-app calls - `versioning.py` — workflow versioning with `is_patched()` - `simple_aio_client.py` — async client variant - `async_activities.py` — `async def` activities (fan-out/fan-in with simulated I/O, configurable payload sizes) diff --git a/examples/AGENTS.md b/examples/AGENTS.md index 6c0839bfc..70ba5b17e 100644 --- a/examples/AGENTS.md +++ b/examples/AGENTS.md @@ -98,7 +98,7 @@ Common component types used in examples: `state.redis`, `pubsub.redis`, `lock.re |---------|---------|-------------|----------------| | `workflow` | Multiple standalone scripts | `dapr[workflow]` | No | -The `workflow` example includes: `simple.py`, `task_chaining.py`, `fan_out_fan_in.py`, `human_approval.py`, `monitor.py`, `child_workflow.py`, `cross-app1/2/3.py`, `versioning.py`, `simple_aio_client.py`. +The `workflow` example includes standalone feature examples such as `simple.py`, `simple_aio_client.py`, and `versioning.py`, plus pattern directories: `task-chaining/`, `fan-out-fan-in/`, `human-interaction/`, `monitor/`, `child-workflow/`, and `multi-app/`. Each pattern directory has its own README. ### Secrets, configuration, locks | Example | Pattern | SDK packages | Has components | diff --git a/examples/rag/reconciliation_workflow.py b/examples/rag/reconciliation_workflow.py index afc3fb190..dcc96759a 100644 --- a/examples/rag/reconciliation_workflow.py +++ b/examples/rag/reconciliation_workflow.py @@ -31,7 +31,7 @@ `ctx.create_timer(...)` / `ctx.wait_for_external_event(...)` pattern inside an actual Dapr Workflow instead (one durable "quiet window" orchestration per prefix, restarting its timer on every new external event, matching -`examples/workflow/human_approval.py`'s wait-with-timeout shape) so exactly +`examples/workflow/human-interaction/human_approval.py`'s wait-with-timeout shape) so exactly one reconciliation fires regardless of replica count. Noted here rather than implemented, to keep this example's scope matched to its single-process demo. For the same reason, a process crash between accumulating a pending diff --git a/examples/workflow/README.md b/examples/workflow/README.md index 694fdca86..47ffbac77 100644 --- a/examples/workflow/README.md +++ b/examples/workflow/README.md @@ -17,7 +17,7 @@ pip3 install -r requirements.txt ## Running the samples -Each of the examples in this directory can be run directly from the command line. +Run the standalone examples below from `examples/workflow`. For a [workflow pattern](#workflow-patterns), change into its directory and follow its README. ### Simple Workflow This example represents a workflow that manages counters through a series of activities and child workflows. @@ -151,348 +151,18 @@ The output of this example should look like this: - "Workflow completed! Result: Completed" ``` -### Task Chaining +### Workflow patterns -This example demonstrates how to chain "activity" tasks together in a workflow. You can run this sample using the following command: - - -```sh -dapr run --app-id wfexample -- python3 task_chaining.py -``` - - -The output of this example should look like this: - -``` -Workflow started. Instance ID: b716208586c24829806b44b62816b598 -Step 1: Received input: 42. -Step 2: Received input: 43. -Step 3: Received input: 86. -Workflow completed! Status: WorkflowStatus.COMPLETED -``` - -### Fan-out/Fan-in - -This example demonstrates how to fan-out a workflow into multiple parallel tasks, and then fan-in the results of those tasks. You can run this sample using the following command: - - - -```sh -dapr run --app-id wfexample -- python3 fan_out_fan_in.py -``` - - -The output of this sample should look like this: - -``` -Workflow started. Instance ID: 2e656befbb304e758776e30642b75944 -Processing work item: 1. -Processing work item: 2. -Processing work item: 3. -Processing work item: 4. -Processing work item: 5. -Processing work item: 6. -Processing work item: 7. -Processing work item: 8. -Processing work item: 9. -Processing work item: 10. -Work item 1 processed. Result: 2. -Work item 2 processed. Result: 4. -Work item 3 processed. Result: 6. -Work item 4 processed. Result: 8. -Work item 5 processed. Result: 10. -Work item 6 processed. Result: 12. -Work item 7 processed. Result: 14. -Work item 8 processed. Result: 16. -Work item 9 processed. Result: 18. -Work item 10 processed. Result: 20. -Final result: 110. -``` - -Note that the ordering of the work-items is non-deterministic since they are all running in parallel. - -### Human Interaction - -This example demonstrates how to use a workflow to interact with a human user. This example requires input from the user, so you'll need to have a separate command for the Dapr CLI and the Python app. - -The Dapr CLI can be started using the following command: - -```sh -dapr run --app-id wfexample -``` - -In a separate terminal window, run the following command to start the Python workflow app: - -```sh - python3 human_approval.py - ``` - -When you run the example, you will see output as well as a prompt like this: - -``` -*** Requesting approval from user for order: namespace(cost=2000, product='MyProduct', quantity=1) -Press [ENTER] to approve the order... -``` - -Press the `ENTER` key to continue the workflow. If `ENTER` is pressed before the hardcoded timeout expires, then the following output will be displayed: - -``` -*** Placing order: namespace(cost=2000, product='MyProduct', quantity=1) -Workflow completed! Result: "Approved by 'Me'" -``` - -However, if the timeout expires before `ENTER` is pressed, then the following output will be displayed: - -``` -*** Workflow timed out! -``` - -### Monitor - -This example demonstrates how to eternally running workflow that polls an endpoint to detect service health events. This example requires input from the user, so you'll need to have a separate command for the Dapr CLI and the Python app. - -The Dapr CLI can be started using the following command: - -```sh -dapr run --app-id wfexample -``` - -In a separate terminal window, run the following command to start the Python workflow app: - -```sh -python3 monitor.py -``` - -The workflow runs forever, or until the app is stopped. While it is running, it will periodically report information about whether a "job" is healthy or unhealthy. After several minutes, the output of this workflow will look something like this (note that the healthy and unhealthy message ordering is completely random): - -``` -Press Enter to stop... -Job 'job1' is healthy. -Job 'job1' is healthy. -Job 'job1' is unhealthy. -*** Alert: Job 'job1' is unhealthy! -Job 'job1' is healthy. -Job 'job1' is healthy. -Job 'job1' is healthy. -Job 'job1' is unhealthy. -*** Alert: Job 'job1' is unhealthy! -Job 'job1' is unhealthy. -``` - -This workflow runs forever or until you press `ENTER` to stop it. Starting the app again after stopping it will cause the same workflow instance to resume where it left off. - -### Child Workflow - -This example demonstrates how to call a child workflow. The Dapr CLI can be started using the following command: - -```sh -dapr run --app-id wfexample -``` - -In a separate terminal window, run the following command to start the Python workflow app: - -```sh -python3 child_workflow.py -``` - -When you run the example, you will see output like this: -``` -... -*** Calling child workflow 29a7592a1e874b07aad2bb58de309a51-child -*** Child workflow 6feadc5370184b4998e50875b20084f6 called -... -``` - - -### Multi-app Workflows - -This example demonstrates how to call child workflows and activities in different apps. The multiple Dapr CLI instances can be started using the following commands: - - - -```sh -dapr run --app-id wfexample3 -- python3 multi-app3.py & -dapr run --app-id wfexample2 -- python3 multi-app2.py & -dapr run --app-id wfexample1 -- python3 multi-app1.py -``` - - -When you run the apps, you will see output like this: -``` -... -app1 - triggering app2 workflow -app2 - triggering app3 activity -... -``` -among others. This shows that the workflow calls are working as expected. - -#### Cross-app client operations - -The multi-app examples above call across apps from inside a workflow. Client -operations can target another app too: pass `app_id` to -`schedule_new_workflow` and to the operations that follow, and each one is -applied to the instance owned by that app. The caller registers no workflow of -its own. Whether it is permitted is governed by the target app's -`WorkflowAccessPolicy`. - - - -```sh -dapr run --app-id wfcrossapphost -- python3 cross-app-client-host.py & -dapr run --app-id wfcrossappclient -- python3 cross-app-client.py -``` - - -Requires a Dapr runtime with cross-app workflow support. Against an older -runtime the app ID is ignored and every operation applies to the caller's own -app. - -#### Error handling on activity calls - -This example demonstrates how the error handling works on activity calls in multi-app workflows. - -Error handling on activity calls in multi-app workflows works as normal workflow activity calls. - -In this example we run `app3` in failing mode, which makes the activity call return error constantly. The activity call from `app2` will fail after the retry policy is exhausted. - - - -```sh -export ERROR_ACTIVITY_MODE=true -dapr run --app-id wfexample3 -- python3 multi-app3.py & -dapr run --app-id wfexample2 -- python3 multi-app2.py & -dapr run --app-id wfexample1 -- python3 multi-app1.py -``` - - - -When you run the apps with the `ERROR_ACTIVITY_MODE` environment variable set, you will see output like this: -``` -... -app3 - received activity call -app3 - raising error in activity due to error mode being enabled -app2 - received activity error from app3 -... -``` -among others. This shows that the activity calls are failing as expected, and they are being handled as expected too. - - -#### Error handling on workflow calls - -This example demonstrates how the error handling works on workflow calls in multi-app workflows. - -Error handling on workflow calls in multi-app workflows works as normal workflow calls. - -In this example we run `app2` in failing mode, which makes the workflow call return error constantly. The workflow call from `app1` will fail after the retry policy is exhausted. - - - -```sh -export ERROR_WORKFLOW_MODE=true -dapr run --app-id wfexample3 -- python3 multi-app3.py & -dapr run --app-id wfexample2 -- python3 multi-app2.py & -dapr run --app-id wfexample1 -- python3 multi-app1.py -``` - - -When you run the apps with the `ERROR_WORKFLOW_MODE` environment variable set, you will see output like this: -``` -... -app2 - received workflow call -app2 - raising error in workflow due to error mode being enabled -app1 - received workflow error from app2 -... -``` -among others. This shows that the workflow calls are failing as expected, and they are being handled as expected too. +Each workflow pattern has its own directory and README with setup instructions, run commands, and expected output. +| Pattern | Example directory | +|---------|-------------------| +| Task Chaining | [task-chaining](task-chaining/README.md) | +| Fan-out/Fan-in | [fan-out-fan-in](fan-out-fan-in/README.md) | +| Human Interaction | [human-interaction](human-interaction/README.md) | +| Monitor | [monitor](monitor/README.md) | +| Child Workflow | [child-workflow](child-workflow/README.md) | +| Multi-app Workflows | [multi-app](multi-app/README.md) | ### Versioning diff --git a/examples/workflow/child-workflow/README.md b/examples/workflow/child-workflow/README.md new file mode 100644 index 000000000..422c69553 --- /dev/null +++ b/examples/workflow/child-workflow/README.md @@ -0,0 +1,32 @@ +# Child Workflow + +## Prerequisites + +Follow the [workflow prerequisites](../README.md#prerequisites), then run these commands from the repository root: + +```sh +cd examples/workflow/child-workflow +pip3 install -r ../requirements.txt +``` + +## Run the example + +This example demonstrates how to call a child workflow. The Dapr CLI can be started using the following command: + +```sh +dapr run --app-id wfexample +``` + +In a separate terminal window, run the following command to start the Python workflow app: + +```sh +python3 child_workflow.py +``` + +When you run the example, you will see output like this: +``` +... +*** Calling child workflow 29a7592a1e874b07aad2bb58de309a51-child +*** Child workflow 6feadc5370184b4998e50875b20084f6 called +... +``` diff --git a/examples/workflow/child_workflow.py b/examples/workflow/child-workflow/child_workflow.py similarity index 100% rename from examples/workflow/child_workflow.py rename to examples/workflow/child-workflow/child_workflow.py diff --git a/examples/workflow/fan-out-fan-in/README.md b/examples/workflow/fan-out-fan-in/README.md new file mode 100644 index 000000000..abf44ca42 --- /dev/null +++ b/examples/workflow/fan-out-fan-in/README.md @@ -0,0 +1,76 @@ +# Fan-out/Fan-in + +## Prerequisites + +Follow the [workflow prerequisites](../README.md#prerequisites), then run these commands from the repository root: + +```sh +cd examples/workflow/fan-out-fan-in +pip3 install -r ../requirements.txt +``` + +## Run the example + +This example demonstrates how to fan-out a workflow into multiple parallel tasks, and then fan-in the results of those tasks. You can run this sample using the following command: + + + +```sh +dapr run --app-id wfexample -- python3 fan_out_fan_in.py +``` + + +The output of this sample should look like this: + +``` +Workflow started. Instance ID: 2e656befbb304e758776e30642b75944 +Processing work item: 1. +Processing work item: 2. +Processing work item: 3. +Processing work item: 4. +Processing work item: 5. +Processing work item: 6. +Processing work item: 7. +Processing work item: 8. +Processing work item: 9. +Processing work item: 10. +Work item 1 processed. Result: 2. +Work item 2 processed. Result: 4. +Work item 3 processed. Result: 6. +Work item 4 processed. Result: 8. +Work item 5 processed. Result: 10. +Work item 6 processed. Result: 12. +Work item 7 processed. Result: 14. +Work item 8 processed. Result: 16. +Work item 9 processed. Result: 18. +Work item 10 processed. Result: 20. +Final result: 110. +``` + +Note that the ordering of the work-items is non-deterministic since they are all running in parallel. diff --git a/examples/workflow/fan_out_fan_in.py b/examples/workflow/fan-out-fan-in/fan_out_fan_in.py similarity index 100% rename from examples/workflow/fan_out_fan_in.py rename to examples/workflow/fan-out-fan-in/fan_out_fan_in.py diff --git a/examples/workflow/human-interaction/README.md b/examples/workflow/human-interaction/README.md new file mode 100644 index 000000000..4fd21bcac --- /dev/null +++ b/examples/workflow/human-interaction/README.md @@ -0,0 +1,46 @@ +# Human Interaction + +## Prerequisites + +Follow the [workflow prerequisites](../README.md#prerequisites), then run these commands from the repository root: + +```sh +cd examples/workflow/human-interaction +pip3 install -r ../requirements.txt +``` + +## Run the example + +This example demonstrates how to use a workflow to interact with a human user. This example requires input from the user, so you'll need to have a separate command for the Dapr CLI and the Python app. + +The Dapr CLI can be started using the following command: + +```sh +dapr run --app-id wfexample +``` + +In a separate terminal window, run the following command to start the Python workflow app: + +```sh + python3 human_approval.py + ``` + +When you run the example, you will see output as well as a prompt like this: + +``` +*** Requesting approval from user for order: namespace(cost=2000, product='MyProduct', quantity=1) +Press [ENTER] to approve the order... +``` + +Press the `ENTER` key to continue the workflow. If `ENTER` is pressed before the hardcoded timeout expires, then the following output will be displayed: + +``` +*** Placing order: namespace(cost=2000, product='MyProduct', quantity=1) +Workflow completed! Result: "Approved by 'Me'" +``` + +However, if the timeout expires before `ENTER` is pressed, then the following output will be displayed: + +``` +*** Workflow timed out! +``` diff --git a/examples/workflow/human_approval.py b/examples/workflow/human-interaction/human_approval.py similarity index 100% rename from examples/workflow/human_approval.py rename to examples/workflow/human-interaction/human_approval.py diff --git a/examples/workflow/monitor/README.md b/examples/workflow/monitor/README.md new file mode 100644 index 000000000..29c76cd6b --- /dev/null +++ b/examples/workflow/monitor/README.md @@ -0,0 +1,44 @@ +# Monitor + +## Prerequisites + +Follow the [workflow prerequisites](../README.md#prerequisites), then run these commands from the repository root: + +```sh +cd examples/workflow/monitor +pip3 install -r ../requirements.txt +``` + +## Run the example + +This example demonstrates how to run an eternal workflow that polls an endpoint to detect service health events. This example requires input from the user, so you'll need to have a separate command for the Dapr CLI and the Python app. + +The Dapr CLI can be started using the following command: + +```sh +dapr run --app-id wfexample +``` + +In a separate terminal window, run the following command to start the Python workflow app: + +```sh +python3 monitor.py +``` + +The workflow runs forever, or until the app is stopped. While it is running, it will periodically report information about whether a "job" is healthy or unhealthy. After several minutes, the output of this workflow will look something like this (note that the healthy and unhealthy message ordering is completely random): + +``` +Press Enter to stop... +Job 'job1' is healthy. +Job 'job1' is healthy. +Job 'job1' is unhealthy. +*** Alert: Job 'job1' is unhealthy! +Job 'job1' is healthy. +Job 'job1' is healthy. +Job 'job1' is healthy. +Job 'job1' is unhealthy. +*** Alert: Job 'job1' is unhealthy! +Job 'job1' is unhealthy. +``` + +This workflow runs forever or until you press `ENTER` to stop it. Starting the app again after stopping it will cause the same workflow instance to resume where it left off. diff --git a/examples/workflow/monitor.py b/examples/workflow/monitor/monitor.py similarity index 100% rename from examples/workflow/monitor.py rename to examples/workflow/monitor/monitor.py diff --git a/examples/workflow/multi-app/README.md b/examples/workflow/multi-app/README.md new file mode 100644 index 000000000..af87e84f6 --- /dev/null +++ b/examples/workflow/multi-app/README.md @@ -0,0 +1,165 @@ +# Multi-app Workflows + +## Prerequisites + +Follow the [workflow prerequisites](../README.md#prerequisites), then run these commands from the repository root: + +```sh +cd examples/workflow/multi-app +pip3 install -r ../requirements.txt +``` + +## Run the example + +This example demonstrates how to call child workflows and activities in different apps. The multiple Dapr CLI instances can be started using the following commands: + + + +```sh +dapr run --app-id wfexample3 -- python3 multi-app3.py & +dapr run --app-id wfexample2 -- python3 multi-app2.py & +dapr run --app-id wfexample1 -- python3 multi-app1.py +``` + + +When you run the apps, you will see output like this: +``` +... +app1 - triggering app2 workflow +app2 - triggering app3 activity +... +``` +among others. This shows that the workflow calls are working as expected. + +### Cross-app client operations + +The multi-app examples above call across apps from inside a workflow. Client +operations can target another app too: pass `app_id` to +`schedule_new_workflow` and to the operations that follow, and each one is +applied to the instance owned by that app. The caller registers no workflow of +its own. Whether it is permitted is governed by the target app's +`WorkflowAccessPolicy`. + + + +```sh +dapr run --app-id wfcrossapphost -- python3 cross-app-client-host.py & +dapr run --app-id wfcrossappclient -- python3 cross-app-client.py +``` + + +Requires a Dapr runtime with cross-app workflow support. Against an older +runtime the app ID is ignored and every operation applies to the caller's own +app. + +### Error handling on activity calls + +This example demonstrates how the error handling works on activity calls in multi-app workflows. + +Error handling on activity calls in multi-app workflows works as normal workflow activity calls. + +In this example we run `app3` in failing mode, which makes the activity call return error constantly. The activity call from `app2` will fail after the retry policy is exhausted. + + + +```sh +export ERROR_ACTIVITY_MODE=true +dapr run --app-id wfexample3 -- python3 multi-app3.py & +dapr run --app-id wfexample2 -- python3 multi-app2.py & +dapr run --app-id wfexample1 -- python3 multi-app1.py +``` + + + +When you run the apps with the `ERROR_ACTIVITY_MODE` environment variable set, you will see output like this: +``` +... +app3 - received activity call +app3 - raising error in activity due to error mode being enabled +app2 - received activity error from app3 +... +``` +among others. This shows that the activity calls are failing as expected, and they are being handled as expected too. + + +### Error handling on workflow calls + +This example demonstrates how the error handling works on workflow calls in multi-app workflows. + +Error handling on workflow calls in multi-app workflows works as normal workflow calls. + +In this example we run `app2` in failing mode, which makes the workflow call return error constantly. The workflow call from `app1` will fail after the retry policy is exhausted. + + + +```sh +export ERROR_WORKFLOW_MODE=true +dapr run --app-id wfexample3 -- python3 multi-app3.py & +dapr run --app-id wfexample2 -- python3 multi-app2.py & +dapr run --app-id wfexample1 -- python3 multi-app1.py +``` + + +When you run the apps with the `ERROR_WORKFLOW_MODE` environment variable set, you will see output like this: +``` +... +app2 - received workflow call +app2 - raising error in workflow due to error mode being enabled +app1 - received workflow error from app2 +... +``` +among others. This shows that the workflow calls are failing as expected, and they are being handled as expected too. diff --git a/examples/workflow/cross-app-client-host.py b/examples/workflow/multi-app/cross-app-client-host.py similarity index 100% rename from examples/workflow/cross-app-client-host.py rename to examples/workflow/multi-app/cross-app-client-host.py diff --git a/examples/workflow/cross-app-client.py b/examples/workflow/multi-app/cross-app-client.py similarity index 100% rename from examples/workflow/cross-app-client.py rename to examples/workflow/multi-app/cross-app-client.py diff --git a/examples/workflow/multi-app1.py b/examples/workflow/multi-app/multi-app1.py similarity index 100% rename from examples/workflow/multi-app1.py rename to examples/workflow/multi-app/multi-app1.py diff --git a/examples/workflow/multi-app2.py b/examples/workflow/multi-app/multi-app2.py similarity index 100% rename from examples/workflow/multi-app2.py rename to examples/workflow/multi-app/multi-app2.py diff --git a/examples/workflow/multi-app3.py b/examples/workflow/multi-app/multi-app3.py similarity index 100% rename from examples/workflow/multi-app3.py rename to examples/workflow/multi-app/multi-app3.py diff --git a/examples/workflow/task-chaining/README.md b/examples/workflow/task-chaining/README.md new file mode 100644 index 000000000..339a4aaeb --- /dev/null +++ b/examples/workflow/task-chaining/README.md @@ -0,0 +1,38 @@ +# Task Chaining + +## Prerequisites + +Follow the [workflow prerequisites](../README.md#prerequisites), then run these commands from the repository root: + +```sh +cd examples/workflow/task-chaining +pip3 install -r ../requirements.txt +``` + +## Run the example + +This example demonstrates how to chain "activity" tasks together in a workflow. You can run this sample using the following command: + + +```sh +dapr run --app-id wfexample -- python3 task_chaining.py +``` + + +The output of this example should look like this: + +``` +Workflow started. Instance ID: b716208586c24829806b44b62816b598 +Step 1: Received input: 42. +Step 2: Received input: 43. +Step 3: Received input: 86. +Workflow completed! Status: WorkflowStatus.COMPLETED +``` diff --git a/examples/workflow/task_chaining.py b/examples/workflow/task-chaining/task_chaining.py similarity index 100% rename from examples/workflow/task_chaining.py rename to examples/workflow/task-chaining/task_chaining.py diff --git a/tests/examples/test_workflow.py b/tests/examples/test_workflow.py index 2d5ec18eb..44eebec37 100644 --- a/tests/examples/test_workflow.py +++ b/tests/examples/test_workflow.py @@ -25,14 +25,14 @@ ] -@pytest.mark.example_dir('workflow') +@pytest.mark.example_dir('workflow/task-chaining') def test_task_chaining(dapr): output = dapr.run('--app-id workflow-task-chaining -- python3 task_chaining.py', timeout=30) for line in EXPECTED_TASK_CHAINING: assert line in output, f'Missing in output: {line}' -@pytest.mark.example_dir('workflow') +@pytest.mark.example_dir('workflow/fan-out-fan-in') def test_fan_out_fan_in(dapr): output = dapr.run('--app-id workflow-fan-out-fan-in -- python3 fan_out_fan_in.py', timeout=60) for line in EXPECTED_FAN_OUT_FAN_IN: