@conductor-oss/conductor-skills 1.4.2
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.
- package/.claude-plugin/marketplace.json +20 -0
- package/.claude-plugin/plugin.json +13 -0
- package/LICENSE.txt +176 -0
- package/README.md +352 -0
- package/VERSION +1 -0
- package/bin/conductor-skills.js +135 -0
- package/commands/conductor-optimize.md +18 -0
- package/commands/conductor-scaffold-worker.md +19 -0
- package/commands/conductor-setup.md +15 -0
- package/commands/conductor.md +15 -0
- package/install.ps1 +677 -0
- package/install.sh +855 -0
- package/package.json +50 -0
- package/skills/conductor/SKILL.md +151 -0
- package/skills/conductor/examples/ai-agent-loop.md +119 -0
- package/skills/conductor/examples/ai-agent-mcp.md +129 -0
- package/skills/conductor/examples/create-and-run-workflow.md +50 -0
- package/skills/conductor/examples/do-while-loop.md +72 -0
- package/skills/conductor/examples/fork-join.md +52 -0
- package/skills/conductor/examples/llm-chat.md +61 -0
- package/skills/conductor/examples/llm-rag.md +115 -0
- package/skills/conductor/examples/monitor-and-retry.md +54 -0
- package/skills/conductor/examples/review-workflow.md +67 -0
- package/skills/conductor/examples/signal-wait-task.md +36 -0
- package/skills/conductor/examples/sub-workflow.md +52 -0
- package/skills/conductor/examples/workflows/ai-agent-loop.json +88 -0
- package/skills/conductor/examples/workflows/ai-agent-mcp.json +69 -0
- package/skills/conductor/examples/workflows/child-normalize.json +21 -0
- package/skills/conductor/examples/workflows/do-while-loop.json +35 -0
- package/skills/conductor/examples/workflows/fork-join.json +61 -0
- package/skills/conductor/examples/workflows/llm-chat.json +28 -0
- package/skills/conductor/examples/workflows/llm-rag.json +49 -0
- package/skills/conductor/examples/workflows/parent-pipeline.json +35 -0
- package/skills/conductor/examples/workflows/weather-notification.json +42 -0
- package/skills/conductor/references/api-reference.md +111 -0
- package/skills/conductor/references/cli-index.md +92 -0
- package/skills/conductor/references/fallback-cli.md +36 -0
- package/skills/conductor/references/optimization.md +148 -0
- package/skills/conductor/references/orkes.md +57 -0
- package/skills/conductor/references/schedules.md +81 -0
- package/skills/conductor/references/setup.md +126 -0
- package/skills/conductor/references/troubleshooting.md +35 -0
- package/skills/conductor/references/visualization.md +49 -0
- package/skills/conductor/references/workers.md +227 -0
- package/skills/conductor/references/workflow-definition.md +672 -0
- package/skills/conductor/scripts/conductor_api.py +396 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Schedules
|
|
2
|
+
|
|
3
|
+
Run a workflow on a cron schedule. Schedules are part of OSS Conductor.
|
|
4
|
+
|
|
5
|
+
## CLI
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
conductor schedule list
|
|
9
|
+
conductor schedule get {name}
|
|
10
|
+
conductor schedule create schedule.json
|
|
11
|
+
conductor schedule update schedule.json
|
|
12
|
+
conductor schedule delete {name}
|
|
13
|
+
conductor schedule pause {name}
|
|
14
|
+
conductor schedule resume {name}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The Python fallback script does **not** include schedule commands — the CLI is required.
|
|
18
|
+
|
|
19
|
+
## Schedule definition
|
|
20
|
+
|
|
21
|
+
Write the schedule to a JSON file, then `conductor schedule create file.json`.
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"name": "nightly-cleanup",
|
|
26
|
+
"cronExpression": "0 0 2 * * ?",
|
|
27
|
+
"startWorkflowRequest": {
|
|
28
|
+
"name": "cleanup_workflow",
|
|
29
|
+
"version": 1,
|
|
30
|
+
"input": { "olderThanDays": 30 },
|
|
31
|
+
"correlationId": "scheduled-${date}"
|
|
32
|
+
},
|
|
33
|
+
"scheduleStartTime": 0,
|
|
34
|
+
"scheduleEndTime": 0,
|
|
35
|
+
"paused": false
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
| Field | Type | Required | Description |
|
|
40
|
+
|-------|------|----------|-------------|
|
|
41
|
+
| `name` | string | yes | Unique schedule name |
|
|
42
|
+
| `cronExpression` | string | yes | Quartz cron (6 or 7 fields, see below) |
|
|
43
|
+
| `startWorkflowRequest` | object | yes | The workflow to start each tick |
|
|
44
|
+
| `scheduleStartTime` | long | no | Epoch ms; `0` = no start bound |
|
|
45
|
+
| `scheduleEndTime` | long | no | Epoch ms; `0` = no end bound |
|
|
46
|
+
| `paused` | boolean | no | If true, schedule exists but does not fire |
|
|
47
|
+
| `description` | string | no | Human description |
|
|
48
|
+
|
|
49
|
+
`startWorkflowRequest` mirrors `POST /workflow`: `name`, `version`, `input`, `correlationId`, `taskToDomain`.
|
|
50
|
+
|
|
51
|
+
## Cron expression format
|
|
52
|
+
|
|
53
|
+
Quartz cron — 6 fields (no year) or 7 fields (with year):
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
seconds minutes hours day-of-month month day-of-week [year]
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
| Pattern | Meaning |
|
|
60
|
+
|---------|---------|
|
|
61
|
+
| `0 0 * * * ?` | Every hour, on the hour |
|
|
62
|
+
| `0 0 2 * * ?` | Every day at 02:00 |
|
|
63
|
+
| `0 30 9 ? * MON-FRI` | Weekdays at 09:30 |
|
|
64
|
+
| `0 0 0 1 * ?` | First of every month, midnight |
|
|
65
|
+
| `0 */15 * * * ?` | Every 15 minutes |
|
|
66
|
+
|
|
67
|
+
Either day-of-month or day-of-week must be `?` (Quartz quirk — they can't both be specified). Cron is evaluated in the server's timezone unless you configure otherwise.
|
|
68
|
+
|
|
69
|
+
## Patterns
|
|
70
|
+
|
|
71
|
+
**Idempotency.** Use a derived `correlationId` (e.g. `nightly-cleanup-${date}`) so a schedule that double-fires won't create duplicate work — your workflow can detect the existing run and short-circuit.
|
|
72
|
+
|
|
73
|
+
**Pausing without deleting.** Set `paused: true` and update — preserves history vs delete.
|
|
74
|
+
|
|
75
|
+
**Ad-hoc backfill.** To run the scheduled workflow immediately for a missed window, just `conductor workflow start -w {name} -i '{...}'` — the schedule is just a trigger, not the workflow.
|
|
76
|
+
|
|
77
|
+
**Monitoring.** Schedules don't have their own execution history. Search executions by `correlationId` prefix to find scheduled runs:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
conductor workflow search -q "correlationId:scheduled-*" -c 50
|
|
81
|
+
```
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Server Setup
|
|
2
|
+
|
|
3
|
+
Connect the agent to a Conductor server. Run through this once per environment.
|
|
4
|
+
|
|
5
|
+
## Server resolution order
|
|
6
|
+
|
|
7
|
+
`--profile` > `CONDUCTOR_SERVER_URL` > CLI auto-detection of a local server started via `conductor server start`.
|
|
8
|
+
|
|
9
|
+
Most commands need no flags — the CLI finds the server automatically. Only append `--profile {env}` when the user mentions a named environment (dev, qa, prod, staging, uat). If unsure which profile exists, read `~/.conductor-cli/config.yaml` and ask the user to confirm. Only create named profiles when the user explicitly wants to switch between multiple environments.
|
|
10
|
+
|
|
11
|
+
The Python fallback script (`scripts/conductor_api.py`) does **not** auto-detect — it requires `CONDUCTOR_SERVER_URL` to be set explicitly.
|
|
12
|
+
|
|
13
|
+
## Step 1 — Install the CLI
|
|
14
|
+
|
|
15
|
+
Check whether `conductor` is already installed:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
conductor --version
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
If not installed, check for npm/Node.js:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm --version
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
If npm is also missing, install Node.js first:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# macOS
|
|
31
|
+
brew install node
|
|
32
|
+
# Linux (Debian/Ubuntu)
|
|
33
|
+
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash - && sudo apt-get install -y nodejs
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
For installing the CLI itself, two options:
|
|
37
|
+
|
|
38
|
+
- **Recommended (no global install):** invoke as `npx @conductor-oss/conductor-cli ...` for one-off use. No system modification.
|
|
39
|
+
- **Global install (ask the user first):** `npm install -g @conductor-oss/conductor-cli`. Modifies the global npm prefix — confirm before running. Once approved, verify with `conductor --version`.
|
|
40
|
+
|
|
41
|
+
**Fallback** — only after the CLI is genuinely unavailable (`conductor --version` fails) **and** Node/npm cannot be installed (restricted environment, no package manager), fall back to the bundled REST API script:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
export CONDUCTOR_API="<path-to-this-skill>/scripts/conductor_api.py"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The fallback supports a subset of CLI commands — see [fallback-cli.md](fallback-cli.md). It does not support key/secret auth (token only), profiles, server auto-detection, taskDef CRUD, or time-range search.
|
|
48
|
+
|
|
49
|
+
## Step 2 — Choose a server
|
|
50
|
+
|
|
51
|
+
Ask the user:
|
|
52
|
+
|
|
53
|
+
- **Option A** — Start a local server (good for development/testing).
|
|
54
|
+
- **Option B** — Connect to an existing remote server.
|
|
55
|
+
|
|
56
|
+
Don't assume — present both.
|
|
57
|
+
|
|
58
|
+
**Option A — local server:**
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
conductor server start
|
|
62
|
+
# custom port:
|
|
63
|
+
conductor server start --port 3000
|
|
64
|
+
# verify:
|
|
65
|
+
conductor server status
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Option B — existing server:**
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
export CONDUCTOR_SERVER_URL="http://your-server:8080/api"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Step 3 — Test connectivity and handle auth
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
conductor workflow list
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
If this succeeds, the server has no auth — go to Step 4.
|
|
81
|
+
|
|
82
|
+
If you get **401 or 403**, the server requires authentication. Only then ask the user for credentials. Set them via env vars (never echo the values):
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# Key + Secret (recommended for Orkes / Enterprise)
|
|
86
|
+
export CONDUCTOR_AUTH_KEY="<ask user>"
|
|
87
|
+
export CONDUCTOR_AUTH_SECRET="<ask user>"
|
|
88
|
+
|
|
89
|
+
# Or a pre-existing token
|
|
90
|
+
export CONDUCTOR_AUTH_TOKEN="<ask user>"
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Re-test: `conductor workflow list`
|
|
94
|
+
|
|
95
|
+
> **Auth header:** the REST API expects `X-Authorization: <token>`. The CLI handles this automatically. If using the Python fallback, only `CONDUCTOR_AUTH_TOKEN` is supported — key/secret exchange is not implemented.
|
|
96
|
+
|
|
97
|
+
## Step 4 — Verify
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
conductor workflow list
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Report the result to the user. Setup complete.
|
|
104
|
+
|
|
105
|
+
## Optional — named profiles
|
|
106
|
+
|
|
107
|
+
For switching between multiple servers (dev / staging / prod):
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
conductor config save --server https://dev.example.com/api --auth-key KEY --auth-secret SECRET --profile dev
|
|
111
|
+
conductor config save --server https://prod.example.com/api --auth-key KEY --auth-secret SECRET --profile prod
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Then append `--profile {env}` on any command. Profiles live in `~/.conductor-cli/config.yaml`.
|
|
115
|
+
|
|
116
|
+
## Updating this skill
|
|
117
|
+
|
|
118
|
+
If the user asks to upgrade or you suspect this skill is outdated:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# macOS / Linux
|
|
122
|
+
curl -sSL https://conductor-oss.github.io/conductor-skills/install.sh | bash -s -- --all --upgrade
|
|
123
|
+
|
|
124
|
+
# Windows
|
|
125
|
+
irm https://conductor-oss.github.io/conductor-skills/install.ps1 -OutFile install.ps1; .\install.ps1 -All -Upgrade
|
|
126
|
+
```
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Troubleshooting & Output
|
|
2
|
+
|
|
3
|
+
## Output formatting
|
|
4
|
+
|
|
5
|
+
- Present workflow data as structured summaries: `workflowId`, `status`, `startTime`, `endTime`, failed-task details.
|
|
6
|
+
- For searches, render a table with `workflowId`, `name`, `status`, `startTime`.
|
|
7
|
+
- On failures, include the failed task name, error message, and retry count.
|
|
8
|
+
- Never echo auth tokens, keys, or secrets in output or logs.
|
|
9
|
+
|
|
10
|
+
## Common errors
|
|
11
|
+
|
|
12
|
+
| Symptom | Likely cause | Fix |
|
|
13
|
+
|---------|--------------|-----|
|
|
14
|
+
| `conductor: command not found` | CLI not installed | Run `npx @conductor-oss/conductor-cli ...`, or ask the user before global install (see [setup.md](setup.md)). If npm itself is missing, fall back to `scripts/conductor_api.py`. |
|
|
15
|
+
| `Connection refused` / `URLError` | Server not running, or wrong URL | Verify `CONDUCTOR_SERVER_URL`. For local servers run `conductor server status`. |
|
|
16
|
+
| `401 Unauthorized` | Missing or invalid auth | Check `CONDUCTOR_AUTH_TOKEN` (or `CONDUCTOR_AUTH_KEY` + `_SECRET` with the CLI). Re-run `conductor workflow list` to confirm. |
|
|
17
|
+
| `403 Forbidden` | Token valid but lacks permissions | Confirm with the user that the credentials have access to the target workflow/namespace. |
|
|
18
|
+
| `404 Not Found` | Wrong workflow name, version, or execution ID | Run `conductor workflow list` or `conductor workflow search` to find the correct identifier. |
|
|
19
|
+
| Workflow stuck on a SIMPLE task | No worker polling for that task type | Run `conductor task queue-size --task-type {name}` — if size > 0 and growing, no worker is consuming. Scaffold a worker (see [workers.md](workers.md)). |
|
|
20
|
+
| `409 Conflict` on workflow create | Definition with that name+version already exists | Bump version, or use update instead of create. |
|
|
21
|
+
| 5xx errors | Server-side issue | The fallback script auto-retries 3× with backoff. CLI may need a manual retry. Surface server error to the user. |
|
|
22
|
+
|
|
23
|
+
## Diagnosis flow for failed workflows
|
|
24
|
+
|
|
25
|
+
1. `conductor workflow get-execution {id} -c` — full task list with statuses.
|
|
26
|
+
2. Identify the failed task (`status: FAILED` or `TIMED_OUT`) and its `reasonForIncompletion`.
|
|
27
|
+
3. Decide:
|
|
28
|
+
- `TIMED_OUT` with retries remaining → `conductor workflow retry {id}`.
|
|
29
|
+
- `FAILED_WITH_TERMINAL_ERROR` → not retryable; fix root cause first.
|
|
30
|
+
- Persistent timeouts → recommend raising `responseTimeoutSeconds` on the task definition.
|
|
31
|
+
|
|
32
|
+
## Docs
|
|
33
|
+
|
|
34
|
+
- General Conductor docs: https://orkes.io/content/
|
|
35
|
+
- REST endpoints: see [api-reference.md](api-reference.md)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Workflow Visualization
|
|
2
|
+
|
|
3
|
+
Generate a Mermaid flowchart when the user asks to visualize a workflow, or after creating one. Renders in any Markdown viewer (GitHub, VS Code, etc.).
|
|
4
|
+
|
|
5
|
+
Always pair the diagram with a **1–2 sentence text summary of the flow** ("Fetches data, then either notifies on success or terminates on failure"). The summary helps users skim and is the accessibility fallback for screen readers and any environment that doesn't render Mermaid.
|
|
6
|
+
|
|
7
|
+
## Diagram rules
|
|
8
|
+
|
|
9
|
+
- Use `flowchart TD` for sequential workflows, `flowchart LR` for wide parallel flows.
|
|
10
|
+
- Only use `-->` arrows and `-->|label|` for labeled edges.
|
|
11
|
+
- Do **not** use `title`, `style`, or `classDef`.
|
|
12
|
+
- In **edge label text** (between `|...|`), avoid `{}[]()` — they break Mermaid parsing. Node shapes (`[]`, `()`, `{}`) are fine; just keep the text inside short.
|
|
13
|
+
- Keep node labels short: task type + reference name, e.g. `fetch_data[HTTP: fetch_data]`.
|
|
14
|
+
|
|
15
|
+
## Mapping Conductor constructs
|
|
16
|
+
|
|
17
|
+
| Construct | Mermaid pattern |
|
|
18
|
+
|-----------|-----------------|
|
|
19
|
+
| Sequential tasks | `task1 --> task2 --> task3` |
|
|
20
|
+
| SWITCH (decision) | `sw{Switch: ref}` with `-->|case: value|` edges per case + `-->|default|` |
|
|
21
|
+
| FORK_JOIN (parallel) | `fork[Fork] --> branch_a & branch_b` then both `--> join[Join]` |
|
|
22
|
+
| DO_WHILE (loop) | `loop[DO_WHILE: ref] --> body --> loop` with `body -->|done| next` |
|
|
23
|
+
| SUB_WORKFLOW | `sub([Sub: workflow_name])` rounded node |
|
|
24
|
+
| WAIT / HUMAN | `wait[/WAIT: ref/]` parallelogram (signals external input) |
|
|
25
|
+
|
|
26
|
+
## Example
|
|
27
|
+
|
|
28
|
+
````markdown
|
|
29
|
+
```mermaid
|
|
30
|
+
flowchart TD
|
|
31
|
+
start([Start]) --> fetch[HTTP: fetch_data]
|
|
32
|
+
fetch --> check{Switch: check_status}
|
|
33
|
+
check -->|case: ok| transform[INLINE: transform]
|
|
34
|
+
check -->|default| fail[TERMINATE: fail]
|
|
35
|
+
transform --> approve[/WAIT: approval/]
|
|
36
|
+
approve --> notify[HTTP: send_notification]
|
|
37
|
+
notify --> done([End])
|
|
38
|
+
```
|
|
39
|
+
````
|
|
40
|
+
|
|
41
|
+
## Conductor UI link
|
|
42
|
+
|
|
43
|
+
If a Conductor server is reachable, also offer a link to the visual editor:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
{BASE_URL}/workflowDef/{workflowName}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Where `BASE_URL` is `CONDUCTOR_SERVER_URL` with the trailing `/api` stripped. Example: `http://localhost:8080/api` → `http://localhost:8080/workflowDef/order-processing`. Always resolve the actual URL — never output `{SERVER_UI_URL}` literally.
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# Writing Conductor Workers
|
|
2
|
+
|
|
3
|
+
Workers execute SIMPLE tasks in a workflow. They poll the Conductor server for tasks, execute business logic, and report results back. Workers are stateless and idempotent.
|
|
4
|
+
|
|
5
|
+
## How workers work
|
|
6
|
+
|
|
7
|
+
1. Workflow reaches a SIMPLE task
|
|
8
|
+
2. Task is placed in a queue
|
|
9
|
+
3. Worker polls the queue, picks up the task
|
|
10
|
+
4. Worker executes logic using task's `inputData`
|
|
11
|
+
5. Worker returns result (COMPLETED or FAILED) with `outputData`
|
|
12
|
+
6. Workflow continues to the next task
|
|
13
|
+
|
|
14
|
+
## SDKs
|
|
15
|
+
|
|
16
|
+
| Language | Package | Install |
|
|
17
|
+
|----------|---------|---------|
|
|
18
|
+
| Python | `conductor-python` | `pip install conductor-python` |
|
|
19
|
+
| JavaScript/TypeScript | `@io-orkes/conductor-javascript` | `npm install @io-orkes/conductor-javascript` |
|
|
20
|
+
| Java | `org.conductoross:conductor-client` | Maven/Gradle (see below) |
|
|
21
|
+
| Go | `github.com/conductor-sdk/conductor-go` | `go get github.com/conductor-sdk/conductor-go` |
|
|
22
|
+
| C# | [conductor-oss/csharp-sdk](https://github.com/conductor-oss/csharp-sdk) | NuGet |
|
|
23
|
+
| Ruby | [conductor-oss/ruby-sdk](https://github.com/conductor-oss/ruby-sdk) | Gem |
|
|
24
|
+
| Rust | [conductor-oss/rust-sdk](https://github.com/conductor-oss/rust-sdk) | Cargo |
|
|
25
|
+
|
|
26
|
+
All SDKs connect via the same env vars: `CONDUCTOR_SERVER_URL`, `CONDUCTOR_AUTH_KEY`, `CONDUCTOR_AUTH_SECRET`.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Python
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install conductor-python
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Define a worker
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from conductor.client.worker.worker_task import worker_task
|
|
40
|
+
|
|
41
|
+
@worker_task(task_definition_name='process_order')
|
|
42
|
+
def process_order(order_id: str, amount: float) -> dict:
|
|
43
|
+
# Your business logic here
|
|
44
|
+
return {'status': 'processed', 'order_id': order_id, 'total': amount * 1.1}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Function parameters are automatically mapped from the task's `inputParameters`. The return value becomes the task's `outputData`.
|
|
48
|
+
|
|
49
|
+
### Start workers
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from conductor.client.automator.task_handler import TaskHandler
|
|
53
|
+
from conductor.client.configuration.configuration import Configuration
|
|
54
|
+
|
|
55
|
+
config = Configuration() # reads CONDUCTOR_SERVER_URL, CONDUCTOR_AUTH_KEY, CONDUCTOR_AUTH_SECRET
|
|
56
|
+
with TaskHandler(configuration=config, scan_for_annotated_workers=True) as handler:
|
|
57
|
+
handler.start_processes()
|
|
58
|
+
# Workers poll until stopped
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## JavaScript / TypeScript
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npm install @io-orkes/conductor-javascript
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Define and run a worker
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import {
|
|
73
|
+
orkesConductorClient,
|
|
74
|
+
TaskManager,
|
|
75
|
+
} from "@io-orkes/conductor-javascript";
|
|
76
|
+
|
|
77
|
+
const client = await orkesConductorClient({
|
|
78
|
+
serverUrl: "http://localhost:8080/api",
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const taskManager = new TaskManager(client, [
|
|
82
|
+
{
|
|
83
|
+
taskType: "process_order",
|
|
84
|
+
execute: async ({ inputData }) => {
|
|
85
|
+
return {
|
|
86
|
+
status: "COMPLETED",
|
|
87
|
+
outputData: {
|
|
88
|
+
status: "processed",
|
|
89
|
+
order_id: inputData.order_id,
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
]);
|
|
95
|
+
|
|
96
|
+
taskManager.startPolling();
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Java
|
|
102
|
+
|
|
103
|
+
**Gradle:**
|
|
104
|
+
```gradle
|
|
105
|
+
implementation 'org.conductoross:conductor-client:5.0.0'
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Maven:**
|
|
109
|
+
```xml
|
|
110
|
+
<dependency>
|
|
111
|
+
<groupId>org.conductoross</groupId>
|
|
112
|
+
<artifactId>conductor-client</artifactId>
|
|
113
|
+
<version>5.0.0</version>
|
|
114
|
+
</dependency>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Option A: Implement Worker interface
|
|
118
|
+
|
|
119
|
+
```java
|
|
120
|
+
public class ProcessOrderWorker implements Worker {
|
|
121
|
+
@Override
|
|
122
|
+
public String getTaskDefName() {
|
|
123
|
+
return "process_order";
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@Override
|
|
127
|
+
public TaskResult execute(Task task) {
|
|
128
|
+
String orderId = (String) task.getInputData().get("order_id");
|
|
129
|
+
TaskResult result = new TaskResult(task);
|
|
130
|
+
result.setStatus(TaskResult.Status.COMPLETED);
|
|
131
|
+
result.addOutputData("status", "processed");
|
|
132
|
+
result.addOutputData("order_id", orderId);
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Option B: @WorkerTask annotation
|
|
139
|
+
|
|
140
|
+
```java
|
|
141
|
+
public class Workers {
|
|
142
|
+
@WorkerTask("process_order")
|
|
143
|
+
public Map<String, Object> processOrder(@InputParam("order_id") String orderId) {
|
|
144
|
+
return Map.of("status", "processed", "order_id", orderId);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Start workers
|
|
150
|
+
|
|
151
|
+
```java
|
|
152
|
+
ConductorClient client = ConductorClient.builder()
|
|
153
|
+
.basePath("http://localhost:8080/api")
|
|
154
|
+
.build();
|
|
155
|
+
|
|
156
|
+
TaskClient taskClient = new TaskClient(client);
|
|
157
|
+
new TaskRunnerConfigurer.Builder(taskClient, List.of(new ProcessOrderWorker()))
|
|
158
|
+
.withThreadCount(10)
|
|
159
|
+
.build()
|
|
160
|
+
.init();
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Go
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
go get github.com/conductor-sdk/conductor-go
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Define and run a worker
|
|
172
|
+
|
|
173
|
+
```go
|
|
174
|
+
package main
|
|
175
|
+
|
|
176
|
+
import (
|
|
177
|
+
"fmt"
|
|
178
|
+
"time"
|
|
179
|
+
"github.com/conductor-sdk/conductor-go/sdk/client"
|
|
180
|
+
"github.com/conductor-sdk/conductor-go/sdk/model"
|
|
181
|
+
"github.com/conductor-sdk/conductor-go/sdk/worker"
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
func ProcessOrder(task *model.Task) (interface{}, error) {
|
|
185
|
+
orderId := fmt.Sprintf("%v", task.InputData["order_id"])
|
|
186
|
+
return map[string]interface{}{
|
|
187
|
+
"status": "processed",
|
|
188
|
+
"order_id": orderId,
|
|
189
|
+
}, nil
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
func main() {
|
|
193
|
+
apiClient := client.NewAPIClientFromEnv()
|
|
194
|
+
taskRunner := worker.NewTaskRunnerWithApiClient(apiClient)
|
|
195
|
+
taskRunner.StartWorker("process_order", ProcessOrder, 1, time.Millisecond*100)
|
|
196
|
+
// Blocks and polls until stopped
|
|
197
|
+
select {}
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Linking workers to workflows
|
|
204
|
+
|
|
205
|
+
In your workflow definition, use `"type": "SIMPLE"` and set `"name"` to the task type your worker polls for:
|
|
206
|
+
|
|
207
|
+
```json
|
|
208
|
+
{
|
|
209
|
+
"name": "process_order",
|
|
210
|
+
"taskReferenceName": "process_order_ref",
|
|
211
|
+
"type": "SIMPLE",
|
|
212
|
+
"inputParameters": {
|
|
213
|
+
"order_id": "${workflow.input.order_id}",
|
|
214
|
+
"amount": "${workflow.input.amount}"
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
The worker registered for task type `process_order` will automatically pick up this task when the workflow reaches it.
|
|
220
|
+
|
|
221
|
+
## Best practices
|
|
222
|
+
|
|
223
|
+
- **Idempotent**: Workers may receive the same task on retry. Design for safe re-execution.
|
|
224
|
+
- **Timeouts**: Set `responseTimeoutSeconds` on task definitions so stuck tasks get rescheduled.
|
|
225
|
+
- **Error handling**: Return `FAILED` status with a `reasonForIncompletion` message for graceful failures. Return `FAILED_WITH_TERMINAL_ERROR` to fail the task without retrying.
|
|
226
|
+
- **Scaling**: Run multiple worker instances for throughput. Each polls independently.
|
|
227
|
+
- **Domain isolation**: Use task domains to route tasks to specific worker groups (e.g. region-specific workers).
|