Module 1 agents - #2
Merged
Merged
Conversation
- BaseAgent(ABC): async arun() contract + sync run() driver that refuses to nest inside a running event loop (clear error, no deadlock). - Agent facade: composition for collaborators, async-core orchestration in final shape, sync callables run via asyncio.to_thread, async awaited directly. - Null-object seams (NullTracer/NullCostTracker/PassthroughReliability) + their Protocol contracts so Modules 2/3/4/9 swap in real impls with zero Agent edits. - observability/conventions.py: GenAI attribute-key single source of truth. - logging.py: added reset_trace_id(token). - wrap() = methodoverload site #3: @overload on BaseAgent + @overload on object catch-all (ordered, first-match-wins). methodoverload findings (documented in docs/concepts/methodoverload.md): - `from __future__ import annotations` BREAKS runtime isinstance dispatch (PEP 563 stringizes annotations) -> agent.py omits it; constrains all future overload sites. - a plain method overwrites an @overload -> every branch must be decorated. 52 tests (16 new), 97% coverage; ruff + mypy clean. DESIGN_LOG + HARD_QUESTIONS (10) + module_notes/module1.md written. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Add uv.lock (committed) and .python-version (3.12) for reproducible installs. - README: document the uv dev workflow (uv venv / uv pip install / uv run). - CI: switch to astral-sh/setup-uv; install + run ruff/mypy/pytest via uv, driving each matrix Python (3.10/3.11/3.12) explicitly so .python-version doesn't override the matrix. - .venv is gitignored; deps stay declared in pyproject.toml (uv.lock is the lockfile, no requirements.txt needed). All 52 tests green inside the isolated .venv. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
From the Module 1 HARD_QUESTIONS review: make the "trace_ids nest, don't compete" model an explicit tested invariant. - agent.py: docstring note on the one-trace_id-per-arun / nesting semantics. - test: Agent-wrapping-Agent gives the inner its own trace_id, then restores the outer's (contextvar token nesting); context fully clears on unwind. - Clarified that only Agent participates in the trace-id scheme; a bare BaseAgent inherits the surrounding context (correct, not a bug). 53 tests green; ruff + mypy clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Module 1 — Agents: BaseAgent + Agent facade
Introduces the wrap — the central abstraction of AgentArgus.
Agent(inner)takes anything you already have (a function, an async function, a callable object, or another agent) and runs it uniformly, producing aRunResult. This is the facade every later module plugs into.What's included
BaseAgent(ABC) —agents/base.pyarun(input) -> RunResult— the single real contract.run(input)— synchronous driver overarun, which refuses to nest inside a running event loop (clear, actionable error instead of a deadlock or fragilenest_asynciopatching).Agentfacade —agents/agent.pyarun;rundrives it. Reliability, tracing, cost, and HITL are written once on the async path — no duplicated code paths.Agenthas a tracer / cost tracker / reliability policy; it is not one.asyncio.to_thread(never block the loop); async callables are awaited directly.wrap()is methodoverload site #3:@overloadonBaseAgent+@overloadonobjectas an ordered catch-all (first-match-wins routes correctly).Null-object seams —
agents/seams.pyNullTracer,NullCostTracker,PassthroughReliability+ theirProtocolcontracts.Agent.arunis written in its final shape today; Modules 2/3/4/9 swap in real implementations with zero edits toAgent. The null classes double as the documented contract each real collaborator must satisfy.Supporting
observability/conventions.py— GenAI semantic-convention attribute keys (single source of truth).logging.py— addedreset_trace_id(token)for proper contextvar restore.Trace model
trace_idperarun()call. When anAgentwraps anotherAgent, trace_ids nest (via contextvar token stacking) rather than compete — the inner run shadows the outer's id, then it's restored on unwind. Locked in as a tested invariant. Module 2's OTel Tracer will formalize this as a parent/child span tree.methodoverload findings (documented in
docs/concepts/methodoverload.md)Two runtime behaviors of the overload library that constrain all future overload sites:
from __future__ import annotationsbreaks dispatch — PEP 563 stringizes annotations, and the library doesisinstance(value, annotation)at runtime, soisinstance(x, "BaseAgent")raises.agent.pyomits the future import; every future overload module must too. Guarded by a dispatch test in CI.@overload— the library only merges@overload-decorated siblings, so the callable catch-all is itself an@overloadonobject.Tooling — uv adoption
uv.lock(committed) and.python-version(3.12) for reproducible installs.astral-sh/setup-uv, running ruff/mypy/pytest via uv across the 3.10 / 3.11 / 3.12 matrix..venvis gitignored.Verification
ruff check+ruff format --checkcleanmypy --strictcleanAgent(lambda x: x*2).run(21)→output=42with livetrace_idlog correlationDefinition of Done
BaseAgent,Agent,wrap)Agentis-aBaseAgent), composition (collaborators), polymorphism (overload dispatch)print(); correct log levels + trace correlationDESIGN_LOG.mdentry +HARD_QUESTIONS.mdbatch written and answered