This document defines exactly which layer is responsible for what across Rastir’s three tiers: Adapter, Client, and Server. Every adapter implementation must conform to these boundaries.
1. Adapters (per-provider / per-framework)
Adapters are stateless extraction modules. They inspect request arguments and response objects to produce structured metadata. They must never create spans, mutate global state, or interact with the server.
1.1 Adapter Kinds
Kind
Priority Range
Purpose
framework
200–300
Unwrap framework wrappers (LangChain, LangGraph, LlamaIndex, CrewAI) and detect embedded model objects
Needs can_handle_request for ChatAnthropic objects
Bedrock
provider
140
✅ guardrail config
✅ model, tokens, guardrails
✅
✅
Fully compliant
Azure OpenAI
provider
155
❌
✅ model, tokens, finish_reason
✅
—
Needs can_handle_request for Azure model objects
Gemini
provider
150
❌
✅ model, tokens
✅
—
Needs can_handle_request for Gemini model objects
Groq
provider
152
❌
✅ model, tokens
✅
—
Needs can_handle_request for Groq model objects
Mistral
provider
150
❌
✅ model, tokens
✅
—
Needs can_handle_request for Mistral model objects
Cohere
provider
150
❌
✅ model, tokens
✅
—
Needs can_handle_request for Cohere model objects
LangChain
framework
250
✅ model, provider from model objects
✅ unwrap + model
✅ (flag only)
—
Stream extraction defers to provider adapter
LangGraph
framework
260
✅ model, provider via node/closure walk
✅ unwrap + model
✅
—
Fully compliant
LlamaIndex
framework
240
❌
✅ unwrap + model
✅
—
Needs can_handle_request for LlamaIndex model objects
CrewAI
framework
245
❌
✅ unwrap + model
❌
—
Needs request metadata + streaming
Retrieval
provider
50
❌
✅ retrieval metadata
❌
—
N/A — no LLM, no tokens
Tool
provider
10
❌
✅ tool metadata
❌
—
N/A — no LLM, no tokens
Fallback
fallback
0
❌
✅ basic model/provider
❌
—
Catch-all, minimal by design
Alignment Gaps (TODO)
Provider adapters need request-phase extraction: OpenAI, Anthropic, Gemini, Groq, Mistral, Cohere, Azure OpenAI should implement can_handle_request() / extract_request_metadata() to detect their native SDK model objects in function arguments and return model + provider. This ensures model metadata survives even when the API call fails.
LlamaIndex needs request metadata: Should scan args for LlamaIndex model objects.
CrewAI needs request metadata + streaming: Currently the least compliant framework adapter.
LangChain streaming: Declares supports_streaming = True but does not implement can_handle_stream() / extract_stream_delta() — relies on response unwrapping to provider adapter for streaming.
6. Label Inheritance — Parent → Child Propagation
6.1 Propagation Mechanism
Rastir does not copy attributes from parent spans to children. Instead, it uses dedicated ContextVar variables that decorators set and read:
ContextVar
Set by
Read by
Value
_current_span
start_span()
get_current_span()
Current span (for parent-child linking)
_current_agent
@agent
@llm, @retrieval
Agent name string
_current_model
@llm (in _finalize_llm_span)
—
Model name string
_current_provider
@llm (in _finalize_llm_span)
—
Provider name string
6.2 Inheritance Rules (Normative)
Every span type MUST carry these labels when available. The table below specifies who sets each label and where the value comes from.
Label
@agent
@llm
@retrieval
@trace
evaluation (server)
agent
Sets from decorator arg
Reads from _current_agent
Reads from _current_agent
—
Copies from parent LLM span
model
—
Adapter extracts (request + response) or decorator override
Should read from _current_model
—
Copies from parent LLM span
provider
—
Adapter extracts (request + response) or decorator override
The server reads labels from span attributes to derive metrics. These labels MUST be present on the span for the corresponding metric to be meaningful:
Implement can_handle_request() / extract_request_metadata() on each
8
LlamaIndex, CrewAI lack request metadata
Implement same
9
LangChain declares streaming but doesn’t implement stream methods
Either implement or remove the flag
8. Rules for New Adapters
Never import the provider SDK at module level. Use type(obj).__name__ and type(obj).__module__ for detection.
Always implement both request and response phases. A well-behaved adapter provides can_handle_request() + extract_request_metadata() AND can_handle() + transform().
Use BaseAdapter._find_in_args() to scan positional and keyword arguments.
Use BaseAdapter._extract_model_attr() to read the first available model attribute from an object.
Use detect_provider_from_module() to map module names to canonical provider strings.
Declare capability flags accurately — the registry uses them to skip unnecessary calls.
Set priority in the correct range for your adapter kind.
Register via __init__.py — adapters are auto-registered on import.
Labels must flow downward. Any label set by a parent decorator (agent, model, provider) must be readable by child spans via ContextVars. If adding a new inheritable label, add a ContextVar + getter/setter in context.py.