Adapter System
Adapters translate provider responses or framework events into Attest’s common Trace format. Choose one that records the evidence your assertions need: a provider response alone does not describe an entire agent workflow or prove a tool’s external effects.
Integration types
Section titled “Integration types”| Type | Input | What it can establish |
|---|---|---|
| Provider | A completed provider response and caller-supplied context | LLM output, reported tokens, requested tool calls |
| Framework | Framework callbacks, events, or run summaries | The execution details actually exposed by that integration |
| Manual | Steps supplied by your code | Whatever you explicitly record |
| OpenTelemetry | Existing spans | What the spans and supported attributes contain |
These integration types are not the cost tiers (TIER_1–TIER_3) or maturity levels below. An adapter does not make unsupported evidence appear. In particular, a requested tool call is not proof that the tool ran, and missing cost or timing information is not a measured zero.
Provider response capture
Section titled “Provider response capture”Python provider adapters share BaseProviderAdapter.trace_from_response(). It builds a trace from an existing response; it does not automatically wrap every client call. Supply the input and timestamps around the actual request when you need them:
from attest import OpenAIAdapter
# response and messages come from your application's completed provider call.trace = OpenAIAdapter(agent_id="support").trace_from_response( response, input_messages=messages, started_at_ms=started, ended_at_ms=ended,)The base implementation resolves timestamps, creates a TraceBuilder, extracts completion/model/token data and tool calls, and assembles output and metadata. Missing timestamps fall back to the current time; those fallbacks do not measure the original request duration. The caller can supply metadata such as cost_usd and latency_ms.
OpenAI capture supports Chat Completions and Responses API response shapes. An OpenAI-compatible service must return a supported response shape; endpoint compatibility alone is not a guarantee of trace parity.
Custom Python provider adapters
Section titled “Custom Python provider adapters”Subclass BaseProviderAdapter from attest.adapters._base. Its required extraction hooks are:
| Hook | Return value |
|---|---|
_extract_completion(response) | Completion string |
_extract_model(response, **metadata) | Model string or None |
_extract_total_tokens(response) | Token count or None |
_extract_tool_calls(response) | List of dictionaries with name and optional args |
Optional hooks include _extract_input(input_messages, **metadata), _build_output(response, completion_text, **metadata), _extract_token_split(response), and _extract_refusal(response). Default output is {"message": completion_text}. Return unknown usage as absent rather than inventing measurements.
Framework capture
Section titled “Framework capture”Framework adapters have framework-specific entry points rather than one universal interception interface.
| Python integration | Entry point | Evidence boundary |
|---|---|---|
| LangChain | with adapter.capture() as handler:; pass handler in callbacks, then read adapter.trace | Captures supplied callback events |
| Google ADK | await adapter.capture_async(runner, user_id, session_id, message) or GoogleADKAdapter.from_events(events) | Converts the runner’s event stream |
| CrewAI | with adapter.capture() as cap: then cap.trace_from_crew_output(result, crew) | Summary-only: final output, token usage, and latency; not per-tool/delegation evidence |
| LlamaIndex | with handler: then handler.build_trace(query=..., response=...) | Captures supported instrumentation events |
A LangChain example:
from attest import LangChainAdapter
adapter = LangChainAdapter(agent_id="support")with adapter.capture() as handler: executor.invoke({"input": "Check my order"}, config={"callbacks": [handler]})trace = adapter.traceDo not infer parallel execution, agent identity, or nested delegation merely from the framework’s feature list. Inspect the trace for the required agent_call/sub_trace structure and temporal fields before using those assertions. Use manual instrumentation or another capture path when an adapter cannot expose the necessary evidence.
Manual and OpenTelemetry capture
Section titled “Manual and OpenTelemetry capture”Manual traces are useful for custom agents or isolated tests, without provider calls:
from attest import ManualAdapter
builder = ManualAdapter(agent_id="support").create_builder()builder.set_input(query="Check order")builder.add_tool_call("lookup_order", args={"order_id": "O-1042"})builder.set_output(message="Order found")trace = builder.build()Python OTelAdapter.from_spans(otel_spans) accepts readable spans, while from_otel_span_dicts() accepts span dictionaries. They convert supported telemetry into traces; the existence of an OpenTelemetry integration does not guarantee that every observability vendor or attribute convention is supported.
Released inventory and maturity
Section titled “Released inventory and maturity”The 1.1.0 engine capability inventory lists 10 Python and 7 TypeScript adapters. Maturity describes compatibility commitments, not assertion cost:
- Stable: covered by parity and conformance suites; behavior changes are breaking changes.
- Beta: shipped and tested, but interface or behavior can change in a minor release.
- Experimental: no stability commitment; may change or be removed in any release.
| Integration | Python | TypeScript | Maturity |
|---|---|---|---|
| OpenAI | OpenAIAdapter | OpenAI | Stable |
| Anthropic | AnthropicAdapter | Anthropic | Stable |
| Manual | ManualAdapter | Manual | Stable |
| Gemini | GeminiAdapter | Gemini | Beta |
| Ollama | OllamaAdapter | Ollama | Beta |
| LangChain / LangChain.js | LangChainAdapter | LangChain.js | Beta |
| OpenTelemetry | OTelAdapter | OpenTelemetry | Beta |
| CrewAI | CrewAIAdapter | Not shipped | Beta |
| Google ADK | GoogleADKAdapter | Not shipped | Experimental |
| LlamaIndex | LlamaIndexInstrumentationHandler | Not shipped | Experimental |
Install the Python extra for the chosen integration:
uv add "attest-ai[openai]>=1.1.0"Other extras are anthropic, gemini, ollama, langchain, crewai, google-adk, llamaindex, and otel. Manual capture is included in the base package. See the assertion pipeline for the evidence each kind of check needs.