> ## 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.

# LlamaIndex

> BaseMemory implementation and semantic retriever for LlamaIndex pipelines.

Give LlamaIndex chat engines and RAG pipelines persistent memory backed by Synap. Conversations survive restarts, and Synap-stored memories sit alongside your document retrieval as first-class context.

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

## Overview

This guide shows how to add Synap to a LlamaIndex application to build pipelines that:

* Maintain chat history across sessions and processes
* Retrieve user-scoped memories alongside document chunks in a RAG flow
* Fuse memory-based and document-based retrieval into a single ranked result set

The Synap LlamaIndex integration ships two drop-in components. Each one implements a native LlamaIndex interface, so you can use it anywhere a vanilla LlamaIndex memory or retriever is accepted.

| Component         | LlamaIndex interface | Purpose                                           |
| ----------------- | -------------------- | ------------------------------------------------- |
| `SynapChatMemory` | `BaseMemory`         | Persistent chat history per `conversation_id`     |
| `SynapRetriever`  | `BaseRetriever`      | Returns `NodeWithScore` objects for RAG pipelines |

## Setup

Install the package alongside LlamaIndex:

<CodeGroup>
  ```bash pip theme={null}
  pip install maximem-synap-llamaindex llama-index llama-index-llms-openai
  ```

  ```bash uv theme={null}
  uv add maximem-synap-llamaindex llama-index llama-index-llms-openai
  # pip-compatible (existing venv): uv pip install maximem-synap-llamaindex llama-index llama-index-llms-openai
  ```
</CodeGroup>

<Note>
  The pip package is `maximem-synap-llamaindex`, but the import drops the `maximem-` prefix and uses underscores: `from synap_llamaindex 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
OPENAI_API_KEY=your-openai-api-key
```

Initialize the SDK once at application startup:

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

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

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

## Basic integration

The smallest useful integration plugs `SynapChatMemory` into any LlamaIndex chat engine. Past turns are loaded automatically on each call, and new turns are persisted on the way out:

```python theme={null}
# pip install maximem-synap-llamaindex llama-index llama-index-llms-openai
import uuid
from maximem_synap import MaximemSynapSDK
from llama_index.core.chat_engine import CondensePlusContextChatEngine
from synap_llamaindex import SynapChatMemory

sdk = MaximemSynapSDK()
await sdk.initialize()

# conversation_id (and every other id) must be a valid UUID.
memory = SynapChatMemory(
    sdk=sdk,
    conversation_id=str(uuid.uuid4()),
    user_id="alice",
    customer_id="acme",   # optional, required for B2B instances
)

chat_engine = CondensePlusContextChatEngine.from_defaults(
    retriever=your_doc_retriever,
    memory=memory,
)

response = await chat_engine.achat("What were my action items from last week?")
```

`SynapChatMemory` loads prior messages on `get()` and writes new turns back to Synap on `put()`. **Failed reads return an empty buffer and log an error; failed writes surface explicitly** so callers know if persistence failed.

To make user-specific memories *retrievable* inside the chat engine (alongside or in place of documents), layer in `SynapRetriever` below.

***

## Core concepts

### Persistent chat memory

`SynapChatMemory` implements `BaseMemory`. Every LlamaIndex chat engine accepts a memory object: drop this one in to make the conversation durable:

```python theme={null}
from synap_llamaindex import SynapChatMemory

memory = SynapChatMemory(
    sdk=sdk,
    conversation_id=str(uuid.uuid4()),
    user_id="alice",
    customer_id="acme",
)
```

Each `conversation_id` maps one-to-one to a Synap conversation. Restart your process, instantiate `SynapChatMemory` again with the same `conversation_id`, and the chat engine resumes with the prior history.

### Semantic retrieval

`SynapRetriever` implements `BaseRetriever` and returns `NodeWithScore` objects, the same shape every LlamaIndex RAG component expects. Use it as the retriever of a `RetrieverQueryEngine`, or as a sub-retriever inside a `RouterRetriever` / `QueryFusionRetriever`:

```python theme={null}
from synap_llamaindex import SynapRetriever

retriever = SynapRetriever(
    sdk=sdk,
    user_id="alice",
    customer_id="acme",
    max_results=6,
    mode="accurate",   # "fast" (vector + graph, no LLM decomposition) or "accurate" (adds LLM subquery decomposition + reranking)
)

nodes = await retriever.aretrieve("What are the user's project preferences?")
# node.text = memory text
# node.score = relevance score
```

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. See [Context Fetch](/sdk/context-fetch) for the full retrieval contract.

***

## Complete example: support assistant with memory + docs

The following pipeline gives a chat engine both Synap-backed conversation memory *and* a fused retriever that blends user-specific memories with document chunks:

```python theme={null}
import uuid
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.core.chat_engine import CondensePlusContextChatEngine
from llama_index.core.retrievers import QueryFusionRetriever
from llama_index.llms.openai import OpenAI
from synap_llamaindex import SynapChatMemory, SynapRetriever


class SupportAssistant:
    def __init__(self, sdk, user_id: str, customer_id: str | None = None):
        self.sdk = sdk
        self.user_id = user_id
        self.customer_id = customer_id

        # Document retriever: your existing RAG corpus
        docs = SimpleDirectoryReader("./knowledge_base").load_data()
        doc_index = VectorStoreIndex.from_documents(docs)
        doc_retriever = doc_index.as_retriever(similarity_top_k=4)

        # Synap retriever: user-scoped memories
        memory_retriever = SynapRetriever(
            sdk=sdk,
            user_id=user_id,
            customer_id=customer_id,
            max_results=4,
            mode="fast",
        )

        # Fuse the two so a single retrieve call returns ranked results from both
        self.retriever = QueryFusionRetriever(
            retrievers=[doc_retriever, memory_retriever],
            similarity_top_k=6,
            num_queries=1,
        )

        self.llm = OpenAI(model="gpt-4o")

    async def ask(self, conversation_id: str, message: str) -> str:
        memory = SynapChatMemory(
            sdk=self.sdk,
            conversation_id=conversation_id,
            user_id=self.user_id,
            customer_id=self.customer_id,
        )

        chat_engine = CondensePlusContextChatEngine.from_defaults(
            retriever=self.retriever,
            memory=memory,
            llm=self.llm,
        )

        response = await chat_engine.achat(message)
        return str(response)


# Usage
assistant = SupportAssistant(sdk, user_id="alice", customer_id="acme")
reply = await assistant.ask(str(uuid.uuid4()), "Has my refund been processed?")
```

Three things to notice in this pattern:

1. **`SynapChatMemory`** is constructed per-conversation so multiple sessions can run side-by-side without interfering.
2. **`SynapRetriever`** is fused with the document retriever via `QueryFusionRetriever`, so user-specific facts and corpus documents come back as one ranked list.
3. **Memory and retrieval are independent.** Drop either and the pipeline still works; together they cover both the "what did we say" and "what do I know about this user" axes.

***

## Advanced patterns

### Multi-tenant scoping

Both components accept the same scoping triple: `user_id` (required), optional `customer_id`, optional `conversation_id`. `customer_id` is required on B2B Synap instances and ignored on single-tenant ones. See [Memory Scopes](/concepts/memory-scopes).

```python theme={null}
retriever = SynapRetriever(
    sdk=sdk,
    user_id="alice",
    customer_id="acme",   # scope retrievals to acme's tenancy
)
```

### Tuning retrieval mode per query

`SynapRetriever` takes a default `mode` at construction, but you can swap it temporarily for a single high-recall lookup:

```python theme={null}
retriever.mode = "accurate"
nodes = await retriever.aretrieve("Summarize everything about the Acme account.")
retriever.mode = "fast"
```

### Composing with other retrievers

`SynapRetriever` is a regular `BaseRetriever`, so it composes cleanly with LlamaIndex's `RouterRetriever`, `QueryFusionRetriever`, or any custom retriever you build. Combine it with a document retriever (as in the example above), or route between Synap memories and a vector store based on the query.

### Failure semantics

The integration follows the Synap-wide contract:

* **Retrieval failures degrade gracefully:** `SynapRetriever.aretrieve` returns `[]` and logs an error
* **Memory reads degrade gracefully:** `SynapChatMemory.get` returns an empty buffer and logs an error
* **Memory writes surface failures:** `SynapChatMemory.put` raises `SynapIntegrationError` so callers know persistence failed

This is by design: read failures should never break a user-facing turn, while write failures must be visible to callers.

***

## 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="LangChain" icon="link-simple" href="/integrations/langchain">
    Memory, retriever, and tools for LangChain chains and agents.
  </Card>

  <Card title="Haystack" icon="layer-group" href="/integrations/haystack">
    Retriever and memory-writer pipeline components for Haystack.
  </Card>

  <Card title="Context Fetch" icon="search" href="/sdk/context-fetch">
    The retrieval API that powers `SynapRetriever`: modes, scopes, and response shapes.
  </Card>

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