Skip to content

Module 1 agents - #2

Merged
mohdcodes merged 4 commits into
mainfrom
module-1-agents
Jul 19, 2026
Merged

Module 1 agents#2
mohdcodes merged 4 commits into
mainfrom
module-1-agents

Conversation

@mohdcodes

Copy link
Copy Markdown
Owner

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 a RunResult. This is the facade every later module plugs into.

What's included

BaseAgent (ABC) — agents/base.py

  • Abstract arun(input) -> RunResult — the single real contract.
  • Concrete run(input) — synchronous driver over arun, which refuses to nest inside a running event loop (clear, actionable error instead of a deadlock or fragile nest_asyncio patching).

Agent facade — agents/agent.py

  • Async-core, sync-wraps: all orchestration lives in arun; run drives it. Reliability, tracing, cost, and HITL are written once on the async path — no duplicated code paths.
  • Composition over inheritance for collaborators: Agent has a tracer / cost tracker / reliability policy; it is not one.
  • Normalises any inner target into one async callable — sync callables run via asyncio.to_thread (never block the loop); async callables are awaited directly.
  • wrap() is methodoverload site #3: @overload on BaseAgent + @overload on object as an ordered catch-all (first-match-wins routes correctly).

Null-object seams — agents/seams.py

  • NullTracer, NullCostTracker, PassthroughReliability + their Protocol contracts.
  • Agent.arun is written in its final shape today; Modules 2/3/4/9 swap in real implementations with zero edits to Agent. 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 — added reset_trace_id(token) for proper contextvar restore.

Trace model

  • Exactly one trace_id per arun() call. When an Agent wraps another Agent, 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 annotations breaks dispatch — PEP 563 stringizes annotations, and the library does isinstance(value, annotation) at runtime, so isinstance(x, "BaseAgent") raises. agent.py omits the future import; every future overload module must too. Guarded by a dispatch test in CI.
  • A plain method overwrites an @overload — the library only merges @overload-decorated siblings, so the callable catch-all is itself an @overload on object.

Tooling — uv adoption

  • Added uv.lock (committed) and .python-version (3.12) for reproducible installs.
  • CI switched to astral-sh/setup-uv, running ruff/mypy/pytest via uv across the 3.10 / 3.11 / 3.12 matrix.
  • Dev workflow documented in the README; .venv is gitignored.

Verification

  • ✅ 53 tests passing (17 new), 97% coverage (gate: 80%)
  • ruff check + ruff format --check clean
  • mypy --strict clean
  • ✅ Smoke test: Agent(lambda x: x*2).run(21)output=42 with live trace_id log correlation

Definition of Done

  • §6.1 contract implemented (BaseAgent, Agent, wrap)
  • OOP pillars present: inheritance (Agent is-a BaseAgent), composition (collaborators), polymorphism (overload dispatch)
  • methodoverload used at designated site #3, with findings documented
  • Mandatory §8 tests (wrap dispatch, run/arun, trace_id populated + correlated, async path)
  • ruff / mypy / pytest green
  • No print(); correct log levels + trace correlation
  • DESIGN_LOG.md entry + HARD_QUESTIONS.md batch written and answered

mohdcodes and others added 4 commits July 19, 2026 16:25
- 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>
@mohdcodes
mohdcodes merged commit 20960b7 into main Jul 19, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant