Skip to content

feat(instrument): Smolagents adapter closure — sample + doc + alias (fixes mature label honesty)#109

Closed
mmercuri wants to merge 3 commits into
mainfrom
feat/instrument-frameworks-smolagents-closure
Closed

feat(instrument): Smolagents adapter closure — sample + doc + alias (fixes mature label honesty)#109
mmercuri wants to merge 3 commits into
mainfrom
feat/instrument-frameworks-smolagents-closure

Conversation

@mmercuri

Copy link
Copy Markdown
Contributor

Why this PR exists

scripts/emit_adapter_manifest.py lists smolagents in the _MATURE set
(line 156), alongside openai, anthropic, aws_bedrock, etc. The
manifest schema docstring defines mature as:

has dedicated unit-test file in tests/instrument/ AND a reference
doc in docs/adapters/.

Audit finding: smolagents had the test file
(tests/instrument/adapters/frameworks/test_smolagents_adapter.py) but
no docs/adapters/frameworks-smolagents.md and no
samples/instrument/smolagents/. The atlas-app catalog UI was
therefore advertising a tier the SDK could not back, in violation of
CLAUDE.md's "no fake data, no inflated labels, complete means
complete" rule.

This PR closes the artifact gap so the existing mature label is
truthful — no scope expansion, no decomposition of the 388-LOC
lifecycle.py (that is a separate follow-up PR).

What's in the PR

1. Reference doc (docs/adapters/frameworks-smolagents.md)

Mirrors the structure of frameworks-agno.md:

  • Install + Python/Pydantic version compatibility
  • Quick-start with real CodeAgent + HfApiModel
  • Events emitted (8 events across L1/L2/L3/L4a/L5a + cross-cutting)
  • SmolAgents specifics: manager → managed agent recursion,
    CodeAgent vs ToolCallingAgent, idempotent re-instrumentation,
    per-thread duration_ns correctness
  • Capability matrix: what's auto-wrapped vs caller-driven
  • Capture config recipes including capture_content=False
    (PII-sensitive)
  • BYOK note + backward-compat section pointing at the
    STRATIXSmolAgentsAdapter deprecation alias

2. Offline-runnable sample (samples/instrument/smolagents/)

  • main.py, __init__.py, README.md
  • Diverges from the samples/instrument/agno/ "requires OPENAI_API_KEY"
    pattern intentionally — the sample uses a duck-typed _FakeAgent so
    every developer can run python -m samples.instrument.smolagents.main
    with zero installs, zero network, zero API keys. The README
    documents the live-path swap (real CodeAgent + HttpEventSink).
  • Output is deterministic — 3 events emitted in known order, suitable
    for CI snapshotting.

3. STRATIX → LayerLens deprecation alias

  • STRATIXSmolAgentsAdapter re-exposed via PEP 562 module
    __getattr__ in src/.../smolagents/__init__.py, with
    DeprecationWarning on first access.
  • Listed in __all__ and __dir__ for IDE / dir() discoverability.
  • New tests: test_legacy_stratix_alias_warns_and_resolves,
    test_unknown_attribute_still_raises_attribute_error.
  • The lifecycle docstrings were already LayerLens-branded; no STRATIX
    strings to sweep within the smolagents adapter package.

Out of scope (explicitly)

  • Decomposing the 388-LOC lifecycle.py into sub-modules — separate
    follow-up PR.
  • Auto-wrapping smolagents tool / model layers — needs upstream
    callback hooks; documented as caller-driven for now.

Acceptance — STRICT (all green)

$ uv run pytest tests/instrument/adapters/frameworks/test_smolagents_adapter.py -x
14 passed in 1.10s

$ uv run mypy --strict src/layerlens/instrument/adapters/frameworks/smolagents
Success: no issues found in 2 source files

$ uv run ruff check src/layerlens/instrument/adapters/frameworks/smolagents \
                   tests/instrument/adapters/frameworks/test_smolagents_adapter.py
All checks passed!

$ uv run python -m samples.instrument.smolagents.main
Agent output: echo: What is 2 + 2?
Emitted 3 event(s):
  -     environment.config  agent=demo-agent
  -            agent.input  agent=demo-agent
  -           agent.output  agent=demo-agent

Lazy-import + default-install guards still pass for smolagents
(tests/instrument/test_lazy_imports.py — both
test_layerlens_import_does_not_pull_frameworks and
test_instrument_import_does_not_pull_frameworks green).

CLAUDE.md commercial-grade compliance

  • No TODOs, no skeleton, no placeholders.
  • Sample runs offline end-to-end with no real LLM / network calls.
  • All renames preserve backward compat via the deprecation alias.
  • No co-author trailer. Draft PR.

mmercuri and others added 3 commits April 25, 2026 19:13
Bootstraps the LayerLens instrument layer with the abstract base classes,
adapter registry, capture configuration, event sinks, vendored event
schemas, and pydantic v1/v2 compatibility shim that every concrete
adapter (frameworks, protocols, providers) will depend on.

Scope
-----
- src/layerlens/instrument/__init__.py: lean re-export surface
- src/layerlens/instrument/_vendored/: frozen ateam event schemas (no
  runtime ateam dependency)
- src/layerlens/instrument/adapters/_base/: BaseAdapter, AdapterRegistry,
  AdapterStatus, AdapterHealth, AdapterCapability, ReplayableTrace,
  CaptureConfig, EventSink, TraceStoreSink, IngestionPipelineSink,
  PydanticCompat
- src/layerlens/_compat/pydantic.py: model_dump/model_validate shim
  spanning pydantic v1 + v2
- scripts/{port_adapter,port_protocol,emit_adapter_manifest,
  regen_dep_baselines}.py: codegen helpers used to port the rest of M1
- tests/instrument/{test_base_layer,test_lazy_imports,
  test_default_install,test_resolved_dep_tree}.py + _baselines/
- .github/workflows/dep-tree-guard.yaml: CI gate that locks the default
  install footprint
- docs/adapters/: CONTRIBUTING, STATUS, pydantic-compatibility, testing,
  PERSONA_REVIEW

Blast radius
------------
- Pure additions. No public surface changes outside the new
  layerlens.instrument namespace.
- Default `pip install layerlens` install set is unchanged (verified by
  test_default_install.py against the new baseline).
- Lazy adapter discovery: importing layerlens.instrument MUST NOT pull
  in any optional adapter dep (verified by test_lazy_imports.py).

Test plan
---------
- uv run pytest tests/instrument/test_base_layer.py
  tests/instrument/test_lazy_imports.py -x  -> 45 passed
- The dep-tree-guard workflow exercises test_default_install.py and
  test_resolved_dep_tree.py against the new baselines on every PR.

LAY-3400 umbrella: this PR is the prerequisite for the M1.B/M1.C/M1.D
adapter ports, M7 protocol certification, and M8 Cohere/Mistral.
Ports the twelve agent-tier framework adapters from the ateam
reference implementation onto the new layerlens.instrument base layer:

  Semantic Kernel, LlamaIndex, OpenAI Agents, Pydantic-AI, Agno,
  Strands, SmolAgents, MS Agent Framework, Google ADK,
  Bedrock Agents, Embedding (vector store hooks), Benchmark Import

Pairs with feat/instrument-frameworks-orchestration (M1.C part 1)
which lands LangChain, LangGraph, CrewAI, AutoGen, Langfuse, and
Agentforce. Together they complete M1.C.

Scope
-----
- src/layerlens/instrument/adapters/frameworks/{semantic_kernel,
  llama_index,openai_agents,pydantic_ai,agno,strands,smolagents,
  ms_agent_framework,google_adk,bedrock_agents,embedding,
  benchmark_import}/: per-framework packages
- tests/instrument/adapters/frameworks/test_*_adapter.py + the
  test_bulk_ported_smoke.py harness (which exercises every ported
  adapter against canned trace fixtures so partial framework SDKs
  on a given runner don't drop coverage to zero)
- samples/instrument/<framework>/: runnable per-framework samples
- docs/adapters/frameworks-<framework>.md: per-framework integration
  guide
- pyproject.toml: twelve new optional extras
  (semantic-kernel, llama-index, openai-agents, pydantic-ai, agno,
  strands, smolagents, ms-agent-framework, google-adk,
  bedrock-agents, embedding, benchmark-import) with python_version
  markers; pyright/ruff exclusions for the dynamic monkey-patching
  framework code

Blast radius
------------
- Default `pip install layerlens` install set is unchanged. Each
  framework's heavy deps are gated behind their own extra.
- No changes to existing public API surface.
- Importing layerlens.instrument still does NOT pull in any framework
  module (lazy registry lookup).

Test plan
---------
- uv run pytest tests/instrument/adapters/frameworks/ -x  ->
  184 passed, 1 skipped (test_bulk_ported_smoke.py covers all 12
  agent-tier adapters plus the orchestration-tier ones from part 1
  via the same harness)

Stacks on
---------
- feat/instrument-base-foundation (M1.A) — required for the
  BaseAdapter surface this PR consumes.

Sibling of
----------
- feat/instrument-frameworks-orchestration (M1.C part 1) — both
  branches stack on the base foundation independently and don't
  conflict; they can land in either order.

LAY-3400 umbrella (M1.C part 2).
…on alias

The smolagents adapter is listed in scripts/emit_adapter_manifest.py
_MATURE alongside openai/anthropic/etc., but the manifest spec defines
"mature" as having BOTH a dedicated unit-test file AND a reference doc
in docs/adapters/. Until now smolagents had only the test file - no
sample, no doc - so the catalog was advertising a coverage tier the
artifacts did not back. Per CLAUDE.md (no fake data, no inflated
labels, complete means complete), the label was misleading customers
who saw "mature" and assumed full documentation parity.

This PR closes the artifact gap so the existing label is truthful:

* docs/adapters/frameworks-smolagents.md - install, quick-start,
  events emitted, lifecycle hooks, capability matrix, version
  compatibility, BYOK, and backward-compat notes. Mirrors the
  structure of frameworks-agno.md.
* samples/instrument/smolagents/{main,__init__}.py + README.md - a
  fully offline-runnable demo using a duck-typed _FakeAgent so the
  wrapper, lifecycle hooks, and event emission can be exercised with
  zero dependencies (no smolagents install, no OpenAI key, no
  network). Diverges from the agno sample's required-OPENAI_API_KEY
  pattern intentionally so contributors can verify the adapter on
  any laptop in CI.
* STRATIXSmolAgentsAdapter deprecation alias via PEP 562
  __getattr__ in the package __init__.py. The legacy
  STRATIX-branded name still imports for one deprecation cycle and
  emits DeprecationWarning on first access. New tests cover both
  the alias resolution and the unknown-attribute path.

NOT in scope: decomposing the 388-LOC single-file lifecycle.py into
sub-modules. That refactor is a follow-up PR; this one is artifact-
only closure to match the manifest's existing tier claim.

Acceptance:
* uv run pytest tests/instrument/adapters/frameworks/test_smolagents_adapter.py -x  ->  14 passed
* uv run mypy --strict src/layerlens/instrument/adapters/frameworks/smolagents     ->  clean
* uv run ruff check ...smolagents tests/.../test_smolagents_adapter.py             ->  clean
* uv run python -m samples.instrument.smolagents.main                              ->  3 events emitted, deterministic output
* Lazy-import + default-install guards still pass for smolagents
@mmercuri mmercuri requested a review from m-peko April 26, 2026 22:49
@m-peko m-peko closed this May 21, 2026
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.

2 participants