agentflowkit 0.5.0__tar.gz → 0.6.1__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.
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/.github/workflows/ci.yml +1 -1
- agentflowkit-0.6.1/.gitignore +33 -0
- agentflowkit-0.6.1/CHANGELOG.md +240 -0
- agentflowkit-0.6.1/LEVEL_UP.md +314 -0
- agentflowkit-0.5.0/README.md → agentflowkit-0.6.1/PKG-INFO +502 -388
- agentflowkit-0.6.1/PUBLIC_API.md +78 -0
- agentflowkit-0.5.0/PKG-INFO → agentflowkit-0.6.1/README.md +454 -430
- agentflowkit-0.6.1/SECURITY.md +30 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/docs/index.md +4 -4
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/examples/cpp_build_pipeline.py +14 -31
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/examples/drone_telemetry_agent.py +2 -1
- agentflowkit-0.6.1/examples/earnings_triage.py +183 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/examples/market_analysis_crew.py +9 -5
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/examples/notebooks/parallel_execution_demo.ipynb +306 -292
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/examples/notebooks/quickstart.ipynb +18 -1
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/examples/research_react_agent.py +3 -4
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/examples/tool_agent.py +29 -2
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/pyproject.toml +8 -6
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/src/agentflow/__init__.py +25 -25
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/src/agentflow/agent.py +157 -48
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/src/agentflow/cache.py +24 -4
- agentflowkit-0.6.1/src/agentflow/contrib/__init__.py +1 -0
- agentflowkit-0.6.1/src/agentflow/contrib/otel.py +102 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/src/agentflow/distillation.py +1 -1
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/src/agentflow/events.py +8 -3
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/src/agentflow/exceptions.py +16 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/src/agentflow/llm.py +30 -23
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/src/agentflow/pipeline.py +288 -273
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/src/agentflow/rate_limiter.py +17 -23
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/src/agentflow/swarm.py +41 -28
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/src/agentflow/swarm_routing.py +6 -6
- agentflowkit-0.6.1/src/agentflow/types.py +136 -0
- agentflowkit-0.6.1/tests/test_agent.py +114 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/tests/test_cache.py +19 -7
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/tests/test_conditional.py +1 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/tests/test_hitl.py +99 -12
- agentflowkit-0.6.1/tests/test_llm.py +53 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/tests/test_long_term_memory.py +1 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/tests/test_memory.py +19 -17
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/tests/test_observability.py +1 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/tests/test_parallel.py +1 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/tests/test_pipeline.py +78 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/tests/test_rate_limiter.py +1 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/tests/test_sandbox.py +24 -24
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/tests/test_streaming.py +2 -1
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/tests/test_swarm.py +61 -17
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/tests/test_swarm_routing.py +14 -13
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/tests/test_tools.py +14 -13
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/tests/test_triggers.py +7 -2
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/uv.lock +8 -2
- agentflowkit-0.5.0/.coverage +0 -0
- agentflowkit-0.5.0/.gitignore +0 -21
- agentflowkit-0.5.0/AUDIT_REPORT.md +0 -137
- agentflowkit-0.5.0/CHANGELOG.md +0 -111
- agentflowkit-0.5.0/CMakeLists.txt +0 -30
- agentflowkit-0.5.0/PASS2_RESOLUTION_REPORT.md +0 -261
- agentflowkit-0.5.0/src/agentflow/cpp_core/bindings.cpp +0 -36
- agentflowkit-0.5.0/src/agentflow/cpp_core/dag_engine.cpp +0 -105
- agentflowkit-0.5.0/src/agentflow/cpp_core/dag_engine.h +0 -22
- agentflowkit-0.5.0/src/agentflow/types.py +0 -68
- agentflowkit-0.5.0/tests/test_agent.py +0 -54
- agentflowkit-0.5.0/tests/test_llm.py +0 -31
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/.gitattributes +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/.github/workflows/docs.yml +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/.github/workflows/publish.yml +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/CONTRIBUTING.md +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/LICENSE +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/benchmarks/parallel_speedup.py +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/docs/getting-started.md +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/docs/guides/cost-streaming.md +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/docs/guides/memory.md +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/docs/guides/observability.md +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/docs/guides/tools.md +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/docs/reference.md +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/examples/code_reviewer.py +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/examples/memory_chat_agents.py +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/examples/research_crew.py +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/examples/robotics_mqtt_agent.py +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/examples/streaming_and_cost.py +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/mkdocs.yml +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/src/agentflow/hitl.py +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/src/agentflow/logging.py +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/src/agentflow/memory.py +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/src/agentflow/observability.py +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/src/agentflow/pricing.py +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/src/agentflow/py.typed +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/src/agentflow/sandbox.py +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/src/agentflow/tools.py +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/src/agentflow/triggers.py +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/tests/test_logging.py +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/tests/test_pricing.py +0 -0
- {agentflowkit-0.5.0 → agentflowkit-0.6.1}/tests/test_retry.py +0 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.pyc
|
|
3
|
+
*.pyo
|
|
4
|
+
*.egg-info/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
.venv/
|
|
8
|
+
venv/
|
|
9
|
+
.env
|
|
10
|
+
.pytest_cache/
|
|
11
|
+
.mypy_cache/
|
|
12
|
+
.ruff_cache/
|
|
13
|
+
.coverage
|
|
14
|
+
*.egg
|
|
15
|
+
# AI assistants / agents
|
|
16
|
+
.claude/
|
|
17
|
+
CLAUDE.md
|
|
18
|
+
.cursor/
|
|
19
|
+
.cursorrules
|
|
20
|
+
.aider*
|
|
21
|
+
.github/copilot-instructions.md
|
|
22
|
+
AGENTS.md
|
|
23
|
+
chat.md
|
|
24
|
+
tmpclaude-*
|
|
25
|
+
# Session/report scaffolding — should never be committed
|
|
26
|
+
*_REPORT.md
|
|
27
|
+
PASS[0-9]*_*.md
|
|
28
|
+
commit_msg.txt
|
|
29
|
+
# Hackathon demo app — lives in its own repo, not in the library
|
|
30
|
+
nova_bridge/
|
|
31
|
+
run_nova_bridge.py
|
|
32
|
+
# Multi-agent orchestration workspace — untracked local state, never a package file
|
|
33
|
+
.bridgespace/
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented here.
|
|
4
|
+
|
|
5
|
+
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
6
|
+
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## [0.6.1] — 2026-07-16
|
|
11
|
+
|
|
12
|
+
Packaging-hygiene release. **0.6.0 has been yanked from PyPI.** Its source
|
|
13
|
+
distribution (`.tar.gz`) accidentally bundled a local, untracked
|
|
14
|
+
`.bridgespace/` agent-workspace directory whose contents included a leaked
|
|
15
|
+
PyPI API token (since revoked by PyPI). The Python package code was
|
|
16
|
+
identical and unaffected; only the sdist contained the stray directory.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
- **Security/packaging**: `.bridgespace/` (a local multi-agent workspace, never
|
|
20
|
+
part of the library) was not excluded from the build and leaked into the
|
|
21
|
+
0.6.0 sdist. It is now git-ignored and excluded from all distributions. The
|
|
22
|
+
wheel was never affected. If you installed 0.6.0, upgrade to 0.6.1.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## [0.6.0] — 2026-07-16
|
|
27
|
+
|
|
28
|
+
First release shipping a pure-Python `py3-none-any` wheel. The only wheel
|
|
29
|
+
published for 0.5.0 was a Windows/CPython-3.11 binary left over from the
|
|
30
|
+
(since removed) C++ extension era — every other platform fell back to an
|
|
31
|
+
sdist build. If 0.4.0/0.5.0 failed to install for you, this release is the fix.
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
- **Typed data plane**: `LLM.generate()` returns a typed `LLMResponse` pydantic
|
|
35
|
+
model. Dict-style access is kept as a deprecated shim (see Deprecated).
|
|
36
|
+
- **Validated output flows downstream**: when an agent declares
|
|
37
|
+
`output_schema`, its validated output (`AgentResult.data`) is what downstream
|
|
38
|
+
agents receive in `context` — not the raw JSON string.
|
|
39
|
+
- **Cost budgets**: `Pipeline(budget_usd=0.25)` enforces a hard USD ceiling per
|
|
40
|
+
run, raising `BudgetExceededError` when exceeded (checked after each level).
|
|
41
|
+
- **OpenTelemetry adapter**: `agentflow.contrib.otel.OTelHooks` (install the
|
|
42
|
+
`otel` extra) — one import to spans.
|
|
43
|
+
- **`AgentSpec`** is the public name of the object `@Agent` returns (previously
|
|
44
|
+
the private `_DecoratorAgent`); `@Agent(system_prompt=...)` fully replaces
|
|
45
|
+
the default role-based system prompt.
|
|
46
|
+
- **`wall_time`** (elapsed) and **`agent_seconds`** (summed agent time) on
|
|
47
|
+
`PipelineResult`.
|
|
48
|
+
- **`PUBLIC_API.md`** stability contract and **`SECURITY.md`**.
|
|
49
|
+
- Showcase example `examples/earnings_triage.py`: diamond DAG with tools,
|
|
50
|
+
parallel analysts, typed output, budget, and cache — zero-key via Ollama/Groq.
|
|
51
|
+
- CI test matrix extended to Python 3.13 and 3.14.
|
|
52
|
+
|
|
53
|
+
### Fixed
|
|
54
|
+
- `SupervisorAgent` crashed with `TypeError` when an upstream context value was
|
|
55
|
+
a validated-output dict.
|
|
56
|
+
- `SupervisorAgent` billing race: concurrent runs sharing one instance
|
|
57
|
+
corrupted each other's worker token/cost accounting (now a run-scoped ledger).
|
|
58
|
+
- A real agent failure alongside an HITL pause in the same DAG level was
|
|
59
|
+
silently swallowed; errors now take precedence and raise (the discarded
|
|
60
|
+
pause is logged).
|
|
61
|
+
- Resumed agents dropped their validated `output_schema` result;
|
|
62
|
+
`AgentResult.data` is now populated on the resume path too.
|
|
63
|
+
- `Pipeline.serve()` silently discarded the trigger's `context_data`; it is now
|
|
64
|
+
appended to the task prompt as a JSON block.
|
|
65
|
+
- `Pipeline.resume()` could raise a spurious `ValidationError` when the last
|
|
66
|
+
saved context value was a dict.
|
|
67
|
+
- Dependency-cycle errors now name the agents involved.
|
|
68
|
+
|
|
69
|
+
### Changed
|
|
70
|
+
- `run()`, `resume()`, and `stream()` now share a single execution driver
|
|
71
|
+
(previously three divergent copies of the same loop — the source of the
|
|
72
|
+
pause/error bugs above). `resume()` and `stream()` fire the same
|
|
73
|
+
`on_agent_start`/`on_agent_end`/`on_agent_error` hooks as `run()`.
|
|
74
|
+
- `stream()` persists HITL pause state under a real `run_id` distinct from the
|
|
75
|
+
session id.
|
|
76
|
+
- `Event.type` and `EventEmitter.emit` are typed with the new `EventType`
|
|
77
|
+
Literal (now includes `pipeline_paused`); `PipelineResult.status` is
|
|
78
|
+
`Literal["completed", "paused"]`.
|
|
79
|
+
- `__version__` is single-sourced from package metadata (`importlib.metadata`).
|
|
80
|
+
- Sandbox and trigger names are no longer re-exported from the top-level
|
|
81
|
+
package — import via `agentflow.sandbox` / `agentflow.triggers` (best-effort
|
|
82
|
+
modules outside the semver contract).
|
|
83
|
+
- The C++ DAG engine and its build scaffolding were removed entirely: DAG
|
|
84
|
+
resolution is microseconds against multi-second LLM calls.
|
|
85
|
+
|
|
86
|
+
### Deprecated (warn since 0.6, removal at 1.0)
|
|
87
|
+
- Dict-style access on `LLMResponse` (`response["content"]`, `.get(...)`) —
|
|
88
|
+
use attribute access.
|
|
89
|
+
- `set_session()` / `set_approval_policy()` — pass `session_id=` /
|
|
90
|
+
`approval_policy=` to `execute()`; mutating shared agent instances is unsafe
|
|
91
|
+
under concurrency.
|
|
92
|
+
- `PipelineResult.total_duration` — use `agent_seconds` or `wall_time`.
|
|
93
|
+
- The `_DecoratorAgent` name — use `AgentSpec`.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## [0.5.0] — 2026-07-03 (retroactive entry)
|
|
98
|
+
|
|
99
|
+
Written after the fact on 2026-07-16: 0.4.0 and 0.5.0 shipped without
|
|
100
|
+
changelog entries, violating this file's own policy. Reconstructed from git
|
|
101
|
+
history. **Packaging note:** the only 0.5.0 wheel on PyPI is
|
|
102
|
+
`cp311-cp311-win_amd64`; every other platform builds from the sdist. Prefer 0.6.0.
|
|
103
|
+
|
|
104
|
+
### Added
|
|
105
|
+
- **Swarm routing**: `SupervisorAgent` delegates sub-tasks to worker agents via
|
|
106
|
+
a generated `delegate_task` tool; worker tokens/cost bubble up to the
|
|
107
|
+
supervisor's result. Unexported `swarm_routing.DynamicSupervisorAgent`
|
|
108
|
+
prototype with depth-capped dynamic agent creation.
|
|
109
|
+
- **Background memory distillation** (`agentflow.distillation`): compresses
|
|
110
|
+
long session memory with a version lock against concurrent writes.
|
|
111
|
+
- **Human-in-the-loop**: `ApprovalPolicy` + `PauseExecution` +
|
|
112
|
+
`Pipeline.resume()` — pause on blocked tool calls, persist state to memory,
|
|
113
|
+
resume with approve/reject.
|
|
114
|
+
- **Sandboxes** (`agentflow.sandbox`): Docker/subprocess code-execution tools.
|
|
115
|
+
- **MQTT triggers** (`agentflow.triggers`) and `Pipeline.serve()` daemon mode.
|
|
116
|
+
|
|
117
|
+
### Changed
|
|
118
|
+
- The C++ DAG engine introduced in 0.4.0 was made optional (skipped when no
|
|
119
|
+
compiler is available) — and removed entirely in 0.6.0.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## [0.4.0] — 2026-07-02 (retroactive entry)
|
|
124
|
+
|
|
125
|
+
### Added
|
|
126
|
+
- C++ DAG engine via pybind11 (Kahn's algorithm) — later judged unnecessary
|
|
127
|
+
and removed; see the 0.5.0/0.6.0 notes.
|
|
128
|
+
- Memory module (`BaseMemory`, `InMemoryContext`, `RedisContext`,
|
|
129
|
+
`VectorContext`) with per-session context injection into agent prompts.
|
|
130
|
+
- ReAct loop hardening: duplicate-call detection, tool-output truncation,
|
|
131
|
+
sliding message window, per-iteration LLM retry.
|
|
132
|
+
|
|
133
|
+
### Note
|
|
134
|
+
- 0.2.0 and 0.3.0 were developed and documented below but never published to
|
|
135
|
+
PyPI, which jumped 0.1.0 → 0.4.0.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## [0.3.0]
|
|
140
|
+
|
|
141
|
+
### Added
|
|
142
|
+
- **Tool / function calling** (`tools.py`): the `@tool` decorator turns any sync
|
|
143
|
+
or async Python function into an LLM-callable tool. Argument JSON schemas are
|
|
144
|
+
generated automatically from type hints via Pydantic. Agents given `tools=[...]`
|
|
145
|
+
run a bounded **ReAct loop** (call → execute tools → observe → repeat) up to
|
|
146
|
+
`max_tool_iterations` (default 6). Tool errors are fed back to the model for
|
|
147
|
+
recovery. New `ToolError` exception; tool-call traces recorded in
|
|
148
|
+
`AgentResult.metadata["tool_calls"]`.
|
|
149
|
+
- **Cost tracking** (`pricing.py`): built-in USD price tables for common OpenAI
|
|
150
|
+
and Anthropic models with longest-prefix matching. `LLM.generate()` returns a
|
|
151
|
+
`cost` (and `prompt_tokens`/`completion_tokens`); `AgentResult.cost` and
|
|
152
|
+
`PipelineResult.total_cost` aggregate spend. Cache hits bill `0.0`.
|
|
153
|
+
`register_price()` / `estimate_cost()` are public.
|
|
154
|
+
- **Token streaming**: `LLM.astream()` yields content deltas token-by-token for
|
|
155
|
+
interactive UIs (honours the rate limiter; no cache/retry mid-stream).
|
|
156
|
+
- **Observability hooks** (`observability.py`): `Hooks` base class + `LoggingHooks`
|
|
157
|
+
wire the previously-unused `PipelineLogger` into `Pipeline.run()` (which was
|
|
158
|
+
silent before). A raising hook is caught and warned, never crashing the run.
|
|
159
|
+
- **Production-grade retry**: unified exponential backoff with jitter and
|
|
160
|
+
`Retry-After` header support. New `LLM(retry_base_delay=, retry_jitter=)` args.
|
|
161
|
+
- **Documentation site**: MkDocs Material + mkdocstrings under `docs/`, deployed
|
|
162
|
+
via a new `docs.yml` workflow. New `docs` optional-dependency group.
|
|
163
|
+
- New examples: `tool_agent.py`, `streaming_and_cost.py`; and
|
|
164
|
+
`benchmarks/parallel_speedup.py`.
|
|
165
|
+
|
|
166
|
+
### Fixed
|
|
167
|
+
- **`py.typed` marker** added — the package advertised `Typing :: Typed` but
|
|
168
|
+
shipped no marker, so downstream type-checkers saw no types.
|
|
169
|
+
- **Red CI made green**: resolved 1 `ruff` error (B904) and 7 `mypy --strict`
|
|
170
|
+
errors across `llm.py`, `cache.py`, `logging.py`, `events.py`, `pipeline.py`.
|
|
171
|
+
|
|
172
|
+
### Changed
|
|
173
|
+
- `__version__` bumped to `0.3.0`.
|
|
174
|
+
- Test coverage raised to ~91%; `fail_under` tightened from 80 → 90.
|
|
175
|
+
- `Pipeline.__init__` gains a `hooks` parameter; `run()` now emits lifecycle
|
|
176
|
+
events and generates `run_id` up front.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## [0.2.0]
|
|
181
|
+
|
|
182
|
+
### Added
|
|
183
|
+
- **Parallel execution**: Agents at the same DAG level now run concurrently via
|
|
184
|
+
`asyncio.gather()`. `_resolve_levels()` replaces `_resolve_order()` and uses
|
|
185
|
+
Kahn's algorithm to group independent agents.
|
|
186
|
+
- **Per-agent timeout**: `Pipeline.add(timeout=N)` wraps each agent coroutine with
|
|
187
|
+
`asyncio.wait_for`; raises `AgentTimeoutError` on expiry.
|
|
188
|
+
- **Conditional branching**: `Pipeline.add(condition=lambda ctx: ...)` allows
|
|
189
|
+
dynamic skipping of agents based on upstream outputs. Skipped agents emit
|
|
190
|
+
`agent_skipped` events in streaming mode.
|
|
191
|
+
- **Pipeline-level retry**: `Pipeline(retry_failed_agents=N)` retries failed
|
|
192
|
+
agents up to N times with exponential backoff (1s, 2s, 4s). Timeouts are
|
|
193
|
+
non-retriable.
|
|
194
|
+
- **LLM response caching** (`cache.py`): `ResponseCache` ABC with `InMemoryCache`
|
|
195
|
+
(SHA-256 key, lazy TTL eviction, max-size LRU) and `RedisCache` (optional dep).
|
|
196
|
+
Cache is wired into `LLM(cache=...)`.
|
|
197
|
+
- **Rate limiting** (`rate_limiter.py`): `RateLimiter(requests_per_minute, max_concurrent)`
|
|
198
|
+
using `asyncio.Semaphore` + sliding-window counter. Async context manager interface.
|
|
199
|
+
Wired into `LLM(rate_limiter=...)`.
|
|
200
|
+
- **Structured logging** (`logging.py`): `PipelineLogger` (LoggerAdapter with JSON
|
|
201
|
+
formatter) carrying `run_id` and `pipeline` through all log records.
|
|
202
|
+
- **Agent output validation**: `@Agent(output_schema=MyPydanticModel)` validates
|
|
203
|
+
LLM response JSON against a Pydantic v2 model; raises `AgentOutputValidationError`
|
|
204
|
+
on failure.
|
|
205
|
+
- New exception classes: `AgentTimeoutError`, `AgentOutputValidationError`.
|
|
206
|
+
- `AgentResult` gains: `cached`, `level`, `timestamp` fields.
|
|
207
|
+
- `PipelineResult` gains: `run_id`, `levels_executed`, `agents_with_cache_hits` fields.
|
|
208
|
+
- `Event.type` now documents all valid values including `"agent_skipped"`.
|
|
209
|
+
- GitHub Actions CI workflow (lint, test matrix py3.10-3.12, codecov, build check).
|
|
210
|
+
- GitHub Actions publish workflow (OIDC trusted publishing on version tags).
|
|
211
|
+
- Issue templates for bug reports and feature requests.
|
|
212
|
+
- `CONTRIBUTING.md` with development setup and PR checklist.
|
|
213
|
+
- `pythonpath = ["src"]` in pytest config to fix editable install on non-ASCII paths.
|
|
214
|
+
|
|
215
|
+
### Changed
|
|
216
|
+
- `__version__` bumped to `0.2.0`.
|
|
217
|
+
- `pipeline.py`: `Pipeline.__init__` gains `retry_failed_agents` parameter.
|
|
218
|
+
- `pipeline.py`: `Pipeline.add` gains `timeout` and `condition` parameters.
|
|
219
|
+
- `llm.py`: `LLM.__init__` gains `cache` and `rate_limiter` parameters.
|
|
220
|
+
Return dict from `generate()` now includes `"cached"` key.
|
|
221
|
+
- `pyproject.toml`: classifier updated to Beta; dev extras expanded;
|
|
222
|
+
`ruff`, `mypy`, `coverage` tool config sections added.
|
|
223
|
+
|
|
224
|
+
### Performance
|
|
225
|
+
- Two independent agents that each take 0.1s now complete in ~0.1s (parallel),
|
|
226
|
+
not ~0.2s (sequential).
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## [0.1.0] — 2026-02-27
|
|
231
|
+
|
|
232
|
+
### Added
|
|
233
|
+
- Initial release.
|
|
234
|
+
- `@Agent` decorator and `BaseAgent` ABC for defining agents.
|
|
235
|
+
- `Pipeline` with topological sort (`_resolve_order`) for dependency resolution.
|
|
236
|
+
- `LLM` provider abstraction with OpenAI-compatible API, retry logic.
|
|
237
|
+
- `EventEmitter` + `pipeline.stream()` for async event streaming.
|
|
238
|
+
- Pydantic v2 data models: `AgentResult`, `PipelineResult`, `Event`.
|
|
239
|
+
- Custom exception hierarchy: `AgentFlowError`, `AgentError`, `LLMError`, `PipelineError`.
|
|
240
|
+
- Published to PyPI as `agentflowkit`.
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
# agentflow — Level-Up Analysis
|
|
2
|
+
|
|
3
|
+
Reviewer stance: skeptical maintainer deciding whether a real team should depend on this.
|
|
4
|
+
Ordered by impact-per-effort. Verified against the code at v0.5.0 (working tree), not the README's claims.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## The 3 things that matter most
|
|
9
|
+
|
|
10
|
+
**1. The library has an identity crisis, and it's visible from the outside.**
|
|
11
|
+
The README says *"agentflow is a deliberately narrow library, not a framework"* — while
|
|
12
|
+
`__init__.py` exports `DockerSandbox`, `SubprocessSandbox`, `MQTTTrigger`, `SupervisorAgent`,
|
|
13
|
+
`VectorContext`, and an HITL pause/resume state machine. Git history shows a C++ DAG engine
|
|
14
|
+
added in one release ("pillar1") and deleted in the next, plus `swarm_routing.py` and
|
|
15
|
+
`distillation.py` that aren't even exported. A reviewer evaluating this for production reads
|
|
16
|
+
that history and concludes: *the maintainer doesn't know what this is yet, so I can't depend
|
|
17
|
+
on it.* Nothing else in this document matters until the scope is actually narrow, not
|
|
18
|
+
narrated as narrow.
|
|
19
|
+
|
|
20
|
+
**2. The string-only data plane is the one hard-to-reverse API mistake, and it's still reversible today.**
|
|
21
|
+
Agents communicate through `context: dict[str, str]`. `Pipeline.run()` takes a bare `str`.
|
|
22
|
+
`LLM.generate()` returns an untyped `dict[str, Any]`. Structured output (`output_schema`)
|
|
23
|
+
is validated and then **buried in `metadata["validated_output"]`** while downstream agents
|
|
24
|
+
receive the raw JSON string. For a library whose headline trust signal is "fully typed,
|
|
25
|
+
`mypy --strict`", the actual data flowing between agents is untyped strings. PydanticAI's
|
|
26
|
+
entire pitch is typed agent I/O — this is the exact axis you'll be compared on. Fix it
|
|
27
|
+
before 1.0 or never.
|
|
28
|
+
|
|
29
|
+
**3. Trust hygiene is broken in ways that take an hour to fix.**
|
|
30
|
+
`src/agentflow/__init__.py` says `__version__ = "0.3.0"`; `pyproject.toml` says `0.5.0`.
|
|
31
|
+
The import name is `agentflow` but the pip name is `agentflowkit` — every new user's first
|
|
32
|
+
five minutes includes "why doesn't `pip install agentflow` work". Commit messages like
|
|
33
|
+
"massive architecture upgrade" between minor versions signal churn. These are trivial fixes
|
|
34
|
+
that currently scream *hobby project* to anyone doing due diligence.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Analysis 1 — Positioning & the "why not X" test
|
|
39
|
+
|
|
40
|
+
**The honest niche:** agentflow is a *readable, two-dependency, cost-aware parallel DAG
|
|
41
|
+
runner for OpenAI-compatible endpoints*. Its defensible claim is auditability: the core
|
|
42
|
+
(pipeline + agent + llm + tools + types, ~1,700 lines) can be read in an afternoon,
|
|
43
|
+
type-checks under `mypy --strict`, and has retries, timeouts, caching, cost, and events
|
|
44
|
+
built in rather than as plugins. That is a real niche — teams burned by LangChain's
|
|
45
|
+
dependency graph, running on Groq/Ollama/vLLM, who would otherwise hand-roll
|
|
46
|
+
`asyncio.gather` plus retry/cost boilerplate.
|
|
47
|
+
|
|
48
|
+
**The single sharpest wedge:** **cost-aware parallel orchestration on a core you can audit.**
|
|
49
|
+
Nobody else owns "every run tells you what it cost, per agent, with cache hits billing $0,
|
|
50
|
+
in a library small enough to read before deploying." LangGraph won't be small. CrewAI won't
|
|
51
|
+
be rigorous. PydanticAI won't center DAG parallelism. Own this one thing; everything else
|
|
52
|
+
(swarm, HITL, sandbox, MQTT) is secondary or actively dilutive.
|
|
53
|
+
|
|
54
|
+
**Who should NOT use agentflow (say this in the README):**
|
|
55
|
+
- Anyone already inside LangChain/LlamaIndex — the ecosystem gravity isn't worth fighting.
|
|
56
|
+
- Anyone needing durable workflows that survive process death (the HITL resume is
|
|
57
|
+
memory-backed state, not a workflow engine — don't pretend otherwise).
|
|
58
|
+
- Anyone needing native (non-OpenAI-compatible) SDKs: Bedrock, Vertex native.
|
|
59
|
+
- Anyone wanting RAG batteries: loaders, splitters, vector-store integrations.
|
|
60
|
+
- Anyone whose graph is dynamic per-run (agentflow's DAG is static once built).
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Analysis 2 — API design critique
|
|
65
|
+
|
|
66
|
+
### 2a. Hard to reverse once adopted — fix NOW
|
|
67
|
+
|
|
68
|
+
**The untyped data plane** (the #2 issue above). Concretely:
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
# Before — llm.generate returns a dict users index blindly:
|
|
72
|
+
response = await llm.generate(messages)
|
|
73
|
+
content = response["content"] # typo = runtime KeyError
|
|
74
|
+
cost = response.get("cost", 0.0) # every caller re-invents the default
|
|
75
|
+
|
|
76
|
+
# After — a frozen model; attribute access, mypy-checked:
|
|
77
|
+
class LLMResponse(BaseModel):
|
|
78
|
+
content: str
|
|
79
|
+
tokens: TokenUsage
|
|
80
|
+
cost: float
|
|
81
|
+
model: str
|
|
82
|
+
cached: bool = False
|
|
83
|
+
tool_calls: list[ToolCall] | None = None
|
|
84
|
+
finish_reason: str | None = None
|
|
85
|
+
|
|
86
|
+
response = await llm.generate(messages)
|
|
87
|
+
response.content
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
# Before — structured output validated, then thrown away for downstream agents:
|
|
92
|
+
context["analyst"] # raw JSON string
|
|
93
|
+
result.get("analyst").metadata["validated_output"] # the actual data, buried
|
|
94
|
+
|
|
95
|
+
# After — validated output IS the agent's output downstream:
|
|
96
|
+
@Agent(name="analyst", role="...", output_schema=Report)
|
|
97
|
+
...
|
|
98
|
+
report: Report = context["analyst"] # context: dict[str, Any]
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`context: dict[str, str]` → `dict[str, Any]` is the enabling change. Do it while the user
|
|
102
|
+
count is small; after adoption it's a breaking change to every agent body ever written.
|
|
103
|
+
|
|
104
|
+
**Mutable shared agent state — this is a live bug, not just a smell.**
|
|
105
|
+
`@Agent` produces a module-level singleton. `Pipeline._execute_node()` calls
|
|
106
|
+
`agent.set_session(session_id)` and `agent.set_approval_policy(...)` — mutating that
|
|
107
|
+
singleton. Two pipelines (or two `serve()` requests — `serve` explicitly runs concurrent
|
|
108
|
+
`run()`s) sharing one agent will cross-contaminate sessions: pipeline A's agent reads and
|
|
109
|
+
writes pipeline B's memory. The fix is to stop mutating and pass run-scoped state through
|
|
110
|
+
the call:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
# Before (pipeline.py:172-177) — mutate the shared instance:
|
|
114
|
+
agent.set_session(session_id)
|
|
115
|
+
agent.set_approval_policy(policy)
|
|
116
|
+
result = await agent.execute(task, context, self._llm)
|
|
117
|
+
|
|
118
|
+
# After — run-scoped context travels with the call:
|
|
119
|
+
result = await agent.execute(task, context, RunContext(llm=self._llm,
|
|
120
|
+
session_id=session_id,
|
|
121
|
+
approval_policy=policy))
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
This also fixes `BaseAgent.execute(task, context, llm)`'s signature being frozen at three
|
|
125
|
+
positional params — `RunContext` is the extension point so the signature never breaks again.
|
|
126
|
+
|
|
127
|
+
**`Agent(...)` returning the private `_DecoratorAgent`.** The decorator's return type is a
|
|
128
|
+
private class users hold references to, pass to `Pipeline.add`, and will inevitably
|
|
129
|
+
introspect. Rename it public (`AgentSpec` or fold into `BaseAgent`) before someone
|
|
130
|
+
depends on the underscore name.
|
|
131
|
+
|
|
132
|
+
**`hasattr`-based duck typing** (`hasattr(agent, 'set_session')`,
|
|
133
|
+
`hasattr(node.agent, 'resume_execution')` in pipeline.py) — invisible contracts. A user
|
|
134
|
+
subclassing `BaseAgent` gets silently different behavior depending on which optional
|
|
135
|
+
methods they happened to define. Replace with an explicit `Protocol` or move the
|
|
136
|
+
capability into `BaseAgent` with default implementations.
|
|
137
|
+
|
|
138
|
+
### 2b. Semantics that will surprise users
|
|
139
|
+
|
|
140
|
+
- **`PipelineResult.output` = "last agent's output"** — undefined when the final level has
|
|
141
|
+
multiple parallel agents; it's whichever ran last in list order. Either make it the
|
|
142
|
+
outputs of all sink nodes, or document that it's only meaningful for single-sink DAGs
|
|
143
|
+
and raise/warn otherwise.
|
|
144
|
+
- **`total_duration` sums per-agent durations** — in a *parallelism* library, the headline
|
|
145
|
+
duration metric double-counts parallel time. Rename to `agent_seconds` and add
|
|
146
|
+
`wall_time` measured around the run. This one is embarrassing given the pitch.
|
|
147
|
+
- **`condition` receives the full accumulated context; `execute` receives dep-scoped
|
|
148
|
+
context.** Two different context shapes for the same node. Pick one (dep-scoped is the
|
|
149
|
+
right one) — it changes what user lambdas can see, so it's a breaking change later.
|
|
150
|
+
- **The system prompt is hardcoded**: `f"You are a {role}. Provide clear, thorough,
|
|
151
|
+
well-structured responses."` — unoverridable trailing instruction injected into every
|
|
152
|
+
agent. Add `system_prompt=` to `@Agent`.
|
|
153
|
+
- **Memory injection silently truncates to 300 chars** (agent.py:110) — silent data loss
|
|
154
|
+
with no knob and no log line.
|
|
155
|
+
- **`status: str`** on `PipelineResult` → `Literal["completed", "paused"]`.
|
|
156
|
+
- **`depends_on` forbids forward references** (dep must be added first). Fine as a choice,
|
|
157
|
+
but it forces users to topologically sort their own code; validating at `run()` instead
|
|
158
|
+
would cost nothing.
|
|
159
|
+
|
|
160
|
+
### 2c. What's genuinely good — keep and defend
|
|
161
|
+
Dep-scoped context (agents only see declared dependencies) is a *great* decision most
|
|
162
|
+
competitors don't make. Kahn's-levels parallelism is simple and explainable. The tool loop
|
|
163
|
+
(dedup, truncation, sliding window, concurrent tool calls) is careful. Exceptions carry
|
|
164
|
+
agent names. `stream()` cancels the background task when the consumer abandons the
|
|
165
|
+
generator. This core deserves the audit-friendly pitch — the periphery undermines it.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Analysis 3 — The adoption gap (scored 1–5)
|
|
170
|
+
|
|
171
|
+
| Dimension | Score | Justification | Action to raise it |
|
|
172
|
+
|---|---|---|---|
|
|
173
|
+
| Documentation | 3 | mkdocs + guides exist; but exports like `SupervisorAgent`, `sandboxed_tool`, `MQTTTrigger` are thinly documented, no migration notes between 0.x releases | Rule: every name in `__all__` has a reference page and a runnable snippet, or it gets un-exported. Add UPGRADING.md per minor. |
|
|
174
|
+
| Reliability signals | 3 | CI matrix 3.10–3.12, 90% coverage gate, mypy strict — good. But `__version__` mismatch, deleted-feature churn, no 3.13 | Fix version (single-source from `importlib.metadata`), add 3.13, tag every release, keep CHANGELOG honest about removals. |
|
|
175
|
+
| Provider coverage | 2.5 | "OpenAI-compatible" is a fine stance but untested as a claim — nothing in CI exercises Groq/Ollama/OpenRouter quirks (e.g. tool-call format drift) | A recorded-response (VCR-style) provider matrix in CI + a documented compat table: provider / tools / streaming / tested version. |
|
|
176
|
+
| Observability & debuggability | 3 | Hooks + event stream are real; but no way to see *why* the DAG resolved the way it did, or why a condition skipped an agent | `pipe.explain()` — dry-run printing resolved levels, dependencies, and condition outcomes; include skip reasons in `agent_skipped` events. |
|
|
177
|
+
| Error messages | 4 | Exceptions are typed, carry agent/tool names, cycle detection exists | Cycle error should name the cycle members, not just "Cycle detected". |
|
|
178
|
+
| Ecosystem fit | 2 | README name-drops OpenTelemetry/Langfuse but ships no adapter; hooks are the right seam, unused | Ship `agentflow.contrib.otel.OTelHooks` (~50 lines) — one import to spans. Highest fit-per-line item on this list. |
|
|
179
|
+
| Trust signals | 2 | MIT clear, changelog exists — but version mismatch, pip-name/import-name split, sandbox-in-core security surface, feature churn | Fix version; add SECURITY.md; publish a stability policy (below); stop shipping code-execution sandboxes in the "minimal" core package. |
|
|
180
|
+
|
|
181
|
+
The sandbox deserves its own sentence: **a library advertising "minimal, auditable, two
|
|
182
|
+
dependencies" ships 461 lines of Docker/subprocess arbitrary-code execution in core.**
|
|
183
|
+
That's the single largest security surface in the package, in the component least related
|
|
184
|
+
to the wedge. Move it to a separate package or clearly-marked extra.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Analysis 4 — The killer example
|
|
189
|
+
|
|
190
|
+
The wedge is *visible cost + visible parallelism*. Design the demo so both are undeniable
|
|
191
|
+
in one run, with zero API keys required to try it (Groq free tier or Ollama).
|
|
192
|
+
|
|
193
|
+
**"Earnings-call triage"** — realistic diamond DAG with genuine dependencies:
|
|
194
|
+
|
|
195
|
+
```
|
|
196
|
+
┌─ financials_analyst ──┐
|
|
197
|
+
transcript_fetcher ─┼─ sentiment_analyst ──┼─ risk_synthesizer ─ brief_writer
|
|
198
|
+
└─ competitor_scanner ──┘
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
- `transcript_fetcher` uses **tools** (fetch + chunk) — shows the ReAct loop.
|
|
202
|
+
- The three middle analysts run **in parallel** — the event stream prints them starting
|
|
203
|
+
simultaneously with live `agent_complete` lines.
|
|
204
|
+
- `risk_synthesizer` has an `output_schema` — shows typed output.
|
|
205
|
+
- The script ends by printing: **wall time vs. sequential estimate** (e.g. "11.2s
|
|
206
|
+
parallel, ~29s sequential") and **per-agent + total cost**.
|
|
207
|
+
- Then it runs a second time with `InMemoryCache` and prints the delta:
|
|
208
|
+
"$0.0041 → $0.0007, 3 cache hits." That line is the whole pitch in one run.
|
|
209
|
+
|
|
210
|
+
Build it as `examples/earnings_triage.py`, make its *actual captured output* the first
|
|
211
|
+
code block in the README after install, and record a 20-second asciinema of it. Every
|
|
212
|
+
current README example is either a toy ("multiply two numbers") or unverifiable prose;
|
|
213
|
+
this replaces all of them as the centerpiece.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Analysis 5 — Path to real dependents (6 months)
|
|
218
|
+
|
|
219
|
+
**The 3–5 guarantees worth depending on:**
|
|
220
|
+
1. **Typed data plane end-to-end** (LLMResponse model, `Any` context, schema output flows
|
|
221
|
+
downstream) — the PydanticAI-parity feature.
|
|
222
|
+
2. **A written stability contract**: `PUBLIC_API.md` listing exactly what's covered by
|
|
223
|
+
semver, a deprecation policy (warn one minor, remove next major), and explicit 1.0
|
|
224
|
+
criteria. Teams adopt contracts, not features.
|
|
225
|
+
3. **Provider compat matrix tested in CI** — turns "OpenAI-compatible" from a hope into
|
|
226
|
+
a guarantee.
|
|
227
|
+
4. **OTel hooks adapter** — makes it composable with whatever observability stack the
|
|
228
|
+
team already runs.
|
|
229
|
+
5. **Cost budgets**: `Pipeline(budget_usd=0.50)` that aborts the run when exceeded.
|
|
230
|
+
Nobody else has this as a first-class primitive and it's ~40 lines on top of the
|
|
231
|
+
existing cost plumbing. This is the wedge feature.
|
|
232
|
+
|
|
233
|
+
**What hitting 1.0 requires:** the scope cut executed (below), the typed data plane done,
|
|
234
|
+
zero known breaking changes queued, three consecutive minors with no public-API breaks,
|
|
235
|
+
and the version/naming hygiene fixed. Realistically: 1.0 in month 5–6, not sooner.
|
|
236
|
+
|
|
237
|
+
**Where first users come from:** the cost-conscious OpenAI-compatible crowd — r/LocalLLaMA,
|
|
238
|
+
Groq and Ollama Discords, a Show HN. These communities *cannot* use most heavyweight
|
|
239
|
+
frameworks comfortably (local endpoints, tight budgets) and are exactly who the wedge
|
|
240
|
+
serves. What gets them to try it: the killer example running against Ollama with zero
|
|
241
|
+
API keys, and the cost-budget primitive.
|
|
242
|
+
|
|
243
|
+
**What to explicitly NOT build (scope creep already killing this):**
|
|
244
|
+
- No RAG: loaders, splitters, embeddings, vector stores. `VectorContext` is already over
|
|
245
|
+
the line — freeze it or extract it.
|
|
246
|
+
- No C++/Rust engine. The DAG resolution is microseconds against multi-second LLM calls;
|
|
247
|
+
it was rightly deleted, don't bring it back.
|
|
248
|
+
- No agent marketplace, personas, or prompt-template library.
|
|
249
|
+
- No general workflow engine (durable execution, cron, queues). `serve()` + MQTT is
|
|
250
|
+
already drifting there — extract triggers to `agentflowkit-triggers`.
|
|
251
|
+
- Sandbox → separate package (`agentflowkit-sandbox`) with its own security policy.
|
|
252
|
+
- Swarm/`SupervisorAgent`: keep only if it stays ≤ its current size; `swarm_routing.py`
|
|
253
|
+
and `distillation.py` are unexported speculative code — delete or ship, don't carry.
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Analysis 6 — Competitive teardown
|
|
258
|
+
|
|
259
|
+
**LangGraph** — Better: durable checkpointing/persistence, human-in-the-loop that survives
|
|
260
|
+
process death, LangSmith tracing, Studio, massive ecosystem and mindshare. Worse: dependency
|
|
261
|
+
weight, conceptual overhead (channels, reducers, compiled graphs) for what is often a
|
|
262
|
+
5-node DAG, hard to audit. **Structural gap agentflow owns:** LangGraph can never be
|
|
263
|
+
small — its value *is* the ecosystem. "Read the whole orchestrator before prod" is
|
|
264
|
+
permanently unavailable to them.
|
|
265
|
+
|
|
266
|
+
**CrewAI** — Better: marketing, onboarding, templates/personas, huge top-of-funnel. Worse:
|
|
267
|
+
magic-heavy abstractions, weak typing, unpredictable token burn, hard to reason about
|
|
268
|
+
execution order. **Gap:** engineering rigor + cost transparency. CrewAI's audience is
|
|
269
|
+
prototypers; it structurally won't chase `mypy --strict` teams, and per-agent USD
|
|
270
|
+
accounting undercuts them where they're weakest.
|
|
271
|
+
|
|
272
|
+
**PydanticAI** — the dangerous one. Better: typed agent I/O done right, real multi-provider
|
|
273
|
+
abstraction (native Anthropic/Gemini/Bedrock), the Pydantic team's trust halo, growing
|
|
274
|
+
fast. Worse: orchestration is not its center of gravity — multi-agent parallel DAG
|
|
275
|
+
composition means bolting on pydantic-graph, which is heavier and less legible than
|
|
276
|
+
`pipe.add(x, depends_on=[...])`. **Gap:** declarative parallel DAG + cost/budget
|
|
277
|
+
primitives. But note: if agentflow doesn't fix its typed data plane, PydanticAI wins the
|
|
278
|
+
comparison on agentflow's own claimed strength. This is why Analysis 2a is urgent.
|
|
279
|
+
|
|
280
|
+
**Raw asyncio** — Better: zero dependencies, zero abstraction tax, infinitely flexible.
|
|
281
|
+
Worse: every team re-writes retries, backoff-with-Retry-After, cost tables, cache keys,
|
|
282
|
+
event streams, timeout handling — badly, under deadline. **Gap:** agentflow's honest
|
|
283
|
+
one-line pitch against DIY: *"the 1,700 lines you were going to write around
|
|
284
|
+
`asyncio.gather` anyway, already typed and tested."* This baseline, not LangGraph, is the
|
|
285
|
+
real competitor for the target user — position against it explicitly.
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## Next 2 weeks — concrete actions
|
|
290
|
+
|
|
291
|
+
1. **Fix version hygiene** (1 hr): single-source `__version__` from package metadata;
|
|
292
|
+
verify pyproject/`__init__`/git tag agree; add a CI check that fails on mismatch.
|
|
293
|
+
2. **Execute the scope cut** (1–2 days): remove `sandbox`, `triggers`/`serve`,
|
|
294
|
+
`swarm_routing`, `distillation` from the core package (extras or delete); shrink
|
|
295
|
+
`__all__` to the narrow story; update README to match reality.
|
|
296
|
+
3. **Fix the shared-agent mutation bug** (½ day): `RunContext` passed through `execute()`
|
|
297
|
+
replacing `set_session`/`set_approval_policy` mutation; regression test with two
|
|
298
|
+
concurrent `run()`s sharing an agent.
|
|
299
|
+
4. **Type the data plane** (2–3 days): `LLMResponse` model, `context: dict[str, Any]`,
|
|
300
|
+
`output_schema` results flow to downstream agents as model instances; deprecation
|
|
301
|
+
shims for the dict access.
|
|
302
|
+
5. **Rename `total_duration` → `agent_seconds`, add `wall_time`** (1 hr, with shim).
|
|
303
|
+
6. **Build `examples/earnings_triage.py`** (1–2 days) and make its real output the README
|
|
304
|
+
centerpiece; record the cache-hit second run.
|
|
305
|
+
7. **Write `PUBLIC_API.md`** (½ day): covered surface, deprecation policy, 1.0 criteria.
|
|
306
|
+
8. **Ship `OTelHooks`** (½ day): one adapter class in `contrib/`, documented with a
|
|
307
|
+
Jaeger screenshot.
|
|
308
|
+
|
|
309
|
+
**The blunt bottom line:** the core four files are genuinely good — better engineered than
|
|
310
|
+
most 0.x agent libraries. The project's risk is not code quality; it's that it behaves
|
|
311
|
+
like four different projects sharing a repo, with trust-hygiene cracks a reviewer spots in
|
|
312
|
+
minutes. Cut to the wedge, type the data plane, fix the hour-long embarrassments, and
|
|
313
|
+
there is a real (if narrow) adoption path. Keep accreting pillars, and PydanticAI ends the
|
|
314
|
+
story by default.
|