agent-workflow-sdk 0.2.0__tar.gz → 0.3.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.3.0/.github/ISSUE_TEMPLATE/bug_report.md +30 -0
- agent_workflow_sdk-0.3.0/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
- agent_workflow_sdk-0.3.0/.github/PULL_REQUEST_TEMPLATE.md +17 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/.github/workflows/ci.yml +36 -0
- agent_workflow_sdk-0.3.0/.github/workflows/docs.yml +48 -0
- agent_workflow_sdk-0.3.0/.github/workflows/release.yml +88 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/CHANGELOG.md +44 -2
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/PKG-INFO +110 -42
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/README.md +111 -43
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/__init__.py +2 -2
- agent_workflow_sdk-0.3.0/agentflow/_pg.py +79 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/checkpoint/_serde.py +2 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/checkpoint/base.py +21 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/checkpoint/postgres.py +27 -14
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/compiled.py +46 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/controlplane/memory.py +14 -1
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/controlplane/postgres.py +54 -27
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/controlplane/queue.py +10 -5
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/controlplane/records.py +11 -3
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/controlplane/worker.py +13 -10
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/errors.py +8 -3
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/prebuilt/__init__.py +12 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/prebuilt/effects.py +1 -3
- agent_workflow_sdk-0.3.0/agentflow/prebuilt/watch.py +238 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/runtime.py +27 -2
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/state.py +1 -1
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/store/postgres.py +31 -18
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/patterns/long-running-loops.md +39 -0
- agent_workflow_sdk-0.3.0/docs/reference/api/watch.md +12 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/examples/research_plan_implement_review.py +17 -6
- agent_workflow_sdk-0.3.0/examples/watch_until_done.py +101 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/mkdocs.yml +1 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/pyproject.toml +7 -5
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/conftest.py +30 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_gate.py +1 -3
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_idempotent.py +2 -6
- agent_workflow_sdk-0.3.0/tests/test_postgres_checkpointer.py +167 -0
- agent_workflow_sdk-0.3.0/tests/test_postgres_runqueue.py +324 -0
- agent_workflow_sdk-0.3.0/tests/test_postgres_store.py +169 -0
- agent_workflow_sdk-0.3.0/tests/test_watch.py +213 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/.env.example +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/.gitignore +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/CONTRIBUTING.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/DESIGN.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/LICENSE +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/NOTICE +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/SECURITY.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/backends/__init__.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/backends/_http.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/backends/anthropic.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/backends/base.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/backends/claude_code.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/backends/cli_exec.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/backends/codex.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/backends/kiro.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/backends/ollama.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/backends/openai.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/checkpoint/__init__.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/checkpoint/file.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/checkpoint/memory.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/checkpoint/redis.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/checkpoint/sqlite.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/controlplane/__init__.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/controlplane/registry.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/events.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/graph.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/interrupts.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/observability.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/otel.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/prebuilt/gate.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/prebuilt/idempotent.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/prebuilt/loop.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/prebuilt/resilience.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/prebuilt/tool_loop.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/prometheus.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/py.typed +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/redaction.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/store/__init__.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/store/_util.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/store/base.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/store/memory.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/agentflow/telemetry.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/backends/agent-backends.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/backends/llm-backends.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/backends/overview.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/backends/permissions.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/backends/retries.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/concepts/context.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/concepts/execution.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/concepts/nodes-and-edges.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/concepts/running.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/concepts/state.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/concepts/subgraphs.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/durability/checkpointing.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/durability/control-plane.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/durability/human-in-the-loop.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/durability/observability.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/durability/quality-gates.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/durability/store.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/get-started/installation.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/get-started/mental-model.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/get-started/overview.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/get-started/quickstart.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/index.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/patterns/prebuilt.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/patterns/tutorial-qa-agent.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/reference/api/backends.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/reference/api/checkpoint.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/reference/api/compiled.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/reference/api/controlplane.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/reference/api/errors.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/reference/api/events.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/reference/api/gate.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/reference/api/graph.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/reference/api/interrupts.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/reference/api/observability.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/reference/api/permissions.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/reference/api/prebuilt.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/reference/api/runtime.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/reference/api/state.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/reference/api/store.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/reference/api.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/docs/reference/glossary.md +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/examples/agents_demo.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/examples/codebase_qa_ollama.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/examples/hello_graph.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/examples/hitl_permission.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/examples/idempotent_effect.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/examples/mixed_backends.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/examples/optimize_loop.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/examples/quality_gate.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/examples/telemetry_demo.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/examples/tool_loop_ollama.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_anthropic_backend.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_backend_lifecycle.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_checkpoint_contract.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_cli_backends.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_concurrency.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_controlplane.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_effects.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_graph_engine.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_hitl_permission.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_http_retry.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_input_validation.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_kiro_backend.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_live_backends.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_observability.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_ollama_backend.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_openai_backend.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_otel.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_permission_policy.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_prebuilt_loop.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_prebuilt_no_httpx.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_prometheus.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_provider_resilience.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_redaction.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_redis_checkpointer.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_resilience.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_run_timeout.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_sqlite_checkpointer.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_state_isolation.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_step_atomicity.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_store.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_subgraph.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_sync_facade.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.0}/tests/test_telemetry.py +0 -0
- {agent_workflow_sdk-0.2.0 → agent_workflow_sdk-0.3.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,48 @@
|
|
|
1
|
+
name: Docs
|
|
2
|
+
|
|
3
|
+
# Build the mkdocs site and deploy it to GitHub Pages on a version tag, or on
|
|
4
|
+
# demand from the Actions tab. Enable Pages once (Settings -> Pages -> Source:
|
|
5
|
+
# GitHub Actions) for the deploy step to publish.
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
tags:
|
|
9
|
+
- "v*"
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
|
|
12
|
+
# Allow one concurrent deploy; a newer run supersedes an in-flight one.
|
|
13
|
+
concurrency:
|
|
14
|
+
group: pages
|
|
15
|
+
cancel-in-progress: true
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
build:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
- uses: actions/setup-python@v5
|
|
23
|
+
with:
|
|
24
|
+
python-version: "3.11"
|
|
25
|
+
- name: Install docs extra
|
|
26
|
+
run: |
|
|
27
|
+
python -m pip install --upgrade pip
|
|
28
|
+
pip install -e '.[docs]'
|
|
29
|
+
- name: Build the site (strict)
|
|
30
|
+
run: mkdocs build --strict
|
|
31
|
+
- name: Upload the Pages artifact
|
|
32
|
+
uses: actions/upload-pages-artifact@v3
|
|
33
|
+
with:
|
|
34
|
+
path: site
|
|
35
|
+
|
|
36
|
+
deploy:
|
|
37
|
+
needs: build
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
permissions:
|
|
40
|
+
pages: write
|
|
41
|
+
id-token: write
|
|
42
|
+
environment:
|
|
43
|
+
name: github-pages
|
|
44
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
45
|
+
steps:
|
|
46
|
+
- name: Deploy to GitHub Pages
|
|
47
|
+
id: deployment
|
|
48
|
+
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,48 @@ 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.3.0] - 2026-10-02
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Watch nodes: park a run on an external system without burning turns while
|
|
12
|
+
nothing changes. `ctx.wait(wake_at, payload)` is a timer-based sibling of the
|
|
13
|
+
human interrupt — it suspends the run until a time, then re-runs the node.
|
|
14
|
+
`Checkpoint` gains `wake_at` and a derived `suspend_kind` (`"human"` |
|
|
15
|
+
`"timer"`); the human-interrupt path is unchanged.
|
|
16
|
+
- Durable parked execution: `RunStatus.WAITING` + `RunRecord.wake_at`. A run
|
|
17
|
+
parked on `ctx.wait` is `WAITING` in the queue and does not hold a worker;
|
|
18
|
+
`claim` re-takes it once `wake_at` passes (symmetric to lease expiry), in both
|
|
19
|
+
`MemoryRunQueue` and `PostgresRunQueue`. The worker frees itself on a timer
|
|
20
|
+
suspend and resumes on re-claim.
|
|
21
|
+
- `CompiledGraph.run_until_done(...)` drives timer waits inline (sleeps until
|
|
22
|
+
`wake_at` and resumes), for running a watch loop without the control plane —
|
|
23
|
+
the process must stay alive for the wait, unlike the parked control-plane
|
|
24
|
+
path.
|
|
25
|
+
- `agentflow.prebuilt.watch`: `WatchResult` (idle/activity/terminal), the
|
|
26
|
+
`Watcher` protocol, `CommandWatcher` (poll any command that prints the
|
|
27
|
+
contract JSON), `watch_node` (polls, parks on idle, loops until non-idle), and
|
|
28
|
+
`route_watch`. GitHub/CI/etc. are examples over `CommandWatcher`, not core.
|
|
29
|
+
See `examples/watch_until_done.py`.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- First-use schema creation on PostgreSQL is now concurrency-safe. `CREATE TABLE
|
|
34
|
+
IF NOT EXISTS` can collide on `pg_type` when two sessions bootstrap the same
|
|
35
|
+
table at once; the Postgres backends now run that DDL under a
|
|
36
|
+
`pg_advisory_xact_lock` (plus an in-process lock), so a pool of workers
|
|
37
|
+
starting together no longer races.
|
|
38
|
+
|
|
39
|
+
### Changed
|
|
40
|
+
|
|
41
|
+
- Internal: the Postgres backends (`PostgresStore`, `PostgresCheckpointer`,
|
|
42
|
+
`PostgresRunQueue`) now have a real-server contract test suite, run in CI
|
|
43
|
+
against a `postgres` service container. The tests are marked `pg` and skipped
|
|
44
|
+
by the default hermetic run (they need `POSTGRES_TEST_DSN`); they cover the
|
|
45
|
+
SQL-only paths the in-memory suites can't — the `if_absent` conditional write
|
|
46
|
+
and `StoreConflict`, the `if_revision` compare-and-set and `CheckpointConflict`,
|
|
47
|
+
and concurrent `claim` under `FOR UPDATE SKIP LOCKED`.
|
|
48
|
+
|
|
7
49
|
## [0.2.0] - 2026-10-01
|
|
8
50
|
|
|
9
51
|
### Added
|
|
@@ -99,8 +141,8 @@ project aims to follow [Semantic Versioning](https://semver.org/).
|
|
|
99
141
|
|
|
100
142
|
## [0.1.0] - 2026-09-29
|
|
101
143
|
|
|
102
|
-
First public release: an async-first
|
|
103
|
-
|
|
144
|
+
First public release: an async-first SDK for agent workflows with pluggable
|
|
145
|
+
agent and LLM backends.
|
|
104
146
|
|
|
105
147
|
### Added
|
|
106
148
|
|
|
@@ -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.3.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
|
|
@@ -45,69 +45,137 @@ Description-Content-Type: text/markdown
|
|
|
45
45
|
|
|
46
46
|
# agent-workflow-sdk
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
(
|
|
52
|
-
|
|
48
|
+
[](https://pypi.org/project/agent-workflow-sdk/)
|
|
49
|
+
[](https://pypi.org/project/agent-workflow-sdk/)
|
|
50
|
+
[](https://github.com/dankosorkin/agent-workflow-sdk/actions/workflows/ci.yml)
|
|
51
|
+
[](LICENSE)
|
|
52
|
+
[](https://dankosorkin.github.io/agent-workflow-sdk/)
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
An async-first SDK for building agent workflows from composable pieces. You
|
|
55
|
+
describe a workflow as a graph of nodes over a typed, reducer-based state, and
|
|
56
|
+
run it against a pluggable backend — a local coding agent (Claude Code, Codex,
|
|
57
|
+
or Kiro) or a plain LLM (Ollama, any OpenAI-compatible endpoint, or Anthropic).
|
|
56
58
|
|
|
57
|
-
|
|
59
|
+
The import root is `agentflow`. The distribution name is `agent-workflow-sdk`.
|
|
58
60
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
## Who it's for
|
|
62
|
+
|
|
63
|
+
You drive a **local coding-agent CLI** — Claude Code, Codex, or Kiro — and you
|
|
64
|
+
want to orchestrate it with real control flow: branches, loops, retries, a
|
|
65
|
+
human approving a step, a run that survives a crash and resumes. You get that
|
|
66
|
+
without wiring up an API key, because the agent runs through its own CLI and
|
|
67
|
+
its own auth. (An API key path exists too — point a node at `OpenAIBackend` or
|
|
68
|
+
`AnthropicBackend` when you want one — but it's an option, not a requirement.)
|
|
69
|
+
|
|
70
|
+
It is a library, not a coding agent out of the box. If a ready-made agent CLI
|
|
71
|
+
already does what you need, use that. Reach for this when you need to build the
|
|
72
|
+
workflow *around* the agent and keep that logic independent of which backend
|
|
73
|
+
runs underneath.
|
|
74
|
+
|
|
75
|
+
- Build any workflow from primitives: nodes, edges, conditional routing, and a
|
|
76
|
+
shared state. Not a fixed loop, not a linear chain.
|
|
61
77
|
- Swap the backend without touching workflow code. Agents and LLMs share one
|
|
62
78
|
event vocabulary.
|
|
63
79
|
- Async everywhere: the engine, backends, checkpointing, and streaming.
|
|
64
80
|
- Durable by default: every super-step is checkpointed, runs resume after a
|
|
65
81
|
crash, and human-in-the-loop interrupts suspend and resume a run.
|
|
66
82
|
|
|
67
|
-
|
|
68
83
|
## Install
|
|
69
84
|
|
|
85
|
+
```bash
|
|
86
|
+
pip install agent-workflow-sdk
|
|
87
|
+
```
|
|
88
|
+
|
|
70
89
|
The core is dependency-free. Backends that need extra libraries are optional
|
|
71
|
-
extras.
|
|
90
|
+
extras — e.g. `pip install 'agent-workflow-sdk[ollama]'` for the HTTP LLM
|
|
91
|
+
backends. Python 3.11+ is required.
|
|
92
|
+
|
|
93
|
+
The local-agent backends need their CLI on PATH: `claude` for Claude Code,
|
|
94
|
+
`codex` for Codex, `kiro-cli` for Kiro. No API key is needed — each CLI uses
|
|
95
|
+
its own login.
|
|
96
|
+
|
|
97
|
+
To work on the SDK itself, clone and install editable:
|
|
72
98
|
|
|
73
99
|
```bash
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
pip install -e '.[dev]'
|
|
100
|
+
git clone https://github.com/dankosorkin/agent-workflow-sdk
|
|
101
|
+
cd agent-workflow-sdk
|
|
102
|
+
pip install -e '.[dev]'
|
|
77
103
|
```
|
|
78
104
|
|
|
79
|
-
|
|
105
|
+
## Quickstart: a local agent, gated by a human
|
|
80
106
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
107
|
+
A two-step workflow that puts the SDK's point on one screen. A **Claude Code**
|
|
108
|
+
node does the work in its own session; then the run **pauses for a human** to
|
|
109
|
+
approve before anything proceeds. Because every step is checkpointed, the
|
|
110
|
+
pause survives a process restart — you can approve now, tomorrow, or from a
|
|
111
|
+
different process, and the run resumes exactly where it stopped. No API key:
|
|
112
|
+
Claude Code runs through its own CLI login.
|
|
84
113
|
|
|
85
114
|
```python
|
|
86
115
|
import asyncio
|
|
87
116
|
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
|
-
|
|
117
|
+
from agentflow import Graph, START, END, State, FileCheckpointer, last, append
|
|
118
|
+
from agentflow.backends.claude_code import ClaudeCodeBackend
|
|
119
|
+
from agentflow.backends.base import DenyAll
|
|
120
|
+
from agentflow.events import TextChunk, TurnEnd
|
|
121
|
+
|
|
122
|
+
class ReviewState(State):
|
|
123
|
+
target: Annotated[str, last]
|
|
124
|
+
proposal: Annotated[str, last]
|
|
125
|
+
approved: Annotated[bool, last]
|
|
126
|
+
log: Annotated[list, append]
|
|
127
|
+
|
|
128
|
+
claude = ClaudeCodeBackend(model="sonnet", permission=DenyAll()) # read-only
|
|
129
|
+
|
|
130
|
+
async def propose(state, ctx):
|
|
131
|
+
text = []
|
|
132
|
+
async for ev in claude.prompt(
|
|
133
|
+
f"Review this repo and propose ONE concrete improvement to {state['target']}. "
|
|
134
|
+
"Describe the change; do not apply it."
|
|
135
|
+
):
|
|
136
|
+
if isinstance(ev, TextChunk):
|
|
137
|
+
ctx.emit(ev) # stream tokens to the caller as they arrive
|
|
138
|
+
text.append(ev.text)
|
|
139
|
+
elif isinstance(ev, TurnEnd):
|
|
140
|
+
text = [ev.text] if ev.text else text
|
|
141
|
+
return {"proposal": "".join(text)}
|
|
142
|
+
|
|
143
|
+
async def gate(state, ctx):
|
|
144
|
+
# Suspend the whole run until a human answers. On a fresh run this stops
|
|
145
|
+
# here and checkpoints; on resume, interrupt() returns the human's value.
|
|
146
|
+
decision = await ctx.interrupt({"review": state["proposal"]})
|
|
147
|
+
return {"approved": decision == "approve", "log": f"human said: {decision}"}
|
|
148
|
+
|
|
149
|
+
g = Graph(ReviewState)
|
|
150
|
+
g.add_node("propose", propose)
|
|
151
|
+
g.add_node("gate", gate)
|
|
152
|
+
g.add_edge(START, "propose")
|
|
153
|
+
g.add_edge("propose", "gate")
|
|
154
|
+
g.add_edge("gate", END)
|
|
155
|
+
|
|
156
|
+
async def main():
|
|
157
|
+
app = g.compile(checkpointer=FileCheckpointer(".runs")) # durable
|
|
158
|
+
async with claude:
|
|
159
|
+
state = await app.invoke({"target": "the README"}, thread="demo")
|
|
160
|
+
# The run is now parked at the gate. Show the proposal, get a decision:
|
|
161
|
+
cp = await app.get_state("demo")
|
|
162
|
+
print(cp.interrupt_payload["review"]) # Claude's proposal
|
|
163
|
+
final = await app.resume("demo", value="approve")
|
|
164
|
+
print(final["approved"], final["log"])
|
|
165
|
+
|
|
166
|
+
asyncio.run(main())
|
|
107
167
|
```
|
|
108
168
|
|
|
109
|
-
|
|
110
|
-
|
|
169
|
+
That is the whole value in one example: a real local agent does the work, the
|
|
170
|
+
graph owns the control flow, a human gates the result, and durability means
|
|
171
|
+
the pause is not tied to this process staying alive. Swap `ClaudeCodeBackend`
|
|
172
|
+
for `CodexBackend`, `KiroBackend`, or an LLM backend and the workflow code
|
|
173
|
+
above does not change.
|
|
174
|
+
|
|
175
|
+
Prefer to learn the engine first, with no backend and nothing to install
|
|
176
|
+
beyond the core? `examples/hello_graph.py` is a minimal counter graph that
|
|
177
|
+
shows state, reducers, and edges on their own; `examples/optimize_loop.py`
|
|
178
|
+
shows the iterate-until-converged loop.
|
|
111
179
|
|
|
112
180
|
## Core concepts
|
|
113
181
|
|