Add workflow management APIs: list, history and rerun - #1217
javier-aliaga wants to merge 2 commits into
Conversation
The three advanced workflow management operations from dapr/dapr#9729 had no Python surface: the vendored durabletask protos carried ListInstanceIDs, GetInstanceHistory and RerunWorkflowFromEvent, but neither client layer exposed them, so reaching them meant using the gRPC stub directly. DaprWorkflowClient and its async counterpart now expose: - list_workflow_instances(page_size, continuation_token) -> one page of instance IDs plus the token for the next, when the caller wants to hold the cursor themselves. - iter_workflow_instances(page_size) -> a lazy iterator that pages internally; an async generator on the async client. - get_workflow_history(instance_id) -> the instance's events as WorkflowHistoryEvent records. - rerun_workflow_from_event(instance_id, event_id, ...) -> the ID of a new instance that replays history up to the chosen event and resumes there. The rerun input is a single argument rather than a value plus a flag. The wire format pairs a non-optional StringValue with an overwriteInput bool precisely because StringValue cannot express absence, so the two are collapsed behind a sentinel default: omitting input keeps the original, passing None clears it. A falsy value such as 0 still overwrites. WorkflowHistoryEvent carries event_id, timestamp, event_type, name, task_scheduled_id and failure_details, which is enough to choose a rerun point by activity name instead of by raw event number. is_rerunnable reports this SDK's snapshot of which event types the runtime restarts from; the sidecar keeps the final say. Unrecognised event types map to UNKNOWN rather than raising, so a newer sidecar cannot break history reads. Verified end to end against runtime 1.18.0: a failed order is listed, its history read, and the failed charge rerun with a corrected input to completion. examples/workflow/workflow_management.py covers that flow and is asserted by tests/examples/test_workflow.py. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Javier Aliaga <javier@diagrid.io>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1217 +/- ##
==========================================
+ Coverage 83.32% 83.48% +0.15%
==========================================
Files 123 124 +1
Lines 10250 10384 +134
==========================================
+ Hits 8541 8669 +128
- Misses 1709 1715 +6 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
🟡 Changes recommended
dapr/ext/workflow/AGENTS.md now contains a misleading statement about NOT_FOUND being converted to None, which conflicts with the new get_workflow_history() behavior.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
This PR extends the Dapr Python SDK workflow extension by exposing three durabletask-backed workflow management capabilities (instance listing, history retrieval, and rerun-from-event) on both DaprWorkflowClient and dapr.ext.workflow.aio.DaprWorkflowClient. It also adds a runnable example plus unit and example tests to validate the new APIs end-to-end.
Changes:
- Added workflow management client APIs:
list_workflow_instances,iter_workflow_instances,get_workflow_history, andrerun_workflow_from_event(sync + async). - Introduced typed return models for instance pages and history events (
WorkflowInstanceIdPage,WorkflowHistoryEvent,WorkflowHistoryEventType), including “unknown event type” resilience. - Added comprehensive unit tests (client layer + engine client layer) and a new example validated by the examples test suite.
File summaries
| File | Description |
|---|---|
dapr/ext/workflow/dapr_workflow_client.py |
Adds the new management APIs to the sync workflow client. |
dapr/ext/workflow/aio/dapr_workflow_client.py |
Adds async equivalents of the new management APIs. |
dapr/ext/workflow/workflow_management.py |
New typed models and conversions for instance pages and history events. |
dapr/ext/workflow/_durabletask/client.py |
Adds engine-client RPC wrappers + sentinel handling for rerun input semantics. |
dapr/ext/workflow/_durabletask/aio/client.py |
Async engine-client wrappers for list/history/rerun using shared request builder. |
dapr/ext/workflow/__init__.py |
Exposes the new management types (and FailureDetails) at the extension top-level. |
dapr/ext/workflow/AGENTS.md |
Documents the new APIs (but needs a small correction to the NOT_FOUND/None note). |
tests/ext/workflow/test_workflow_management.py |
New unit tests covering sync + async workflow management APIs. |
tests/ext/workflow/durabletask/test_client_management_apis.py |
New engine-client tests validating request/response behavior for list/history/rerun. |
examples/workflow/workflow_management.py |
New example demonstrating list → history → rerun flow. |
examples/workflow/README.md |
Documents the new example and explains rerunnable event types and caveats. |
tests/examples/test_workflow.py |
Adds output-based validation for the new workflow management example. |
Review details
- Files reviewed: 12/12 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
The blanket statement that the client converts "no such instance exists" to a None return only ever described get_workflow_state; the other methods propagate. Adding get_workflow_history, which raises NOT_FOUND for a missing or purged instance, made the sentence actively misleading. Also covers the input sentinel's repr, which is what help() and tracebacks show for the rerun default. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Javier Aliaga <javier@diagrid.io>
Description
The vendored durabletask protos already carry
ListInstanceIDs,GetInstanceHistoryandRerunWorkflowFromEvent, but neither client layer exposed them. This adds all three toDaprWorkflowClientand its async counterpart:list_workflow_instances(*, page_size, continuation_token)WorkflowInstanceIdPage— one page plus the next tokeniter_workflow_instances(*, page_size=1024)async foron the async client)get_workflow_history(instance_id)list[WorkflowHistoryEvent]rerun_workflow_from_event(instance_id, event_id, ...)All additive; no existing signature changes.
Two behaviours worth knowing:
rerun_workflow_from_eventtakes the replacement input as a single argument. Omit it to keep the original input, passNoneto clear it.WorkflowHistoryEventcarriesevent_id,timestamp,event_type,name,task_scheduled_idandfailure_details, so a rerun point can be chosen by activity name. Unrecognised event types map toUNKNOWNrather than raising.Issue reference
Closes #1181
Part of dapr/dapr#9729
Testing
tests/ext/workflow/test_workflow_management.pyandtests/ext/workflow/durabletask/test_client_management_apis.py, covering both clients.mypyandruffclean.examples/workflow/workflow_management.pyruns list → history → rerun against a live sidecar, asserted bytests/examples/test_workflow.py::test_workflow_management.Note for reviewers
get_workflow_historylets gRPCNOT_FOUNDpropagate for a missing or purged instance, matchingpause/resume/terminate/purge, rather than returningNonethe wayget_workflow_statedoes. Happy to invert it if you prefer.