MemorySync
Integrations

AgentOps Memory Observability

See every memory operation in your AgentOps traces. The first SDK-level tracing instrumentor shipped by any memory vendor: one line after agentops.init() and every add, recall, query, and forget appears as a first-class span — with memory content excluded by default.

AgentOps uses its global tracer provider; instrument_memorysync wraps the SDK and emits standard OTel spans without content.

How it works

  1. Install opentelemetry-instrumentation-memorysync next to agentops.
  2. agentops.init() registers AgentOps as the global OpenTelemetry tracer provider.
  3. instrument_memorysync() wraps the MemorySync SDK once — every client in the process now emits standard OTel spans through whatever provider is active.
  4. Your memory operations land in AgentOps traces next to your LLM calls. No adapter, no plugin, no AgentOps-specific code.

Before you start

  1. An AgentOps account and API key, plus a MemorySync API key.
  2. Python 3.9+ with the memorysync SDK 1.8+ in your app.

Install

Terminal
pip install opentelemetry-instrumentation-memorysync agentops

Set up

Step 1 — two lines at startup

Order doesn’t matter — the instrumentor binds the active provider late, so it works before or after agentops.init():

app.py
import agentops
from opentelemetry.instrumentation.memorysync import instrument_memorysync
agentops.init() # registers AgentOps as the global OTel provider
instrument_memorysync() # before or after init() — both work

Step 2 — use the SDK as normal

Nothing else changes. Every operation now produces a span:

app.py
from memorysync import MemorySyncClient
client = MemorySyncClient(api_key="ms_...", base_url="https://api.memorysync.io")
client.add("Prefers window seats", source="chat") # span: memorysync.add
client.query("seating preferences", k=5) # span: memorysync.query

Zero-code alternative

The package registers the standard OTel entry point, so the CLI wrapper also works with no source changes:

Terminal
opentelemetry-instrument python app.py

What flows into your traces

OperationSpanKey attributes
add / bulk_add / summarizememorysync.add …memory_id, status (stored/skipped), skip reason
query / retrieve / recall / search_routedmemorysync.query …k, results_count, score min/max/avg, context_chars, server latency
add_turnmemorysync.add_turntenant/user/session ids, role, processing_status, request_id (the extraction job), already_exists idempotency signal
forgetmemorysync.forgetselector (ids/filters), ids_count, deleted_count, dry_run
get / update / history / feedback / list_memoriesmemorysync.get …memory_id, updated field NAMES (never values), result counts
append_history / list_history / delete_historymemorysync.append_history …session_id, turns_count, extract, created_count, deleted_count, deleted_facts_count
put_state / get_state / list_state / search_state / delete_state / list_state_namespacesmemorysync.put_state …namespace, key, created, request_id, result counts, score min/max/avg, deleted_facts_count

add_turn sends one turn to fact extraction and does not store the turn itself, so its span reports the extraction receipt (processing_status: distilling, skipped_non_user_turn, skipped_low_value, skipped_replay, or skipped over the plan limit) rather than a memory id. The conversation-history and framework-state methods exist in the memorysync SDK 1.10.0 and later; with an older SDK installed, the memory operations are traced as usual.

A memorysync.client.operation.duration histogram is recorded per call for latency dashboards.

Privacy: content is never recorded by default

Memory content is customer data. By default the spans carry counts, identifiers, scores, and latencies — never memory text, query text, or recalled context. Opt in explicitly with instrument_memorysync(capture_content=True) or MEMORYSYNC_OTEL_CAPTURE_CONTENT=true; even then input is truncated to 500 characters and at most 5 result texts of 200 characters each are recorded.

Engineering guarantees

  • Instrumentation can never break the app. Attribute extraction is fully guarded; results and exceptions pass through untouched; spans always end.
  • Full sync/async parity — both MemorySyncClient and AsyncMemorySyncClient, all twenty-three operations (the incumbent wrapper skips history on cloud clients).
  • Errors are first-class: failed calls produce ERROR spans with error.type and the HTTP status, and the original exception is re-raised unchanged.
  • Idempotent lifecycle: double-instrument is a no-op; uninstrument_memorysync() restores the original methods.

Troubleshooting

  • No memory spans in AgentOps — confirm agentops.init() actually ran in the same process, and that instrument_memorysync() was called before the operations you expected to see.
  • Memory text missing from spans — that’s the privacy default. Opt in with capture_content=True if you truly need it.
  • Double-instrumented by accident — harmless; the second call is a no-op.
  • Need to detach — uninstrument_memorysync() restores the original client methods.

Supported versions

SurfaceRequiresVerified on
opentelemetry-instrumentation-memorysync 1.1.0Python 3.9+; opentelemetry-api 1.20+; memorysync 1.8+74 CI checks: the REAL memorysync SDK over production response shapes with the OTel in-memory exporter — sync/async parity, add_turn extraction receipts, the history and state spans, privacy default, byte-identical pass-through, error spans, lifecycle, the late-binding provider seam — plus agentops installed at latest with seam drift alarms pinning the global-tracer-provider contract

Where to go next

Was this page helpful?