> ## Documentation Index
> Fetch the complete documentation index at: https://docs.maximem.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# NeMo Agent Toolkit

> MemoryEditor implementation that backs NVIDIA NeMo Agent Toolkit workflows with Synap.

Plug Synap into NVIDIA NeMo Agent Toolkit (NAT) as a first-class `MemoryEditor`. NAT workflows that declare a memory backend in YAML (or instantiate one programmatically) now get persistent, semantically searchable, per-user memory.

<Note>
  Requires Python 3.11+.
</Note>

## Overview

This guide shows how to add Synap to a NAT workflow to build pipelines that:

* Persist `MemoryItem` objects across workflow executions
* Retrieve memories via semantic search inside any NAT step
* Be declared in NAT's YAML config without writing additional integration code

The Synap NAT integration ships three exports: the editor itself, a registration decorator, and a one-shot factory.

| Export                | Purpose                                                             |
| --------------------- | ------------------------------------------------------------------- |
| `SynapMemoryEditor`   | Implements `nat.memory.interfaces.MemoryEditor` for NAT workflows   |
| `@register_memory`    | Decorator that registers the editor under a YAML-referenceable name |
| `synap_memory_client` | Factory that builds a ready-to-use `SynapMemoryEditor` from config  |

## Setup

Install the package alongside NAT:

<CodeGroup>
  ```bash pip theme={null}
  pip install maximem-synap-nemo-agent-toolkit nat
  ```

  ```bash uv theme={null}
  uv add maximem-synap-nemo-agent-toolkit nat
  # pip-compatible (existing venv): uv pip install maximem-synap-nemo-agent-toolkit nat
  ```
</CodeGroup>

<Note>
  The pip package is `maximem-synap-nemo-agent-toolkit`, but the import drops the `maximem-` prefix and uses underscores: `from synap_nemo_agent_toolkit import ...`.
</Note>

Configure your API key. Generate one from the [Synap Dashboard](https://synap.maximem.ai).

```bash .env theme={null}
SYNAP_API_KEY=synap_your_key_here
```

Initialize the SDK once at application startup:

```python theme={null}
from maximem_synap import MaximemSynapSDK

sdk = MaximemSynapSDK()
await sdk.initialize()
```

Alternatively, use the `synap_memory_client` factory below to skip the SDK setup; it initializes Synap internally.

See [SDK Initialization](/sdk/initialization) for the full lifecycle and configuration options.

## Basic integration

The smallest useful integration constructs a `SynapMemoryEditor` and uses it directly:

```python theme={null}
# pip install maximem-synap-nemo-agent-toolkit nat
from maximem_synap import MaximemSynapSDK
from nat.memory.models import MemoryItem
from synap_nemo_agent_toolkit import SynapMemoryEditor

sdk = MaximemSynapSDK()
await sdk.initialize()

editor = SynapMemoryEditor(
    sdk=sdk,
    customer_id="acme",    # optional, required for B2B instances
    mode="accurate",       # "fast" or "accurate"
)

# Store memories: user_id travels on the item, not the editor
await editor.add_items([
    MemoryItem(user_id="alice", memory="Prefers concise bullet-point summaries", tags=["preference"]),
    MemoryItem(user_id="alice", memory="Working on Q3 roadmap planning", tags=["project"]),
])

# Search memories
results = await editor.search("communication preferences", top_k=5, user_id="alice")
for item in results:
    print(item.memory, item.score)
```

Notice that `user_id` is supplied per item and per query: a single `SynapMemoryEditor` instance serves all users in the workflow. `customer_id` is set once at construction.

***

## Core concepts

### MemoryEditor interface

`SynapMemoryEditor` implements the full `MemoryEditor` protocol. NAT workflows that accept a `MemoryEditor` work without modification:

```python theme={null}
editor = SynapMemoryEditor(sdk=sdk, customer_id="acme", mode="accurate")
```

| Method                          | Behavior                                          |
| ------------------------------- | ------------------------------------------------- |
| `add_items(items)`              | Batch-ingest `MemoryItem` objects into Synap      |
| `search(query, top_k, user_id)` | Semantic search; returns scored `MemoryItem` list |
| `update_items(items)`           | Update existing memories by ID                    |
| `get_items(user_id, limit)`     | Retrieve all memories for a user                  |

The two retrieval modes trade latency against comprehensiveness:

|          | `fast`                                         | `accurate`                                              |
| -------- | ---------------------------------------------- | ------------------------------------------------------- |
| Search   | Vector + graph (no LLM subquery decomposition) | Vector + graph + LLM subquery decomposition + reranking |
| Best for | Real-time chat                                 | Multi-entity queries                                    |

`fast` is lower-latency and suited to the hot path; `accurate` adds LLM-driven query decomposition and reranking for relationship-aware queries at a higher latency cost.

**Reads degrade gracefully**: `search` and `get_items` return empty results and log an error on Synap outages. **Writes surface failures**: `add_items` and `update_items` raise `SynapIntegrationError` so workflows know if persistence failed.

### Registration for YAML configs

NAT lets you declare memory backends in YAML. Use `@register_memory` to make `SynapMemoryEditor` resolvable by name:

```python theme={null}
from synap_nemo_agent_toolkit import register_memory, SynapMemoryEditor

@register_memory("synap")
class _RegisteredSynap(SynapMemoryEditor):
    pass
```

After registration, reference it in any NAT workflow config:

```yaml theme={null}
memory:
  type: synap
  config:
    api_key: ${SYNAP_API_KEY}
    mode: accurate
    customer_id: acme
```

NAT resolves `type: synap` to the registered class and instantiates it with the `config` block.

### Factory function

For programmatic setups outside YAML, `synap_memory_client` builds a ready-to-use editor and initializes the SDK internally, with no separate lifecycle to manage:

```python theme={null}
import os
from synap_nemo_agent_toolkit import synap_memory_client

editor = synap_memory_client(
    api_key=os.environ["SYNAP_API_KEY"],
    customer_id="acme",
    mode="accurate",
)
```

Use this when you want a single function call to produce a configured editor; especially useful in scripts and notebooks.

***

## Complete example: NAT workflow with persistent memory

The pattern below sets up a workflow with Synap-backed memory at startup, ingests a batch of memories, and runs a recall query:

```python theme={null}
from nat.memory.models import MemoryItem
from synap_nemo_agent_toolkit import SynapMemoryEditor


async def setup_memory(sdk, customer_id: str | None = None) -> SynapMemoryEditor:
    editor = SynapMemoryEditor(sdk=sdk, customer_id=customer_id, mode="accurate")

    # Seed the editor with starting memories for known users
    await editor.add_items([
        MemoryItem(user_id="alice", memory="Lead engineer on Project Phoenix", tags=["role"]),
        MemoryItem(user_id="alice", memory="Prefers email for async updates", tags=["preference"]),
        MemoryItem(user_id="bob", memory="QA lead, focuses on flaky tests", tags=["role"]),
    ])

    return editor


async def recall(editor: SynapMemoryEditor, user_id: str, query: str) -> list[str]:
    results = await editor.search(query, top_k=5, user_id=user_id)
    return [r.memory for r in results]


# Usage
editor = await setup_memory(sdk, customer_id="acme")
alice_prefs = await recall(editor, user_id="alice", query="how does alice prefer to be contacted?")
# → ["Prefers email for async updates", ...]
```

Three things to notice in this pattern:

1. **One editor, many users.** `user_id` travels on each `MemoryItem` and each `search` call, so the editor is shared.
2. **`customer_id` is the tenant boundary.** All users sharing an editor are inside the same `customer_id`, so build a separate editor per tenant for multi-tenant services.
3. **Mode is fixed at construction.** Set `mode="fast"` for low-latency NAT steps; `"accurate"` for higher-recall lookups.

***

## Advanced patterns

### Multi-tenant scoping

`SynapMemoryEditor` takes `customer_id` at construction; `user_id` is supplied per call. `customer_id` is required on B2B Synap instances and ignored on single-tenant ones. See [Memory Scopes](/concepts/memory-scopes).

```python theme={null}
# Single-tenant
editor = SynapMemoryEditor(sdk=sdk)

# Multi-tenant: one editor per customer
editor_acme = SynapMemoryEditor(sdk=sdk, customer_id="acme")
editor_initech = SynapMemoryEditor(sdk=sdk, customer_id="initech")
```

### Choosing between SDK-managed and factory-managed lifecycles

* **Use `SynapMemoryEditor(sdk=...)`** when your application owns the SDK lifecycle (recommended for production, since you control init/shutdown).
* **Use `synap_memory_client(api_key=...)`** for scripts, notebooks, or YAML-driven workflows where you'd rather not manage the SDK explicitly.

### Failure semantics

The integration follows the Synap-wide contract:

* **`search` and `get_items` degrade gracefully**: return empty lists and log an error if Synap is unreachable.
* **`add_items` and `update_items` surface failures**: raise `SynapIntegrationError` so the workflow and caller know persistence failed.

This is by design: read failures shouldn't break a workflow step mid-flight, but silent write failures would corrupt the memory pool.

***

## Going further

* [Patterns overview](/patterns/overview): reusable memory patterns across frameworks.
* [Cookbook overview](/cookbook/overview): end-to-end worked examples.

***

## Next steps

<CardGroup cols={2}>
  <Card title="Semantic Kernel" icon="microsoft" href="/integrations/semantic-kernel">
    Plugin for Microsoft Semantic Kernel.
  </Card>

  <Card title="Pydantic AI" icon="shield-check" href="/integrations/pydantic-ai">
    Type-safe deps and tools for Pydantic AI.
  </Card>

  <Card title="Memory Scopes" icon="layer-group" href="/concepts/memory-scopes">
    How `user_id` and `customer_id` interact across reads and writes.
  </Card>

  <Card title="Ingestion" icon="upload" href="/sdk/ingestion">
    Direct ingestion API for pipelines that need finer control than `add_items`.
  </Card>
</CardGroup>
