-
Notifications
You must be signed in to change notification settings - Fork 70
Add the Durable Workflows documentation section #607
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,116 @@ | ||||||||||||
| --- | ||||||||||||
| sidebar_position: 4 | ||||||||||||
| title: "Durable Agentic Workflows" | ||||||||||||
| description: Build AI agents on the durable workflow runtime in WSO2 Integrator — with durable activities, event channels, human tasks, and agent-to-agent collaboration. | ||||||||||||
| keywords: [wso2 integrator, durable agent, agentic workflow, ai agent, human in the loop, events, durable] | ||||||||||||
| --- | ||||||||||||
|
|
||||||||||||
| import ThemedImage from '@theme/ThemedImage'; | ||||||||||||
| import useBaseUrl from '@docusaurus/useBaseUrl'; | ||||||||||||
|
|
||||||||||||
| # Durable Agentic Workflows | ||||||||||||
|
|
||||||||||||
| A durable agentic workflow flips the authoring model: instead of wiring steps together, you describe the goal in natural language and give the agent **capabilities** — activities, events, human tasks, and peer agents. An AI model plans the path at runtime, and because the agent *is* a durable workflow, every reasoning step, tool call, and wait survives crashes and restarts. | ||||||||||||
|
|
||||||||||||
| ## The agent is the declaration | ||||||||||||
|
|
||||||||||||
| Creating a Durable Agentic Workflow (Add Artifact → **Durable Agentic Workflow** → name → **Create Agent**) generates one module-level declaration that carries everything: | ||||||||||||
|
|
||||||||||||
| ```ballerina | ||||||||||||
| final workflow:DurableAgent supportAgent = check new ({ | ||||||||||||
| systemPrompt: { | ||||||||||||
| role: "Expense claim assistant", | ||||||||||||
| instructions: string `Validate each claim, request missing bills, and escalate | ||||||||||||
| unusual claims to a manager before paying.` | ||||||||||||
| }, | ||||||||||||
| model: claimModel, | ||||||||||||
| activities: [ | ||||||||||||
| validateClaim, | ||||||||||||
| requestBill, | ||||||||||||
| {activity: payClaim, requiresApproval: true, userRoles: "manager", | ||||||||||||
| retryPolicy: "manager"}, | ||||||||||||
| {activity: notifyEmployee, retryPolicy: {maxRetries: 3, retryDelay: 2}} | ||||||||||||
|
Comment on lines
+30
to
+32
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: rg -n -C 3 'retryPolicy|requiresApproval|userRoles' en/docs/workflowsRepository: wso2/docs-integrator Length of output: 6339 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== target file =="
sed -n '1,80p' en/docs/workflows/develop/durable-agentic-workflow.md
echo
echo "== relevant review doc sections =="
sed -n '30,90p' en/docs/workflows/develop/review-activity-and-error-handling.md
sed -n '112,128p' en/docs/workflows/getting-started/build-a-claim-workflow-agent.md
echo
echo "== all retryPolicy/hasRetry occurrences =="
rg -n 'retryPolicy|hasRetry|autoRetry|Auto Retry|Human Review|requiresApproval|userRoles' en/docs/workflowsRepository: wso2/docs-integrator Length of output: 10404 Remove the invalid activity retry policy.
Proposed correction- {activity: payClaim, requiresApproval: true, userRoles: "manager",
- retryPolicy: "manager"},
+ {activity: payClaim, requiresApproval: true, userRoles: "manager"},📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||
| ], | ||||||||||||
| events: [ | ||||||||||||
| {name: "billSubmitted", request: BillSubmission, response: string} | ||||||||||||
| ], | ||||||||||||
| humanTasks: [ | ||||||||||||
| {name: "approveExpense", roles: "manager", | ||||||||||||
| title: "Approve expense claim", | ||||||||||||
| description: "Review the claim and bills, then approve or reject."} | ||||||||||||
| ], | ||||||||||||
| maxIter: 16 | ||||||||||||
| }); | ||||||||||||
| ``` | ||||||||||||
|
|
||||||||||||
| The agent model renders this declaration as a single agent node with its capabilities around it. Use the anchored **+** buttons to add capabilities — human tasks (top left), events (bottom left), activities (middle right), agent tools (bottom right) — and click any capability to edit its entry. | ||||||||||||
|
|
||||||||||||
| <ThemedImage | ||||||||||||
| alt="Agent model showing the agent node with activity, event, and human task capability circles" | ||||||||||||
| sources={{ | ||||||||||||
| light: useBaseUrl('/img/workflows/develop/durable-agentic-workflow/01-agent-model.png'), | ||||||||||||
| dark: useBaseUrl('/img/workflows/develop/durable-agentic-workflow/01-agent-model.png'), | ||||||||||||
| }} | ||||||||||||
| /> | ||||||||||||
|
|
||||||||||||
| :::info One place to edit | ||||||||||||
| Where the agent is *used* — a `supportAgent.run(...)` call in a service — the agent box is a read-only reference with a **Go to Agent** button. Configuration always lives on the agent's own model. | ||||||||||||
| ::: | ||||||||||||
|
|
||||||||||||
| ## Capabilities | ||||||||||||
|
|
||||||||||||
| ### Activities | ||||||||||||
|
|
||||||||||||
| `@workflow:Activity` functions the agent may invoke. Each call runs durably with the same guarantees and the same **retry policies** as any workflow activity — including **Requires Approval** gates and **Human Review** retries (see [Review activities and error handling](review-activity-and-error-handling.md)). The agent proposes; your policies decide what needs a human. | ||||||||||||
|
|
||||||||||||
| ### Events | ||||||||||||
|
|
||||||||||||
| Typed channels for data arriving mid-conversation. Declare the request (and optional response) type; the agent waits durably on the channel when its instructions call for it: | ||||||||||||
|
|
||||||||||||
| ```ballerina | ||||||||||||
| events: [ | ||||||||||||
| {name: "billSubmitted", request: BillSubmission, response: string} | ||||||||||||
| ] | ||||||||||||
| ``` | ||||||||||||
|
|
||||||||||||
| ### Human tasks | ||||||||||||
|
|
||||||||||||
| Escalation points the agent can raise on its own judgement — "this claim looks unusual" — decided from the [Control Plane](../icp/managing-workflows.md) inbox by the named roles, exactly like workflow human tasks. | ||||||||||||
|
|
||||||||||||
| ### Agent tools and peers | ||||||||||||
|
|
||||||||||||
| Reuse `@ai:AgentTool` functions and toolkits, or add **peer agents** — other durable agents the agent can delegate to, synchronously or through a callback channel — to build multi-agent systems. | ||||||||||||
|
|
||||||||||||
| ## Driving the agent | ||||||||||||
|
|
||||||||||||
| ```ballerina | ||||||||||||
| // Start an instance; the input becomes part of the first user turn. | ||||||||||||
| string instanceId = check supportAgent.run(claim.toJsonString()); | ||||||||||||
|
|
||||||||||||
| // Deliver an event turn and read that turn's answer. | ||||||||||||
| string token = check supportAgent.sendEvent(instanceId, "billSubmitted", submission); | ||||||||||||
| string reply = check supportAgent.waitForEventResult(instanceId, token); | ||||||||||||
|
|
||||||||||||
| // Read the final outcome (AgentBusyError while a human decision is pending). | ||||||||||||
| string|error result = supportAgent.getResult(instanceId); | ||||||||||||
| ``` | ||||||||||||
|
|
||||||||||||
| All reads are durable: results live in the workflow history, so a crashed caller can re-issue `waitForResult` and get the same answer. | ||||||||||||
|
Comment on lines
+91
to
+98
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: rg -n '\b(waitForResult|waitForEventResult)\b' en/docs/workflowsRepository: wso2/docs-integrator Length of output: 480 Use The example uses 🤖 Prompt for AI Agents |
||||||||||||
|
|
||||||||||||
| ## Why durable agents are different | ||||||||||||
|
|
||||||||||||
| | Standalone agent frameworks | Durable agentic workflows | | ||||||||||||
| | --- | --- | | ||||||||||||
| | Crash loses the conversation and in-flight tool calls | Every turn and tool call is recorded; restarts resume mid-plan | | ||||||||||||
| | Human approval needs custom plumbing | Gates, reviews, and tasks are one form field away | | ||||||||||||
| | Waiting for external input holds a process | Waits are suspended with zero resources, for days if needed | | ||||||||||||
| | Retry logic in every tool | Declarative per-activity retry policies | | ||||||||||||
|
|
||||||||||||
| ## Traditional or agentic? | ||||||||||||
|
|
||||||||||||
| Reach for an agentic workflow when the logic is branchy and judgement-heavy ("request whatever is missing, escalate the odd ones"); keep a [transaction workflow](transaction-workflow.md) when the steps are fixed and auditable. The two share activities, tasks, and the runtime — a claim system can use both side by side. | ||||||||||||
|
|
||||||||||||
| ## Next steps | ||||||||||||
|
|
||||||||||||
| - [Build a Claim Handling Agent](../getting-started/build-a-claim-workflow-agent.md) — the end-to-end getting started. | ||||||||||||
| - [Integration Control Plane](../icp/managing-workflows.md) — approving the agent's gated steps and reading its progress. | ||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,126 @@ | ||
| --- | ||
| sidebar_position: 2 | ||
| title: "Human Task Workflows" | ||
| description: Pause WSO2 Integrator durable workflows for role-based human decisions and external data — for hours, days, or months — with zero resources held while waiting. | ||
| keywords: [wso2 integrator, durable workflow, human task, approval, human in the loop, data event, task inbox, icp] | ||
| --- | ||
|
|
||
| import ThemedImage from '@theme/ThemedImage'; | ||
| import useBaseUrl from '@docusaurus/useBaseUrl'; | ||
|
|
||
| # Human Task Workflows | ||
|
|
||
| Real processes wait on people: a manager approves an expense, a reviewer checks documents, an employee submits missing information. A durable workflow can stop at any point, hand a **task** to a role, and resume the moment someone decides — whether that takes a minute or a month. While it waits, it holds no threads, no memory, no connections. | ||
|
|
||
| ## Await a human task | ||
|
|
||
| Add an **Await Human Task** step from the palette's **Workflow → Steps** group. Give the task a name, the **roles** that may decide it, and the **payload** the decider should see: | ||
|
|
||
| ```ballerina | ||
| RequestDecision request = check ctx->awaitHumanTask("checkExpenseRequest", "manager", | ||
| payload = {"claimId": claim.claimId, "employee": claim.employee, | ||
| "amount": claim.amount, "purpose": claim.purpose}, | ||
| title = string `Check expense request ${claim.claimId}`, | ||
| description = "Review the new claim: request the supporting bills, or reject it.", | ||
| timeout = {days: 3}); | ||
| ``` | ||
|
|
||
| The workflow suspends here. The task appears in the [Integration Control Plane](../icp/managing-workflows.md) task inbox for every user holding the `manager` role, showing your title, description, and payload. | ||
|
|
||
| <ThemedImage | ||
| alt="Integration Control Plane task inbox showing the checkExpenseRequest task with its payload" | ||
| sources={{ | ||
| light: useBaseUrl('/img/workflows/develop/human-task-workflow/01-task-inbox.png'), | ||
| dark: useBaseUrl('/img/workflows/develop/human-task-workflow/01-task-inbox.png'), | ||
| }} | ||
| /> | ||
|
|
||
| ## Typed decisions build the task form | ||
|
|
||
| The value the human submits is typed — declare a record and the Control Plane renders a matching form. Use an **enum** for the action and it becomes a dropdown: | ||
|
|
||
| ```ballerina | ||
| public enum RequestAction { | ||
| REQUEST_BILL, | ||
| REJECT | ||
| } | ||
|
|
||
| public type RequestDecision record {| | ||
| RequestAction action; | ||
| string comment = ""; | ||
| |}; | ||
| ``` | ||
|
|
||
| When the manager submits, the workflow resumes with the decision as a plain value: | ||
|
|
||
| ```ballerina | ||
| if request.action == REJECT { | ||
| // notify and finish | ||
| } else { | ||
| // ask the employee for the supporting bills | ||
| } | ||
| ``` | ||
|
|
||
| :::tip Design for the form | ||
| Whatever you put in the decision record is exactly what the decider fills in. Keep it small: an action enum, a comment, maybe a corrected amount. | ||
| ::: | ||
|
|
||
| ## Wait for external data | ||
|
|
||
| Sometimes the workflow needs *data*, not a decision — the employee submits the bills, a partner system posts a confirmation. Declare a **data event** as a `future` parameter and wait on it: | ||
|
|
||
| ```ballerina | ||
| @workflow:Workflow | ||
| function expenseApprovalWorkflow(workflow:Context ctx, ExpenseClaim claim, | ||
| record {|future<BillSubmission> billSubmitted;|} dataEvents) returns ExpenseResult|error { | ||
| // ... after the manager requests the bills: | ||
| BillSubmission submission = check wait dataEvents.billSubmitted; | ||
| // validate the bills and continue | ||
| } | ||
| ``` | ||
|
|
||
| Anyone with the workflow ID can deliver the data — typically a service resource: | ||
|
|
||
| ```ballerina | ||
| resource function post [string workflowId]/bills(BillSubmission submission) returns json|error { | ||
| check workflow:sendData(expenseApprovalWorkflow, workflowId, "billSubmitted", submission); | ||
| return {workflowId, status: "BILLS_SUBMITTED"}; | ||
|
Comment on lines
+82
to
+87
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift Require authorization before accepting workflow data. The text says that anyone with a workflow ID can submit 🤖 Prompt for AI Agents |
||
| } | ||
| ``` | ||
|
|
||
| While the workflow waits, the Control Plane's execution graph marks the halt point — a **waiting** data node named after the event — so anyone can see exactly what the process is blocked on. | ||
|
|
||
| <ThemedImage | ||
| alt="Execution graph showing the workflow halted on a waiting billSubmitted data event" | ||
| sources={{ | ||
| light: useBaseUrl('/img/workflows/develop/human-task-workflow/02-waiting-data-event.png'), | ||
| dark: useBaseUrl('/img/workflows/develop/human-task-workflow/02-waiting-data-event.png'), | ||
| }} | ||
| /> | ||
|
|
||
| ## A complete two-review flow | ||
|
|
||
| Combining tasks and data events gives the classic claim pattern — two reviews with an evidence hand-off between them: | ||
|
|
||
| 1. **`checkExpenseRequest`** — the manager triages the new claim: request bills, or reject. | ||
| 2. The employee submits bills via `workflow:sendData` → the workflow validates them. | ||
| 3. **`reviewBills`** — the manager sees the validation result and approves or rejects the payout. | ||
|
|
||
| Every decision happens in the Control Plane inbox; the service exposes no task-completion endpoints. | ||
|
|
||
| ## Timeouts | ||
|
|
||
| `timeout` bounds the wait. When it expires the task fails with a timeout error your workflow can handle — escalate, remind, or fail gracefully: | ||
|
|
||
| ```ballerina | ||
| RequestDecision|error decision = ctx->awaitHumanTask("checkExpenseRequest", "manager", | ||
| payload = ..., timeout = {days: 3}); | ||
| if decision is error { | ||
| // escalate to a different role, or end the claim | ||
| } | ||
| ``` | ||
|
|
||
| ## Next steps | ||
|
|
||
| - [Review activities and error handling](review-activity-and-error-handling.md) — approvals attached to *activities* rather than free-standing tasks. | ||
| - [Integration Control Plane](../icp/managing-workflows.md) — where tasks are decided, and how roles map to users. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| --- | ||
| sidebar_position: 3 | ||
| title: "Review Activities & Error Handling" | ||
| description: Gate risky workflow steps behind human approval and turn failures into human-reviewed retries in WSO2 Integrator durable workflows. | ||
| keywords: [wso2 integrator, durable workflow, review activity, retry, error handling, approval gate, human review] | ||
| --- | ||
|
|
||
| import ThemedImage from '@theme/ThemedImage'; | ||
| import useBaseUrl from '@docusaurus/useBaseUrl'; | ||
|
|
||
| # Review Activities & Error Handling | ||
|
|
||
| Failures and risky steps are where durable workflows earn their keep. Instead of scattering try/catch blocks and retry loops through your code, you attach a **retry policy** to each activity call — and for the steps that matter most, you put a **human review** in front of the step or behind its failure. | ||
|
|
||
| ## The three retry policies | ||
|
|
||
| Every activity call takes a **Retry Policy**, chosen in the call's form: | ||
|
|
||
| | Policy | What happens on failure | | ||
| | --- | --- | | ||
| | **No Automatic Retry** (default) | The error returns to your workflow logic — handle it with normal Ballerina `check`/`if` handling. | | ||
| | **Auto Retry** | The engine re-executes the activity with configurable attempts, delay, and backoff. | | ||
| | **Human Review** | A **review task** is created for the roles you name. The reviewer can retry as-is, retry with corrected input, or fail the step. | | ||
|
|
||
| <ThemedImage | ||
| alt="Call Activity form with the Retry Policy dropdown showing No Automatic Retry, Auto Retry, and Human Review" | ||
| sources={{ | ||
| light: useBaseUrl('/img/workflows/develop/review-activity/01-retry-policy-form.png'), | ||
| dark: useBaseUrl('/img/workflows/develop/review-activity/01-retry-policy-form.png'), | ||
| }} | ||
| /> | ||
|
|
||
| ### Auto Retry — for transient failures | ||
|
|
||
| ```ballerina | ||
| string _ = check ctx->callActivity(notifyEmployee, | ||
| {"claimId": claim.claimId, "message": "Your claim was approved."}, | ||
| retryPolicy = {maxRetries: 3, retryDelay: 2, retryBackoff: 2.0}); | ||
| ``` | ||
|
|
||
| A flaky notification service recovers transparently; the workflow never sees the intermediate failures. | ||
|
|
||
| ### Human Review — when a person should fix it | ||
|
|
||
| The roles **are** the policy — name who may decide the retry: | ||
|
|
||
| ```ballerina | ||
| string paymentRef = check ctx->callActivity(makePayment, | ||
| {"claimId": claim.claimId, "amount": claim.amount, "currency": claim.currency}, | ||
| retryPolicy = "manager"); // one role, or ["manager", "finance"] | ||
| ``` | ||
|
|
||
| When `makePayment` fails (say, the gateway rejects the currency), a **review task** appears in the [Control Plane](../icp/managing-workflows.md) inbox for managers, showing the failing input and the error. The reviewer chooses: | ||
|
|
||
| - **Proceed** — rerun with the same input. | ||
| - **Proceed with input** — rerun with corrected input (the form is generated from the activity's parameters, pre-filled with the failing values — fix the currency and retry). | ||
| - **Reject** — surface the original error to the workflow, which handles it like any other error. | ||
|
|
||
| <ThemedImage | ||
| alt="Review task in the Control Plane showing the failed makePayment input with Proceed, Proceed with input, and Reject" | ||
| sources={{ | ||
| light: useBaseUrl('/img/workflows/develop/review-activity/02-review-task.png'), | ||
| dark: useBaseUrl('/img/workflows/develop/review-activity/02-review-task.png'), | ||
| }} | ||
| /> | ||
|
|
||
| ## Approval gates — review *before* the step runs | ||
|
|
||
| Some steps should never run without sign-off, even when nothing has failed. On a durable agent's activity, enable **Requires Approval** with **Reviewer Roles**: | ||
|
|
||
| ```ballerina | ||
| activities: [ | ||
| validateClaim, | ||
| {activity: payClaim, requiresApproval: true, userRoles: "manager"} | ||
| ] | ||
| ``` | ||
|
|
||
| Before every `payClaim` call the agent pauses and a review task shows the *proposed* input. The manager can proceed, edit the input, or reject the call — the same three decisions as a failure review, just before the step instead of after it. | ||
|
|
||
| ## Error handling in the workflow logic | ||
|
|
||
| Retry policies handle the step; your workflow logic handles the outcome. Because activities return ordinary Ballerina values and errors, error handling looks like normal code: | ||
|
|
||
| ```ballerina | ||
| string|error paymentRef = ctx->callActivity(makePayment, | ||
| {"claimId": claim.claimId, "amount": claim.amount}, retryPolicy = "manager"); | ||
| if paymentRef is error { | ||
| // the reviewer chose Reject — compensate and finish cleanly | ||
| string _ = check ctx->callActivity(notifyEmployee, | ||
| {"claimId": claim.claimId, "message": "Payment could not be completed."}); | ||
| return {claimId: claim.claimId, status: "PAYMENT_FAILED"}; | ||
| } | ||
| ``` | ||
|
|
||
| Because completed activities are never re-executed, compensation logic only ever deals with the step that actually failed — everything before it is already safely recorded. | ||
|
|
||
| ## Choosing a policy | ||
|
|
||
| | Situation | Policy | | ||
| | --- | --- | | ||
| | Flaky downstream, safe to repeat | Auto Retry | | ||
| | Bad input a person could correct | Human Review | | ||
| | Risky/irreversible step (payments, deletions) | Approval gate (`requiresApproval`) | | ||
| | Business-level failure with a fallback path | No Automatic Retry + workflow logic | | ||
|
|
||
| ## Next steps | ||
|
|
||
| - [Human task workflows](human-task-workflow.md) — free-standing decisions and external data. | ||
| - [Durable agentic workflows](durable-agentic-workflow.md) — the same policies applied to an AI agent's activities. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: wso2/docs-integrator
Length of output: 5477
Define concise slugs for these workflow pages and update the links.
These new pages repeat the parent
workflowskeyword, and the tutorial slug repeatsworkflowwhile omittinghandling. Use concise, lowercase slugs such asdurable-agenticandbuild-claim-handling-agent, and update the markdown links that point to the current filenames.📍 Affects 2 files
en/docs/workflows/develop/durable-agentic-workflow.md#L3-L3(this comment)en/docs/workflows/getting-started/build-a-claim-workflow-agent.md#L3-L3🤖 Prompt for AI Agents
Source: Path instructions