feat: telemetry for memory manager - #2858
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
e31970c to
965a58b
Compare
|
Assessment: Comment Well-structured feature that fills a real observability gap, and the PR description does an excellent job explaining the why (detached-extraction-as-root-span-with-link, telemetry-as-best-effort, content-as-span-events). Tests are thorough — success/error/early-return paths, telemetry-failure isolation, and the detached link behavior are all covered, and they pass locally. My feedback is mostly about consistency, not correctness. Review themes
Nicely done overall — the design reasoning is unusually clear and the extraction-coordinator failure handling is careful. |
|
I think the otel metrics can be skipped as of now? they might be noisy |
|
@poshinchen will scope the PR to tracing/spans only for now, and expand to TS |
|
Re-review after rescope — Assessment: Comment (close to Approve) Picking up the changes since my first pass: metrics were dropped per @poshinchen's noise concern ( What I verified this round
The scope reduction tightened this up nicely and the TS implementation is a careful 1:1 of the Python semantics. The only remaining item is the cosmetic |
|
Re-review after The latest commit cleanly resolves the only outstanding code nit: No remaining code-level concerns. The one open item is process, not code: per Nicely iterated — the design reasoning and failure handling have been solid throughout. |
ded3a77 to
e059a94
Compare
|
Re-review after This follow-up makes two small, well-scoped improvements, both verified locally (123 tracer + memory-telemetry tests pass, mypy clean, Python/TS kept in parity): What changed
No remaining code-level concerns. The only open item is still process, not code: per Solid, focused iteration. |
Description
Motivation
Observability is a core feature of the SDK, but the memory subsystem was the one major agent subsystem with no OpenTelemetry instrumentation — it emitted only logs. Every other component (agent invocation, event-loop cycle, tool execution, model invocation) flows through the
Tracerfor spans, so memory operations were invisible in traces: you couldn't see how often memory was searched, how slow stores were, how many entries were retrieved/injected/extracted, or why a background extraction failed. Notably, the memory extractor's model call was the only model invocation in either SDK not wrapped in a model-invoke span, leaving extraction cost and latency completely dark.This change instruments the memory subsystem in both the Python and TypeScript SDKs so it matches the rest of the SDK: spans for search, add, context injection, and background extraction, plus wrapping the extraction model call in the existing model-invoke span. Spans nest correctly under the agent/cycle/tool traces.
This PR is tracing/spans only. An earlier revision also added
strands.memory.*metrics; those were dropped after review feedback that the metric set was noisy and thestore_namedimension risked unbounded cardinality. Metrics can follow in a separate PR once there is a concrete dashboard need — most of what they offered is already derivable from span data.Public API Changes
No existing signature on
AgentorMemoryManagerchanged. The new surface is the span schema exporters receive, and theTracermethods that produce it.Span schema — what shows up in a tracing backend. Span names use a
memory.*namespace alongside the standardgen_ai.operation.name; the two SDKs are kept at exact parity on names, attributes, and events.memory.searchmemory.store.names,memory.store.count,memory.max_search_results,memory.result.count,memory.store.failure_countmemory.query,memory.resultsmemory.addmemory.store.names,memory.store.count,memory.store.failure_countmemory.contentmemory.injectmemory.max_entries,memory.injected,memory.entry.count,memory.inject.format_errormemory.extractmemory.store.name,memory.message.count,memory.message.filtered_count,memory.extractor,memory.entry.count,memory.parent.trace_id,memory.parent.span_idNew
Tracermethods — a start/end pair per operation, following the existingstart*Span/end*Spanconvention. TypeScript (strands-ts/src/telemetry/tracer.ts) is options-object based and returnsSpan | null:The matching
StartMemory*SpanOptions/EndMemory*SpanOptionsinterfaces are exported fromstrands-ts/src/telemetry/types.ts. Python (strands-py/src/strands/telemetry/tracer.py) mirrors these as keyword-argument methods:Design notes
Detached extraction is a root span, not a child. Automatic extraction runs in a background task (Python
asyncio, TS promise chain) that typically completes after the agent span has already ended. Parenting to it would orphan the span, so extraction starts its own root trace and carries an OpenTelemetry link back to the agent span — captured synchronously while that span is still live. This preserves causal association without an orphaned parent-child relationship.Telemetry never breaks memory. Span failures are isolated so a misbehaving tracer provider can never turn a successful search/add/extraction into a failure. The detached extraction ends its span best-effort after the save resolves, so a tracing error can't roll back a successful write or trip the store's backoff logic.
Injection fails open and never leaks a span. The injection path ends its span on every branch — no query, no results, a throwing format callback (recorded as a flag, not an error, since the agent did not fail), and an unexpected search error (span ended, then rethrown).
Content on span events. Query and stored/retrieved content are recorded as span events, consistent with how model and tool spans record their inputs. Memory payloads may contain PII; this is documented on the span methods so operators can suppress span content at the exporter when needed.
Per-SDK tracer-acquisition idiom. Python uses the existing global
get_tracer(). TypeScript'sTraceris per-instance, soMemoryManagerconstructs one and shares it with the extraction coordinator; parenting still flows through OTel context, so this does not fragment traces.Related Issues
Documentation PR
Type of Change
New feature
Testing
How have you tested the change? Verify that the changes do not break functionality or introduce new warnings.
hatch run prepareUnit tests in both SDKs cover the span lifecycle for each operation (success, error, and early-return paths), the detached-extraction root-span-with-link behavior, telemetry-failure isolation, fail-open injection, the model-invoke span on the extractor call, and (TS) span-context validity/recording checks. Verified end to end with in-memory span exporters in both SDKs: injection nests a child search span, a tool-invoked search nests under the tool span, and extraction appears as its own root trace linked back to the agent span.
Checklist
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.