agent-workflow-sdk 0.2.0__tar.gz → 0.4.0__tar.gz
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.
- agent_workflow_sdk-0.4.0/.github/ISSUE_TEMPLATE/bug_report.md +30 -0
- agent_workflow_sdk-0.4.0/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
- agent_workflow_sdk-0.4.0/.github/PULL_REQUEST_TEMPLATE.md +17 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/.github/workflows/ci.yml +36 -0
- agent_workflow_sdk-0.4.0/.github/workflows/docs.yml +54 -0
- agent_workflow_sdk-0.4.0/.github/workflows/release.yml +88 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/CHANGELOG.md +69 -2
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/DESIGN.md +17 -7
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/PKG-INFO +114 -42
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/README.md +111 -43
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/__init__.py +2 -2
- agent_workflow_sdk-0.4.0/agentflow/_pg.py +79 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/checkpoint/_serde.py +2 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/checkpoint/base.py +21 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/checkpoint/postgres.py +27 -14
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/compiled.py +46 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/controlplane/memory.py +14 -1
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/controlplane/postgres.py +54 -27
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/controlplane/queue.py +10 -5
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/controlplane/records.py +11 -3
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/controlplane/worker.py +13 -10
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/errors.py +8 -3
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/prebuilt/__init__.py +12 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/prebuilt/effects.py +1 -3
- agent_workflow_sdk-0.4.0/agentflow/prebuilt/watch.py +238 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/runtime.py +27 -2
- agent_workflow_sdk-0.4.0/agentflow/server/__init__.py +46 -0
- agent_workflow_sdk-0.4.0/agentflow/server/app.py +205 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/state.py +1 -1
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/store/postgres.py +31 -18
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/durability/control-plane.md +62 -1
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/patterns/long-running-loops.md +39 -0
- agent_workflow_sdk-0.4.0/docs/reference/api/server.md +23 -0
- agent_workflow_sdk-0.4.0/docs/reference/api/watch.md +12 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/examples/research_plan_implement_review.py +17 -6
- agent_workflow_sdk-0.4.0/examples/serve_control_plane.py +89 -0
- agent_workflow_sdk-0.4.0/examples/watch_until_done.py +101 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/mkdocs.yml +2 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/pyproject.toml +10 -5
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/conftest.py +30 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_gate.py +1 -3
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_idempotent.py +2 -6
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_otel.py +4 -2
- agent_workflow_sdk-0.4.0/tests/test_postgres_checkpointer.py +167 -0
- agent_workflow_sdk-0.4.0/tests/test_postgres_runqueue.py +324 -0
- agent_workflow_sdk-0.4.0/tests/test_postgres_store.py +169 -0
- agent_workflow_sdk-0.4.0/tests/test_server.py +172 -0
- agent_workflow_sdk-0.4.0/tests/test_watch.py +213 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/.env.example +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/.gitignore +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/CONTRIBUTING.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/LICENSE +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/NOTICE +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/SECURITY.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/backends/__init__.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/backends/_http.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/backends/anthropic.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/backends/base.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/backends/claude_code.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/backends/cli_exec.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/backends/codex.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/backends/kiro.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/backends/ollama.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/backends/openai.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/checkpoint/__init__.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/checkpoint/file.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/checkpoint/memory.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/checkpoint/redis.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/checkpoint/sqlite.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/controlplane/__init__.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/controlplane/registry.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/events.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/graph.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/interrupts.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/observability.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/otel.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/prebuilt/gate.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/prebuilt/idempotent.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/prebuilt/loop.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/prebuilt/resilience.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/prebuilt/tool_loop.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/prometheus.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/py.typed +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/redaction.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/store/__init__.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/store/_util.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/store/base.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/store/memory.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/agentflow/telemetry.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/backends/agent-backends.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/backends/llm-backends.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/backends/overview.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/backends/permissions.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/backends/retries.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/concepts/context.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/concepts/execution.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/concepts/nodes-and-edges.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/concepts/running.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/concepts/state.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/concepts/subgraphs.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/durability/checkpointing.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/durability/human-in-the-loop.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/durability/observability.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/durability/quality-gates.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/durability/store.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/get-started/installation.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/get-started/mental-model.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/get-started/overview.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/get-started/quickstart.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/index.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/patterns/prebuilt.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/patterns/tutorial-qa-agent.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/reference/api/backends.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/reference/api/checkpoint.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/reference/api/compiled.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/reference/api/controlplane.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/reference/api/errors.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/reference/api/events.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/reference/api/gate.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/reference/api/graph.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/reference/api/interrupts.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/reference/api/observability.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/reference/api/permissions.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/reference/api/prebuilt.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/reference/api/runtime.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/reference/api/state.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/reference/api/store.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/reference/api.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/docs/reference/glossary.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/examples/agents_demo.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/examples/codebase_qa_ollama.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/examples/hello_graph.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/examples/hitl_permission.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/examples/idempotent_effect.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/examples/mixed_backends.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/examples/optimize_loop.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/examples/quality_gate.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/examples/telemetry_demo.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/examples/tool_loop_ollama.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_anthropic_backend.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_backend_lifecycle.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_checkpoint_contract.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_cli_backends.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_concurrency.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_controlplane.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_effects.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_graph_engine.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_hitl_permission.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_http_retry.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_input_validation.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_kiro_backend.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_live_backends.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_observability.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_ollama_backend.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_openai_backend.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_permission_policy.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_prebuilt_loop.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_prebuilt_no_httpx.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_prometheus.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_provider_resilience.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_redaction.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_redis_checkpointer.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_resilience.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_run_timeout.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_sqlite_checkpointer.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_state_isolation.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_step_atomicity.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_store.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_subgraph.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_sync_facade.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_telemetry.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.4.0}/tests/test_tool_loop.py +0 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug report
|
|
3
|
+
about: Something behaves incorrectly or crashes
|
|
4
|
+
labels: bug
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## What happened
|
|
8
|
+
|
|
9
|
+
A clear description of the bug and what you expected instead.
|
|
10
|
+
|
|
11
|
+
## Reproduction
|
|
12
|
+
|
|
13
|
+
Smallest graph/code that reproduces it. A runnable snippet is ideal.
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
# ...
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Environment
|
|
20
|
+
|
|
21
|
+
- `agent-workflow-sdk` version:
|
|
22
|
+
- Python version:
|
|
23
|
+
- Backend involved (KiroBackend / CodexBackend / ClaudeCodeBackend / OllamaBackend / OpenAIBackend / AnthropicBackend / none):
|
|
24
|
+
- Checkpointer / Store / control-plane backend, if relevant (memory / file / sqlite / redis / postgres):
|
|
25
|
+
|
|
26
|
+
## Logs or traceback
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
paste here
|
|
30
|
+
```
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Feature request
|
|
3
|
+
about: Propose a capability or change
|
|
4
|
+
labels: enhancement
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Problem
|
|
8
|
+
|
|
9
|
+
What are you trying to do that the SDK makes hard or impossible today?
|
|
10
|
+
|
|
11
|
+
## Proposed solution
|
|
12
|
+
|
|
13
|
+
What you'd like to see. If it touches the public API, sketch the shape
|
|
14
|
+
(a node, a backend, a prebuilt pattern, a control-plane method, ...).
|
|
15
|
+
|
|
16
|
+
## Alternatives considered
|
|
17
|
+
|
|
18
|
+
Workarounds you've tried, or other designs you weighed.
|
|
19
|
+
|
|
20
|
+
## Scope check
|
|
21
|
+
|
|
22
|
+
Does this fit a library (engine, backends, control plane) rather than a
|
|
23
|
+
service layer? See `DESIGN.md` for what is intentionally out of scope.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
## What and why
|
|
2
|
+
|
|
3
|
+
What this changes and the problem it solves. Link any related issue.
|
|
4
|
+
|
|
5
|
+
## How
|
|
6
|
+
|
|
7
|
+
Key implementation points a reviewer should know. Note any new public API
|
|
8
|
+
or any change to a documented contract in `DESIGN.md`.
|
|
9
|
+
|
|
10
|
+
## Checklist
|
|
11
|
+
|
|
12
|
+
- [ ] `ruff check agentflow tests examples` passes
|
|
13
|
+
- [ ] `ruff format --check agentflow tests examples` passes
|
|
14
|
+
- [ ] `mypy agentflow` passes
|
|
15
|
+
- [ ] `pytest -q` (offline suite) passes; added/updated tests for the change
|
|
16
|
+
- [ ] Docs updated if behavior or public API changed (README / `docs/` / docstrings)
|
|
17
|
+
- [ ] `CHANGELOG.md` updated under `[Unreleased]` for a user-facing change
|
|
@@ -78,3 +78,39 @@ jobs:
|
|
|
78
78
|
assert "agentflow/py.typed" in names, "py.typed missing from wheel"
|
|
79
79
|
print("py.typed present in", wheel)
|
|
80
80
|
PY
|
|
81
|
+
|
|
82
|
+
pg-tests:
|
|
83
|
+
# The Postgres backends (Store, Checkpointer, RunQueue) are skipped by the
|
|
84
|
+
# hermetic default run, so their real SQL paths are exercised here against
|
|
85
|
+
# a live server started as a service container.
|
|
86
|
+
runs-on: ubuntu-latest
|
|
87
|
+
services:
|
|
88
|
+
postgres:
|
|
89
|
+
image: postgres:16
|
|
90
|
+
env:
|
|
91
|
+
POSTGRES_USER: postgres
|
|
92
|
+
POSTGRES_PASSWORD: postgres
|
|
93
|
+
POSTGRES_DB: agentflow_test
|
|
94
|
+
ports:
|
|
95
|
+
- 5432:5432
|
|
96
|
+
options: >-
|
|
97
|
+
--health-cmd pg_isready
|
|
98
|
+
--health-interval 5s
|
|
99
|
+
--health-timeout 5s
|
|
100
|
+
--health-retries 10
|
|
101
|
+
steps:
|
|
102
|
+
- uses: actions/checkout@v4
|
|
103
|
+
- uses: actions/setup-python@v5
|
|
104
|
+
with:
|
|
105
|
+
python-version: "3.11"
|
|
106
|
+
- name: Install (postgres + ollama + dev extras)
|
|
107
|
+
# ollama is included for httpx: pytest imports every test module during
|
|
108
|
+
# collection, and the HTTP-backend test modules import httpx at top
|
|
109
|
+
# level — without it, collection errors before the -m pg filter applies.
|
|
110
|
+
run: |
|
|
111
|
+
python -m pip install --upgrade pip
|
|
112
|
+
pip install -e '.[postgres,ollama,dev]'
|
|
113
|
+
- name: Run the Postgres test suite
|
|
114
|
+
env:
|
|
115
|
+
POSTGRES_TEST_DSN: postgresql://postgres:postgres@localhost:5432/agentflow_test
|
|
116
|
+
run: pytest -q -m pg tests/test_postgres_store.py tests/test_postgres_checkpointer.py tests/test_postgres_runqueue.py
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
name: Docs
|
|
2
|
+
|
|
3
|
+
# Build the mkdocs site and deploy it to GitHub Pages when docs change on main,
|
|
4
|
+
# or on demand from the Actions tab. Triggering on main (not a tag) matches the
|
|
5
|
+
# github-pages environment's branch protection, which allows the default branch
|
|
6
|
+
# — a tag push is rejected by that rule. Enable Pages once
|
|
7
|
+
# (Settings -> Pages -> Source: GitHub Actions) for the deploy step to publish.
|
|
8
|
+
on:
|
|
9
|
+
push:
|
|
10
|
+
branches: [main]
|
|
11
|
+
paths:
|
|
12
|
+
- "docs/**"
|
|
13
|
+
- "mkdocs.yml"
|
|
14
|
+
- "agentflow/**"
|
|
15
|
+
- ".github/workflows/docs.yml"
|
|
16
|
+
workflow_dispatch:
|
|
17
|
+
|
|
18
|
+
# Allow one concurrent deploy; a newer run supersedes an in-flight one.
|
|
19
|
+
concurrency:
|
|
20
|
+
group: pages
|
|
21
|
+
cancel-in-progress: true
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
build:
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@v4
|
|
28
|
+
- uses: actions/setup-python@v5
|
|
29
|
+
with:
|
|
30
|
+
python-version: "3.11"
|
|
31
|
+
- name: Install docs extra
|
|
32
|
+
run: |
|
|
33
|
+
python -m pip install --upgrade pip
|
|
34
|
+
pip install -e '.[docs]'
|
|
35
|
+
- name: Build the site (strict)
|
|
36
|
+
run: mkdocs build --strict
|
|
37
|
+
- name: Upload the Pages artifact
|
|
38
|
+
uses: actions/upload-pages-artifact@v3
|
|
39
|
+
with:
|
|
40
|
+
path: site
|
|
41
|
+
|
|
42
|
+
deploy:
|
|
43
|
+
needs: build
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
permissions:
|
|
46
|
+
pages: write
|
|
47
|
+
id-token: write
|
|
48
|
+
environment:
|
|
49
|
+
name: github-pages
|
|
50
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
51
|
+
steps:
|
|
52
|
+
- name: Deploy to GitHub Pages
|
|
53
|
+
id: deployment
|
|
54
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Publish to PyPI and cut a GitHub Release when a version tag is pushed.
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
tags:
|
|
7
|
+
- "v*"
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: actions/setup-python@v5
|
|
15
|
+
with:
|
|
16
|
+
python-version: "3.11"
|
|
17
|
+
|
|
18
|
+
- name: Verify the tag matches the packaged version
|
|
19
|
+
# A tag of vX.Y.Z must match pyproject's version and __version__, so a
|
|
20
|
+
# mistagged release fails here instead of publishing the wrong build.
|
|
21
|
+
run: |
|
|
22
|
+
python -m pip install --upgrade pip
|
|
23
|
+
tag="${GITHUB_REF_NAME#v}"
|
|
24
|
+
proj=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
25
|
+
pkg=$(python -c "import re; print(re.search(r'__version__ = \"([^\"]+)\"', open('agentflow/__init__.py').read()).group(1))")
|
|
26
|
+
echo "tag=$tag pyproject=$proj package=$pkg"
|
|
27
|
+
test "$tag" = "$proj" || { echo "tag != pyproject version"; exit 1; }
|
|
28
|
+
test "$tag" = "$pkg" || { echo "tag != __version__"; exit 1; }
|
|
29
|
+
|
|
30
|
+
- name: Build wheel + sdist
|
|
31
|
+
run: |
|
|
32
|
+
python -m pip install build
|
|
33
|
+
python -m build
|
|
34
|
+
|
|
35
|
+
- name: Check artifacts (twine + py.typed)
|
|
36
|
+
run: |
|
|
37
|
+
python -m pip install twine
|
|
38
|
+
python -m twine check dist/*
|
|
39
|
+
python - <<'PY'
|
|
40
|
+
import glob, zipfile
|
|
41
|
+
wheel = glob.glob("dist/*.whl")[0]
|
|
42
|
+
assert "agentflow/py.typed" in zipfile.ZipFile(wheel).namelist(), "py.typed missing"
|
|
43
|
+
print("py.typed present in", wheel)
|
|
44
|
+
PY
|
|
45
|
+
|
|
46
|
+
- name: Upload built artifacts
|
|
47
|
+
uses: actions/upload-artifact@v4
|
|
48
|
+
with:
|
|
49
|
+
name: dist
|
|
50
|
+
path: dist/
|
|
51
|
+
|
|
52
|
+
publish-pypi:
|
|
53
|
+
needs: build
|
|
54
|
+
runs-on: ubuntu-latest
|
|
55
|
+
# Trusted Publishing (OIDC): no API token stored anywhere. Configure the
|
|
56
|
+
# matching publisher for this repo/workflow on PyPI once (Project settings
|
|
57
|
+
# -> Publishing) and every tagged build publishes with no secret.
|
|
58
|
+
environment: pypi
|
|
59
|
+
permissions:
|
|
60
|
+
id-token: write
|
|
61
|
+
steps:
|
|
62
|
+
- name: Fetch built artifacts
|
|
63
|
+
uses: actions/download-artifact@v4
|
|
64
|
+
with:
|
|
65
|
+
name: dist
|
|
66
|
+
path: dist/
|
|
67
|
+
- name: Publish to PyPI
|
|
68
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
69
|
+
|
|
70
|
+
github-release:
|
|
71
|
+
needs: publish-pypi
|
|
72
|
+
runs-on: ubuntu-latest
|
|
73
|
+
permissions:
|
|
74
|
+
contents: write
|
|
75
|
+
steps:
|
|
76
|
+
- uses: actions/checkout@v4
|
|
77
|
+
- name: Fetch built artifacts
|
|
78
|
+
uses: actions/download-artifact@v4
|
|
79
|
+
with:
|
|
80
|
+
name: dist
|
|
81
|
+
path: dist/
|
|
82
|
+
- name: Create the GitHub Release
|
|
83
|
+
env:
|
|
84
|
+
GH_TOKEN: ${{ github.token }}
|
|
85
|
+
run: |
|
|
86
|
+
gh release create "$GITHUB_REF_NAME" dist/* \
|
|
87
|
+
--title "$GITHUB_REF_NAME" \
|
|
88
|
+
--notes "See [CHANGELOG.md](https://github.com/${GITHUB_REPOSITORY}/blob/main/CHANGELOG.md) for what changed."
|
|
@@ -4,6 +4,73 @@ All notable changes to `agent-workflow-sdk` are documented here. The format
|
|
|
4
4
|
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the
|
|
5
5
|
project aims to follow [Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [0.4.0] - 2026-10-03
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Optional HTTP control-plane server (`server` extra): `agentflow.server.
|
|
12
|
+
create_app(queue)` returns a FastAPI ASGI app that maps 1:1 onto a
|
|
13
|
+
`RunQueue`, so runs can be enqueued and observed over the network. Routes:
|
|
14
|
+
`POST /runs`, `GET /runs/{id}`, `GET /runs`, `POST /runs/{id}/resume`,
|
|
15
|
+
`POST /runs/{id}/cancel`, `GET /stats`, `GET /healthz`. Request/response
|
|
16
|
+
bodies are Pydantic models, so FastAPI serves interactive OpenAPI docs at
|
|
17
|
+
`/docs`. `pool_health=` wires a `WorkerPool`'s health into `/healthz`. The
|
|
18
|
+
app adds no behavior of its own — each route forwards to one queue method.
|
|
19
|
+
See `examples/serve_control_plane.py` and the control-plane docs.
|
|
20
|
+
- `server` install extra: `pip install 'agent-workflow-sdk[server]'`
|
|
21
|
+
(`fastapi>=0.110`). The core remains dependency-free; serve the returned
|
|
22
|
+
ASGI app with any ASGI server (e.g. `uvicorn`), installed separately.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- The worker protocol (`claim` / `heartbeat` / `complete`) is deliberately not
|
|
27
|
+
exposed over HTTP; workers talk to the queue directly. The HTTP layer ships
|
|
28
|
+
no authentication, authorization, rate limiting, or multi-tenancy — those
|
|
29
|
+
stay out of scope and are the caller's responsibility (trust boundary /
|
|
30
|
+
reverse proxy). `DESIGN.md` updated accordingly.
|
|
31
|
+
|
|
32
|
+
## [0.3.0] - 2026-10-02
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
- Watch nodes: park a run on an external system without burning turns while
|
|
37
|
+
nothing changes. `ctx.wait(wake_at, payload)` is a timer-based sibling of the
|
|
38
|
+
human interrupt — it suspends the run until a time, then re-runs the node.
|
|
39
|
+
`Checkpoint` gains `wake_at` and a derived `suspend_kind` (`"human"` |
|
|
40
|
+
`"timer"`); the human-interrupt path is unchanged.
|
|
41
|
+
- Durable parked execution: `RunStatus.WAITING` + `RunRecord.wake_at`. A run
|
|
42
|
+
parked on `ctx.wait` is `WAITING` in the queue and does not hold a worker;
|
|
43
|
+
`claim` re-takes it once `wake_at` passes (symmetric to lease expiry), in both
|
|
44
|
+
`MemoryRunQueue` and `PostgresRunQueue`. The worker frees itself on a timer
|
|
45
|
+
suspend and resumes on re-claim.
|
|
46
|
+
- `CompiledGraph.run_until_done(...)` drives timer waits inline (sleeps until
|
|
47
|
+
`wake_at` and resumes), for running a watch loop without the control plane —
|
|
48
|
+
the process must stay alive for the wait, unlike the parked control-plane
|
|
49
|
+
path.
|
|
50
|
+
- `agentflow.prebuilt.watch`: `WatchResult` (idle/activity/terminal), the
|
|
51
|
+
`Watcher` protocol, `CommandWatcher` (poll any command that prints the
|
|
52
|
+
contract JSON), `watch_node` (polls, parks on idle, loops until non-idle), and
|
|
53
|
+
`route_watch`. GitHub/CI/etc. are examples over `CommandWatcher`, not core.
|
|
54
|
+
See `examples/watch_until_done.py`.
|
|
55
|
+
|
|
56
|
+
### Fixed
|
|
57
|
+
|
|
58
|
+
- First-use schema creation on PostgreSQL is now concurrency-safe. `CREATE TABLE
|
|
59
|
+
IF NOT EXISTS` can collide on `pg_type` when two sessions bootstrap the same
|
|
60
|
+
table at once; the Postgres backends now run that DDL under a
|
|
61
|
+
`pg_advisory_xact_lock` (plus an in-process lock), so a pool of workers
|
|
62
|
+
starting together no longer races.
|
|
63
|
+
|
|
64
|
+
### Changed
|
|
65
|
+
|
|
66
|
+
- Internal: the Postgres backends (`PostgresStore`, `PostgresCheckpointer`,
|
|
67
|
+
`PostgresRunQueue`) now have a real-server contract test suite, run in CI
|
|
68
|
+
against a `postgres` service container. The tests are marked `pg` and skipped
|
|
69
|
+
by the default hermetic run (they need `POSTGRES_TEST_DSN`); they cover the
|
|
70
|
+
SQL-only paths the in-memory suites can't — the `if_absent` conditional write
|
|
71
|
+
and `StoreConflict`, the `if_revision` compare-and-set and `CheckpointConflict`,
|
|
72
|
+
and concurrent `claim` under `FOR UPDATE SKIP LOCKED`.
|
|
73
|
+
|
|
7
74
|
## [0.2.0] - 2026-10-01
|
|
8
75
|
|
|
9
76
|
### Added
|
|
@@ -99,8 +166,8 @@ project aims to follow [Semantic Versioning](https://semver.org/).
|
|
|
99
166
|
|
|
100
167
|
## [0.1.0] - 2026-09-29
|
|
101
168
|
|
|
102
|
-
First public release: an async-first
|
|
103
|
-
|
|
169
|
+
First public release: an async-first SDK for agent workflows with pluggable
|
|
170
|
+
agent and LLM backends.
|
|
104
171
|
|
|
105
172
|
### Added
|
|
106
173
|
|
|
@@ -152,9 +152,11 @@ execution state; a store is cross-thread application data.
|
|
|
152
152
|
|
|
153
153
|
## Control plane
|
|
154
154
|
|
|
155
|
-
The control plane manages runs from outside the process that created them.
|
|
156
|
-
is a library layer, not a service
|
|
157
|
-
a thin adapter
|
|
155
|
+
The control plane manages runs from outside the process that created them. The
|
|
156
|
+
core is a library layer, not a service. An optional HTTP layer — the `server`
|
|
157
|
+
extra, `agentflow.server.create_app` — is a thin FastAPI adapter mapping 1:1
|
|
158
|
+
over the queue methods below; it adds no behavior of its own, and auth and
|
|
159
|
+
multi-tenancy remain out of scope (see below).
|
|
158
160
|
|
|
159
161
|
- `GraphRegistry` maps a graph name to a compiled-graph factory. Graphs are
|
|
160
162
|
code and cannot be serialized, so a run request references a graph by name;
|
|
@@ -172,6 +174,13 @@ a thin adapter over these methods).
|
|
|
172
174
|
concurrency.
|
|
173
175
|
- Monitoring: `RunQueue.stats()` and `WorkerPool.health()` return plain values
|
|
174
176
|
a caller can expose through its own `/healthz` and `/metrics`.
|
|
177
|
+
- HTTP (optional, `server` extra): `agentflow.server.create_app(queue)` returns
|
|
178
|
+
a FastAPI ASGI app exposing the enqueue-and-observe plane — `POST /runs`,
|
|
179
|
+
`GET /runs/{id}`, `GET /runs`, `POST /runs/{id}/resume`,
|
|
180
|
+
`POST /runs/{id}/cancel`, `GET /stats`, `GET /healthz`. The worker protocol
|
|
181
|
+
(`claim` / `heartbeat` / `complete`) is deliberately not exposed; workers
|
|
182
|
+
talk to the queue directly. Each route forwards to one queue method, so the
|
|
183
|
+
HTTP layer carries no state of its own.
|
|
175
184
|
|
|
176
185
|
## Observability
|
|
177
186
|
|
|
@@ -197,10 +206,11 @@ See `SECURITY.md` for the reporting process and the operator-facing summary.
|
|
|
197
206
|
|
|
198
207
|
## What is intentionally out of scope
|
|
199
208
|
|
|
200
|
-
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
209
|
+
- Authentication, authorization, and multi-tenancy over the HTTP layer. The
|
|
210
|
+
optional `server` extra adapts the queue to HTTP, but it ships no auth and no
|
|
211
|
+
per-tenant isolation — those are properties of a service, not this SDK. Run
|
|
212
|
+
it inside your own trust boundary, or behind a reverse proxy that supplies
|
|
213
|
+
them. A gRPC API is not provided.
|
|
204
214
|
- Rate limiting and per-tenant quotas. `max_concurrency` /
|
|
205
215
|
`max_node_concurrency` bound pressure within one process; per-tenant
|
|
206
216
|
isolation is the caller's responsibility until the service layer exists.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: agent-workflow-sdk
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: Async-first
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Async-first SDK for orchestrating local coding-agent CLIs (Claude Code, Codex, Kiro) and LLMs as durable, graph-based workflows.
|
|
5
5
|
Project-URL: Homepage, https://github.com/dankosorkin/agent-workflow-sdk
|
|
6
6
|
Project-URL: Repository, https://github.com/dankosorkin/agent-workflow-sdk
|
|
7
7
|
Project-URL: Changelog, https://github.com/dankosorkin/agent-workflow-sdk/blob/main/CHANGELOG.md
|
|
@@ -20,6 +20,8 @@ Classifier: Typing :: Typed
|
|
|
20
20
|
Requires-Python: >=3.11
|
|
21
21
|
Provides-Extra: dev
|
|
22
22
|
Requires-Dist: fakeredis>=2.20; extra == 'dev'
|
|
23
|
+
Requires-Dist: fastapi>=0.110; extra == 'dev'
|
|
24
|
+
Requires-Dist: httpx>=0.27; extra == 'dev'
|
|
23
25
|
Requires-Dist: mypy>=1.11; extra == 'dev'
|
|
24
26
|
Requires-Dist: pip-audit>=2.7; extra == 'dev'
|
|
25
27
|
Requires-Dist: prometheus-client>=0.20; extra == 'dev'
|
|
@@ -41,73 +43,143 @@ Provides-Extra: prometheus
|
|
|
41
43
|
Requires-Dist: prometheus-client>=0.20; extra == 'prometheus'
|
|
42
44
|
Provides-Extra: redis
|
|
43
45
|
Requires-Dist: redis>=5; extra == 'redis'
|
|
46
|
+
Provides-Extra: server
|
|
47
|
+
Requires-Dist: fastapi>=0.110; extra == 'server'
|
|
44
48
|
Description-Content-Type: text/markdown
|
|
45
49
|
|
|
46
50
|
# agent-workflow-sdk
|
|
47
51
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
(
|
|
52
|
-
|
|
52
|
+
[](https://pypi.org/project/agent-workflow-sdk/)
|
|
53
|
+
[](https://pypi.org/project/agent-workflow-sdk/)
|
|
54
|
+
[](https://github.com/dankosorkin/agent-workflow-sdk/actions/workflows/ci.yml)
|
|
55
|
+
[](LICENSE)
|
|
56
|
+
[](https://dankosorkin.github.io/agent-workflow-sdk/)
|
|
53
57
|
|
|
54
|
-
|
|
55
|
-
|
|
58
|
+
An async-first SDK for building agent workflows from composable pieces. You
|
|
59
|
+
describe a workflow as a graph of nodes over a typed, reducer-based state, and
|
|
60
|
+
run it against a pluggable backend — a local coding agent (Claude Code, Codex,
|
|
61
|
+
or Kiro) or a plain LLM (Ollama, any OpenAI-compatible endpoint, or Anthropic).
|
|
56
62
|
|
|
57
|
-
|
|
63
|
+
The import root is `agentflow`. The distribution name is `agent-workflow-sdk`.
|
|
58
64
|
|
|
59
|
-
|
|
60
|
-
|
|
65
|
+
## Who it's for
|
|
66
|
+
|
|
67
|
+
You drive a **local coding-agent CLI** — Claude Code, Codex, or Kiro — and you
|
|
68
|
+
want to orchestrate it with real control flow: branches, loops, retries, a
|
|
69
|
+
human approving a step, a run that survives a crash and resumes. You get that
|
|
70
|
+
without wiring up an API key, because the agent runs through its own CLI and
|
|
71
|
+
its own auth. (An API key path exists too — point a node at `OpenAIBackend` or
|
|
72
|
+
`AnthropicBackend` when you want one — but it's an option, not a requirement.)
|
|
73
|
+
|
|
74
|
+
It is a library, not a coding agent out of the box. If a ready-made agent CLI
|
|
75
|
+
already does what you need, use that. Reach for this when you need to build the
|
|
76
|
+
workflow *around* the agent and keep that logic independent of which backend
|
|
77
|
+
runs underneath.
|
|
78
|
+
|
|
79
|
+
- Build any workflow from primitives: nodes, edges, conditional routing, and a
|
|
80
|
+
shared state. Not a fixed loop, not a linear chain.
|
|
61
81
|
- Swap the backend without touching workflow code. Agents and LLMs share one
|
|
62
82
|
event vocabulary.
|
|
63
83
|
- Async everywhere: the engine, backends, checkpointing, and streaming.
|
|
64
84
|
- Durable by default: every super-step is checkpointed, runs resume after a
|
|
65
85
|
crash, and human-in-the-loop interrupts suspend and resume a run.
|
|
66
86
|
|
|
67
|
-
|
|
68
87
|
## Install
|
|
69
88
|
|
|
89
|
+
```bash
|
|
90
|
+
pip install agent-workflow-sdk
|
|
91
|
+
```
|
|
92
|
+
|
|
70
93
|
The core is dependency-free. Backends that need extra libraries are optional
|
|
71
|
-
extras.
|
|
94
|
+
extras — e.g. `pip install 'agent-workflow-sdk[ollama]'` for the HTTP LLM
|
|
95
|
+
backends. Python 3.11+ is required.
|
|
96
|
+
|
|
97
|
+
The local-agent backends need their CLI on PATH: `claude` for Claude Code,
|
|
98
|
+
`codex` for Codex, `kiro-cli` for Kiro. No API key is needed — each CLI uses
|
|
99
|
+
its own login.
|
|
100
|
+
|
|
101
|
+
To work on the SDK itself, clone and install editable:
|
|
72
102
|
|
|
73
103
|
```bash
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
pip install -e '.[dev]'
|
|
104
|
+
git clone https://github.com/dankosorkin/agent-workflow-sdk
|
|
105
|
+
cd agent-workflow-sdk
|
|
106
|
+
pip install -e '.[dev]'
|
|
77
107
|
```
|
|
78
108
|
|
|
79
|
-
|
|
109
|
+
## Quickstart: a local agent, gated by a human
|
|
80
110
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
111
|
+
A two-step workflow that puts the SDK's point on one screen. A **Claude Code**
|
|
112
|
+
node does the work in its own session; then the run **pauses for a human** to
|
|
113
|
+
approve before anything proceeds. Because every step is checkpointed, the
|
|
114
|
+
pause survives a process restart — you can approve now, tomorrow, or from a
|
|
115
|
+
different process, and the run resumes exactly where it stopped. No API key:
|
|
116
|
+
Claude Code runs through its own CLI login.
|
|
84
117
|
|
|
85
118
|
```python
|
|
86
119
|
import asyncio
|
|
87
120
|
from typing import Annotated
|
|
88
|
-
from agentflow import Graph, START, END, State,
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
121
|
+
from agentflow import Graph, START, END, State, FileCheckpointer, last, append
|
|
122
|
+
from agentflow.backends.claude_code import ClaudeCodeBackend
|
|
123
|
+
from agentflow.backends.base import DenyAll
|
|
124
|
+
from agentflow.events import TextChunk, TurnEnd
|
|
125
|
+
|
|
126
|
+
class ReviewState(State):
|
|
127
|
+
target: Annotated[str, last]
|
|
128
|
+
proposal: Annotated[str, last]
|
|
129
|
+
approved: Annotated[bool, last]
|
|
130
|
+
log: Annotated[list, append]
|
|
131
|
+
|
|
132
|
+
claude = ClaudeCodeBackend(model="sonnet", permission=DenyAll()) # read-only
|
|
133
|
+
|
|
134
|
+
async def propose(state, ctx):
|
|
135
|
+
text = []
|
|
136
|
+
async for ev in claude.prompt(
|
|
137
|
+
f"Review this repo and propose ONE concrete improvement to {state['target']}. "
|
|
138
|
+
"Describe the change; do not apply it."
|
|
139
|
+
):
|
|
140
|
+
if isinstance(ev, TextChunk):
|
|
141
|
+
ctx.emit(ev) # stream tokens to the caller as they arrive
|
|
142
|
+
text.append(ev.text)
|
|
143
|
+
elif isinstance(ev, TurnEnd):
|
|
144
|
+
text = [ev.text] if ev.text else text
|
|
145
|
+
return {"proposal": "".join(text)}
|
|
146
|
+
|
|
147
|
+
async def gate(state, ctx):
|
|
148
|
+
# Suspend the whole run until a human answers. On a fresh run this stops
|
|
149
|
+
# here and checkpoints; on resume, interrupt() returns the human's value.
|
|
150
|
+
decision = await ctx.interrupt({"review": state["proposal"]})
|
|
151
|
+
return {"approved": decision == "approve", "log": f"human said: {decision}"}
|
|
152
|
+
|
|
153
|
+
g = Graph(ReviewState)
|
|
154
|
+
g.add_node("propose", propose)
|
|
155
|
+
g.add_node("gate", gate)
|
|
156
|
+
g.add_edge(START, "propose")
|
|
157
|
+
g.add_edge("propose", "gate")
|
|
158
|
+
g.add_edge("gate", END)
|
|
159
|
+
|
|
160
|
+
async def main():
|
|
161
|
+
app = g.compile(checkpointer=FileCheckpointer(".runs")) # durable
|
|
162
|
+
async with claude:
|
|
163
|
+
state = await app.invoke({"target": "the README"}, thread="demo")
|
|
164
|
+
# The run is now parked at the gate. Show the proposal, get a decision:
|
|
165
|
+
cp = await app.get_state("demo")
|
|
166
|
+
print(cp.interrupt_payload["review"]) # Claude's proposal
|
|
167
|
+
final = await app.resume("demo", value="approve")
|
|
168
|
+
print(final["approved"], final["log"])
|
|
169
|
+
|
|
170
|
+
asyncio.run(main())
|
|
107
171
|
```
|
|
108
172
|
|
|
109
|
-
|
|
110
|
-
|
|
173
|
+
That is the whole value in one example: a real local agent does the work, the
|
|
174
|
+
graph owns the control flow, a human gates the result, and durability means
|
|
175
|
+
the pause is not tied to this process staying alive. Swap `ClaudeCodeBackend`
|
|
176
|
+
for `CodexBackend`, `KiroBackend`, or an LLM backend and the workflow code
|
|
177
|
+
above does not change.
|
|
178
|
+
|
|
179
|
+
Prefer to learn the engine first, with no backend and nothing to install
|
|
180
|
+
beyond the core? `examples/hello_graph.py` is a minimal counter graph that
|
|
181
|
+
shows state, reducers, and edges on their own; `examples/optimize_loop.py`
|
|
182
|
+
shows the iterate-until-converged loop.
|
|
111
183
|
|
|
112
184
|
## Core concepts
|
|
113
185
|
|