Metadata-Version: 2.5
Name: opentelemetry-instrumentation-memorysync
Version: 1.1.0
Summary: OpenTelemetry instrumentation for the MemorySync Python SDK: memory, history and state operations as first-class spans in AgentOps, Respan, and any OTel backend.
Project-URL: Homepage, https://memorysync.io
Project-URL: Documentation, https://docs.memorysync.io/guides/agentops
Project-URL: Respan guide, https://docs.memorysync.io/guides/respan
Author-email: MemorySync <support@memorysync.io>
License: MIT
Keywords: agentops,agents,instrumentation,llm,memory,memorysync,observability,opentelemetry,respan,tracing
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: opentelemetry-api>=1.20.0
Requires-Dist: opentelemetry-instrumentation>=0.41b0
Provides-Extra: instruments
Requires-Dist: memorysync>=1.8.0; extra == 'instruments'
Provides-Extra: test
Requires-Dist: httpx<1.0,>=0.25; extra == 'test'
Requires-Dist: memorysync>=1.10.0; extra == 'test'
Requires-Dist: opentelemetry-sdk>=1.20.0; extra == 'test'
Requires-Dist: pytest>=7.0; extra == 'test'
Description-Content-Type: text/markdown

# opentelemetry-instrumentation-memorysync

OpenTelemetry instrumentation for the [MemorySync](https://memorysync.io)
Python SDK — memory, conversation-history and framework-state operations
become first-class spans in **AgentOps**, **Respan**, and any other
OpenTelemetry backend.

The first SDK-level tracing instrumentor shipped by any memory vendor.

```bash
pip install opentelemetry-instrumentation-memorysync
```

## With AgentOps

`agentops.init()` registers AgentOps as the global OpenTelemetry tracer
provider, so MemorySync spans land in your AgentOps traces automatically:

```python
import agentops
from opentelemetry.instrumentation.memorysync import instrument_memorysync
from memorysync import MemorySyncClient

agentops.init()            # before or after instrument_memorysync() — both work
instrument_memorysync()

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
```

## With Respan (formerly Keywords AI)

Route your LLM calls through the Respan gateway and instrument MemorySync —
every step of the loop is a first-class span:

```python
from openai import OpenAI
from opentelemetry.instrumentation.memorysync import instrument_memorysync

instrument_memorysync()
llm = OpenAI(api_key=RESPAN_API_KEY, base_url="https://api.respan.ai/api/")

context = ms.recall(tenant_id=t, user_id=u, prompt=question, k=6)   # span 1
reply = llm.chat.completions.create(...)                            # gateway-traced
ms.add_turn(tenant_id=t, user_id=u, text=question, role="user")     # span 2
```

`add_turn` sends the user's turn to fact extraction: only the durable facts
it contains are stored, as memories, so the span reports the extraction
receipt (`processing_status`, `request_id`) rather than a memory id.
Assistant replies are not stored as memories, so the reply is not sent.

## With plain OpenTelemetry / auto-instrumentation

The package registers the standard `opentelemetry_instrumentor` entry
point, so the OTel CLI picks it up with zero code changes:

```bash
opentelemetry-instrument python app.py
```

## What gets traced

`memorysync.{operation}` CLIENT spans for both `MemorySyncClient` and
`AsyncMemorySyncClient` (full sync/async parity):

- Memories: `add`, `add_turn`, `bulk_add`, `query`, `retrieve`, `recall`,
  `search_routed`, `get`, `update`, `forget`, `summarize`, `history`,
  `feedback`, `list_memories`.
- Conversation history: `append_history`, `list_history`,
  `delete_history`.
- Framework state: `put_state`, `get_state`, `list_state`,
  `search_state`, `delete_state`, `list_state_namespaces`.

The history and state methods exist in `memorysync` 1.10.0 and later; with
an older SDK installed, the memory operations are traced as usual.

Attributes always include the operation, sync/async client kind,
server address, project/tenant/user identifiers, result counts, score
min/max/avg, idempotency signals (`already_exists` on `add_turn`,
`created` / `created_count` on state and history writes), and error
type + HTTP status on failure. A `memorysync.client.operation.duration`
histogram is recorded per call.

`add_turn` sends one turn to fact extraction and does not store the turn
itself, so its span describes the extraction:

| Attribute | Value |
|---|---|
| `memorysync.role` | the `role` argument, when one is passed |
| `memorysync.processing_status` | `distilling`, `skipped_non_user_turn`, `skipped_low_value`, `skipped_replay`, or `skipped` (over the plan limit) |
| `memorysync.request_id` | the extraction job; absent when none was started |
| `memorysync.already_exists` | `true` when the server recognised a retry of the same turn |
| `memorysync.memory_id` | set only when a response carries one; `add_turn` answers `null` |

History and state spans record `tenant_id`, `user_id` and `source`, plus
(all names prefixed `memorysync.`):

| Span | Request | Response |
|---|---|---|
| `append_history` | `session_id`, `turns_count`, `extract` | `status`, `results_count`, `created_count`, `request_ids_count` |
| `list_history` | `session_id`, `limit` | `results_count` |
| `delete_history` | `selector` (`session` / `ids`), `session_id`, `ids_count`, `delete_derived_facts` | `deleted_count`, `deleted_facts_count` |
| `put_state` | `namespace`, `key`, `index`, `extract`, `replace_derived_facts` | `status`, `created`, `request_id`, `deleted_facts_count` |
| `get_state` | `namespace`, `key` | `results_count` (0 or 1) |
| `list_state` | `namespace_prefix`, `limit`, `offset` | `results_count` |
| `search_state` | `namespace_prefix`, `limit` | `results_count`, `score.min` / `score.max` / `score.avg` |
| `delete_state` | `namespace`, `selector` (`keys` / `namespace`), `keys_count`, `delete_derived_facts` | `deleted_count`, `deleted_facts_count` |
| `list_state_namespaces` | `namespace_prefix`, `max_depth`, `limit`, `offset` | `results_count` |

Namespaces are recorded as string arrays. Flags (`extract`, `index`,
`replace_derived_facts`, `delete_derived_facts`) are recorded when passed
explicitly. `status` is `skipped` when the organization is over its plan
limit and nothing was stored.

## Privacy: content capture is OFF by default

Memory content is customer data. By default **no memory text, query
text, recalled context, transcript turn, or state value is recorded** —
only counts, identifiers (tenant, user, session, namespace, key), scores,
and latencies. Opt in explicitly:

```python
instrument_memorysync(capture_content=True)
# or: MEMORYSYNC_OTEL_CAPTURE_CONTENT=true
```

Even opted in, input is truncated to 500 characters and at most 5
result texts of 200 characters each are recorded. For history and state
spans the input is the appended turns' text, the `put_state` text (the
value's JSON when no text is given) or the `search_state` query; the
results are the listed turns' text and the state items' searchable text.

## Engineering guarantees

- **Instrumentation can never break the app.** Attribute extraction is
  fully guarded and never consumes an argument (a generator passed as
  `ids`, `keys` or `turns` reaches the SDK intact); return values and
  exceptions pass through untouched; spans are always ended.
- **Zero stdout pollution** — the instrumentation never prints (the
  test suite asserts stdout stays empty during instrumented calls).
- **Idempotent** — `instrument_memorysync()` twice is safe;
  `uninstrument_memorysync()` restores the original methods.
- **Order independent** — OTel's proxy tracer late-binds, so
  instrumenting before your platform initializes still works.
- **Zero changes to the `memorysync` package** — methods are wrapped at
  runtime; nothing in the SDK itself is modified on disk.

## Docs

- AgentOps guide: https://docs.memorysync.io/guides/agentops
- Respan guide: https://docs.memorysync.io/guides/respan
