The Adapter Pattern: Unified Tracing Across AI SDK, LangChain, and OpenAI Agents A developer detailed an adapter pattern for unified tracing across AI SDK, LangChain.js, OpenAI Agents SDK, and direct model clients. The approach normalizes framework-specific lifecycles into a common span model, enabling shared observability tooling without coupling to any single framework. The design includes capability declarations and source attribution to aid debugging. An adapter layer becomes strategically useful when several teams need one observability contract but cannot, or should not, standardize on one agent framework. AI SDK, LangChain.js, OpenAI Agents SDK, and direct model clients organize execution differently. One emphasizes generation and streaming, another exposes hierarchical callbacks, another has agent runs and handoffs, and a direct client exposes only provider requests unless the application adds its own spans. Unified tracing should preserve those differences while translating the common lifecycle into one model. Done well, teams can share execution-tree tooling, CI quality gates, privacy policy, and telemetry export without coupling every consumer to every framework. The frameworks do not need a shared callback interface. They need a shared answer to a smaller set of questions: The framework adapter converts its native lifecycle into those semantics. Consumers never call framework hooks directly. php AI SDK lifecycle ---------┐ LangChain callbacks ------+-- framework adapters -- trace core OpenAI Agents tracing ----+ direct client wrappers ---┘ | +------------+------------+ | | | execution UI CI rules telemetry sinks The exact public APIs change over time, so keep the mapping conceptual and verify it against the supported framework version. | Normalized concept | AI SDK-style integration | LangChain-style integration | OpenAI Agents-style integration | Direct client | |---|---|---|---|---| | Root run | Application request or generation | Chain, graph, or agent run | Agent trace or runner invocation | Manual application span | | Model span | Generation or stream lifecycle | LLM/chat-model callback | Model generation item/span | Provider request wrapper | | Tool span | Tool execution lifecycle | Tool callback/run | Function-tool execution | Application tool wrapper | | Retrieval span | Tool or explicit application span | Retriever callback/run | Tool or custom span | Application wrapper | | Handoff | Application-defined transition | Graph/chain transition | Native handoff lifecycle | Application span | | Parent context | App context plus source IDs | Parent run identifiers | Trace/span context | AsyncLocalStorage or explicit context | | Token usage | Final generation usage when exposed | Model callback metadata when exposed | Run/model usage when exposed | Provider response usage | This matrix is a design guide, not a promise that every version exposes every cell. The adapter’s capability declaration is the authoritative record. The common model should represent lifecycle and capability without importing framework types. type Capability = | 'model lifecycle' | 'tool lifecycle' | 'retrieval lifecycle' | 'handoff lifecycle' | 'parent relationships' | 'streaming completion' | 'token usage' | 'cancellation'; type AdapterDescriptor = { id: string; adapterVersion: string; framework: string; supportedFrameworkRange: string; capabilities: Partial