# Memory stores

> Reference for BaseStore, QdrantStore, Mem0Store, embedding classes, MemoryConfig, MemoryIntegration, preload helpers and store factories.

Source: https://10xgraph.com/docs/reference/python/memory-stores
Last updated: 2026-10-08

A memory store keeps facts, preferences and past interactions across conversation threads, so an agent can recall them later. This page documents the `tenxgraph.storage.store` API: embeddings, `QdrantStore`, `Mem0Store`, memory configuration, retrieval helpers and factories. A checkpointer, by contrast, saves state within one thread.

For the concepts and a walkthrough, see [Long-term memory](/docs/concepts/memory-and-store) and [Use a memory store](/docs/guides/use-memory-store).

## Import paths

```python
from tenxgraph.storage.store import (
    BaseStore, QdrantStore, Mem0Store,
    MemoryConfig, AgentMemoryConfig, UserMemoryConfig, MemoryIntegration,
    create_memory_preload_node, get_memory_system_prompt,
)
from tenxgraph.storage.store.store_schema import (
    MemoryType, RetrievalStrategy, DistanceMetric,
    MemorySearchResult, MemoryRecord,
)  # MemoryType, DistanceMetric and MemorySearchResult are also exported from tenxgraph.storage.store
from tenxgraph.storage.store.embedding import (
    BaseEmbedding, OpenAIEmbedding, GoogleEmbedding,
)
from tenxgraph.storage.store.long_term_memory import ReadMode  # also exported from tenxgraph.storage.store
```

Install the dependencies for the backends you use:

```bash
pip install openai              # OpenAIEmbedding
pip install google-genai        # GoogleEmbedding
pip install "10xgraph[qdrant]"  # QdrantStore (qdrant-client)
pip install "10xgraph[mem0]"    # Mem0Store (mem0ai)
```

---

## Enums

### `MemoryType`

| Value | Use case |
|---|---|
| `EPISODIC` | Conversation memories: what happened in a past chat. |
| `SEMANTIC` | Facts and general knowledge, such as "the user prefers dark mode". |
| `PROCEDURAL` | How-to knowledge, such as "to reset the password, go to Settings > Security". |
| `ENTITY` | Named entities and their attributes. |
| `RELATIONSHIP` | Connections between entities. |
| `DECLARATIVE` | Explicit facts and events stated by the user. |
| `CUSTOM` | Application-defined memory categories. |

### `RetrievalStrategy`

| Value | Description |
|---|---|
| `SIMILARITY` | Vector similarity search. The default in `asearch`. |
| `TEMPORAL` | Time-ordered retrieval, most recent first. |
| `RELEVANCE` | Relevance scoring combining recency and similarity. |
| `HYBRID` | Combines similarity and temporal signals. |
| `GRAPH_TRAVERSAL` | Navigates a knowledge graph to find connected memories. |

### `DistanceMetric`

| Value | Description |
|---|---|
| `COSINE` | Cosine similarity. Best for normalised embeddings. |
| `EUCLIDEAN` | Euclidean (L2) distance. Sensitive to vector magnitude. |
| `DOT_PRODUCT` | Inner product. Fast; requires normalisation to equal cosine. |
| `MANHATTAN` | L1 distance. Robust to outliers. |

### `ReadMode`

`ReadMode` is a string enum that controls how an agent reads long-term memory. Strings such as `"preload"` are accepted wherever a `ReadMode` is expected.

| Value | Description |
|---|---|
| `NO_RETRIEVAL` | Value `"no_retrieval"`. The model cannot read past memories. Writing stays available through `memory_tool` when you register it. |
| `PRELOAD` | Value `"preload"`. Relevant memories are retrieved and injected as a system message before the LLM call. |
| `POSTLOAD` | Value `"postload"`. The model decides when to search by calling a memory tool. |

---

## Embeddings

`QdrantStore` requires an embedding service to turn text into vectors for similarity search. Use a built-in class or subclass `BaseEmbedding`. Provider errors surface as `RuntimeError`.

### `BaseEmbedding`

Abstract base class for all embedding implementations. Subclasses must implement `aembed_batch`, `aembed` and the `dimension` property. The example below is a toy deterministic embedding that runs without any API key, useful for tests only.

```python title="custom_embedding.py"
import hashlib

from tenxgraph.storage.store.embedding import BaseEmbedding

class HashEmbedding(BaseEmbedding):
    """Toy embedding for tests: hashes text into a fixed-size vector."""

    async def aembed(self, text: str) -> list[float]:
        digest = hashlib.sha256(text.encode()).digest()
        return [b / 255 for b in digest]  # 32 floats

    async def aembed_batch(self, texts: list[str]) -> list[list[float]]:
        return [await self.aembed(t) for t in texts]

    @property
    def dimension(self) -> int:
        return 32

print(HashEmbedding().embed("hello")[:3])  # sync wrapper
```

| Member | Abstract | Description |
|---|---|---|
| `aembed_batch(texts)` | Yes | Async: embed a list of texts. Returns one vector per text. |
| `aembed(text)` | Yes | Async: embed one text. Returns a vector. |
| `dimension` | Yes (property) | Embedding size as an integer. The collection is created with this size. |
| `embed_batch(texts)` | No | Sync wrapper around `aembed_batch`. |
| `embed(text)` | No | Sync wrapper around `aembed`. |

### `OpenAIEmbedding`

Embedding service using OpenAI embedding models.

Requires: `pip install openai`

```python
from tenxgraph.storage.store.embedding import OpenAIEmbedding

# Uses text-embedding-3-small by default; reads OPENAI_API_KEY
embedding = OpenAIEmbedding()

# Or pass the model and key explicitly
embedding = OpenAIEmbedding(
    model="text-embedding-3-large",
    api_key="<your-openai-key>",
)
```

| Parameter | Type | Default | Description |
|---|---|---|---|
| `model` | `str` | `"text-embedding-3-small"` | OpenAI embedding model to use. |
| `api_key` | `str \| None` | None | OpenAI API key. If not provided, reads from `OPENAI_API_KEY` env var. |

**Raises:** `ImportError` if `openai` is not installed. `ValueError` if no key is found. The `dimension` property raises `ValueError` for a model name it does not know (known: `text-embedding-3-small`, `text-embedding-3-large`, `text-embedding-3-xl`, `text-embedding-4-base`, `text-embedding-4-large`), so wrap an unknown model in a custom `BaseEmbedding` subclass.

### `GoogleEmbedding`

Embedding service using Google Gemini embedding models.

Requires: `pip install google-genai`

```python
from tenxgraph.storage.store.embedding import GoogleEmbedding

# Uses gemini-embedding-001 by default; reads GOOGLE_API_KEY or GEMINI_API_KEY
embedding = GoogleEmbedding()

# Or pass the model, key and a reduced output size explicitly
embedding = GoogleEmbedding(
    model="gemini-embedding-001",
    api_key="<your-google-key>",
    output_dimensionality=768,  # optional
)
```

| Parameter | Type | Default | Description |
|---|---|---|---|
| `model` | `str` | `"gemini-embedding-001"` | Google embedding model to use. |
| `api_key` | `str \| None` | None | Google API key. Reads from `GOOGLE_API_KEY` or `GEMINI_API_KEY` env vars if not provided. |
| `output_dimensionality` | `int \| None` | None | Optional: reduce output dimension. Some models support this. |

**Raises:** `ImportError` if `google-genai` is not installed. `ValueError` if no key is found. `dimension` returns `output_dimensionality` when set, `3072` for `gemini-embedding-001`, and `768` for other models.

---

## Memory configuration

`MemoryConfig` is the object you pass to `Agent(..., memory=...)`. It sets the store, the retrieval mode and two optional scopes: user memory (the model can search and write) and agent memory (the model can only search). All three are pydantic models.

### `MemoryConfig`

Primary configuration object for `Agent(..., memory=...)`. A `str` retrieval mode is converted to `ReadMode`. `limit` must be at least 1 and `score_threshold` must not be negative, otherwise validation raises.

| Field | Type | Default | Description |
|---|---|---|---|
| `store` | `BaseStore \| None` | None | The memory store (QdrantStore, Mem0Store, etc.). Required if memory is enabled. |
| `retrieval_mode` | `ReadMode \| str` | `ReadMode.POSTLOAD` | How memories are retrieved: `NO_RETRIEVAL`, `PRELOAD`, or `POSTLOAD`. |
| `limit` | `int` | 5 | Maximum number of memories to retrieve per search. |
| `score_threshold` | `float` | 0.0 | Minimum similarity score for a memory to be returned. 0.0 = no threshold. |
| `max_tokens` | `int \| None` | None | Optional token budget for retrieved memory context. |
| `inject_system_prompt` | `bool` | True | Whether to include memory system instructions in the model's system prompt. |
| `config` | `dict[str, Any]` | {} | Application-defined config passed to store methods. |
| `user_memory` | `UserMemoryConfig \| None` | UserMemoryConfig() | Config for user-scoped memory (searchable and writable by model). |
| `agent_memory` | `AgentMemoryConfig \| None` | AgentMemoryConfig() | Config for agent/app-scoped memory (searchable but not writable by model). |

```python title="memory_config.py"
from tenxgraph.core import Agent
from tenxgraph.storage.store import MemoryConfig, OpenAIEmbedding, QdrantStore, ReadMode

store = QdrantStore(embedding=OpenAIEmbedding(), path="./qdrant_data")

memory = MemoryConfig(
    store=store,
    retrieval_mode=ReadMode.PRELOAD,  # inject memories before each model call
    limit=5,
    score_threshold=0.5,
    max_tokens=2000,
)

agent = Agent(model="gpt-4o", memory=memory)
```

> **POSTLOAD needs a ToolNode**
>
> With `ReadMode.POSTLOAD` (the default) the agent registers `user_memory_tool` and `agent_memory_tool` on its tool node. If the `Agent` has no `tool_node`, construction raises `RuntimeError`. Pass `tool_node=` or use `PRELOAD`.

### `UserMemoryConfig`

Configuration for user-scoped long-term memory. The model can both search and write to user memories. `memory_type` and `category` must not be empty, and `limit` must be at least 1 when set.

| Field | Type | Default | Description |
|---|---|---|---|
| `enabled` | `bool` | True | Whether user memory is enabled. |
| `store` | `BaseStore \| None` | None | The store instance (if different from the parent MemoryConfig). |
| `memory_type` | `str` | `"episodic"` | Default memory type when storing. One of the MemoryType enum values. |
| `category` | `str` | `"general"` | Default memory category when storing. |
| `limit` | `int \| None` | None | Max memories to retrieve. If None, uses the parent config limit. |
| `score_threshold` | `float \| None` | None | Min similarity score, must not be negative. If None, uses the parent config threshold. |
| `config` | `dict[str, Any]` | {} | Optional config dict passed to store calls. |
| `user_id` | `str \| None` | None | User identifier for scoping. Blank strings are rejected. |

### `AgentMemoryConfig`

Configuration for agent/app-scoped long-term memory. The model can search but not write to agent memories.

| Field | Type | Default | Description |
|---|---|---|---|
| `enabled` | `bool` | False | Whether agent memory is enabled (disabled by default). |
| `store` | `BaseStore \| None` | None | The store instance (if different from the parent MemoryConfig). |
| `memory_type` | `str` | `"episodic"` | Default memory type. |
| `category` | `str` | `"general"` | Default memory category. |
| `limit` | `int \| None` | None | Max memories to retrieve. |
| `score_threshold` | `float \| None` | None | Min similarity score. |
| `config` | `dict[str, Any]` | {} | Optional config dict. |
| `agent_id` | `str \| None` | None | Agent identifier for scoping. |
| `app_id` | `str \| None` | None | App identifier for scoping. |

---

## Memory retrieval functions

These helpers build the system prompt and the preload graph node by hand, for graphs that do not use `Agent(memory=...)`. `MemoryIntegration` bundles them.

### `get_memory_system_prompt(mode)`

Returns a system prompt fragment for the given retrieval mode. All modes include write instructions because writing is always available.

```python
from tenxgraph.storage.store import get_memory_system_prompt

prompt_fragment = get_memory_system_prompt(mode="preload")
print(prompt_fragment[:80])  # add this text to your system prompt
```

| Parameter | Type | Default | Description |
|---|---|---|---|
| `mode` | `str` | `"no_retrieval"` | Retrieval mode: `"no_retrieval"`, `"preload"`, or `"postload"`. |

**Returns:** A string prompt fragment suitable for concatenation into a larger system prompt. Includes memory-specific instructions and write guidance. `mode="preload"` produces text that begins:

```text
You have been provided with long-term memory context from previous interactions. Use it to personalize your responses when relevant. The memory context appears as system messages labeled '[Long-term Memory Context]'.
```

`Agent(memory=...)` uses the related `get_agent_memory_system_prompt(memory_config)` instead, which describes `user_memory_tool` and `agent_memory_tool`.

### `create_memory_preload_node(store, ...)`

Factory function that returns a graph node for preloading memories. Wire it before your LLM node to retrieve relevant memories and inject them as a system message. The search ignores `thread_id`, so memories are shared across threads.

```python title="preload_wiring.py"
from tenxgraph.core import StateGraph
from tenxgraph.core.state import AgentState
from tenxgraph.storage.store import OpenAIEmbedding, QdrantStore, create_memory_preload_node

store = QdrantStore(embedding=OpenAIEmbedding(), path="./qdrant_data")

preload = create_memory_preload_node(store=store, limit=5, score_threshold=0.5)

graph = StateGraph(AgentState)
graph.add_node("memory_preload", preload)
graph.add_edge("memory_preload", "main_agent")  # "main_agent" is your LLM node
graph.set_entry_point("memory_preload")
```

| Parameter | Type | Default | Description |
|---|---|---|---|
| `store` | `BaseStore` | Required | The memory store instance. |
| `query_builder` | `Callable[[AgentState], str] \| None` | None | Custom function to extract the search query from state. Default: extracts the latest user message text. |
| `limit` | `int` | 5 | Maximum memories to retrieve. |
| `score_threshold` | `float` | 0.0 | Minimum similarity score. 0.0 = no threshold. |
| `memory_types` | `list[MemoryType] \| None` | None | If specified, search only these memory types (e.g., `[MemoryType.EPISODIC, MemoryType.SEMANTIC]`). |
| `system_prompt_template` | `str` | Built-in template | Template for the injected system message, filled with `str.format`. Must contain a `{memories}` placeholder. |
| `max_tokens` | `int \| None` | None | Optional token budget for retrieved context. |

**Returns:** An async node function `(state: AgentState, config: dict) -> list[Message]` that returns one system message, or an empty list if there is no user message, no match, or the search fails (the failure is logged).

### `MemoryIntegration`

`MemoryIntegration` is a single entry point that builds the preload node, system prompt and memory tools for a hand-built graph.

```python title="memory_integration.py"
from tenxgraph.core import StateGraph
from tenxgraph.core.state import AgentState
from tenxgraph.storage.store import MemoryIntegration, OpenAIEmbedding, QdrantStore

store = QdrantStore(embedding=OpenAIEmbedding(), path="./qdrant_data")
memory = MemoryIntegration(store=store, retrieval_mode="preload")

graph = StateGraph(AgentState)
memory.wire(graph, entry_to="main_agent")  # adds "memory_preload" and the entry point
system_prompt = memory.system_prompt       # add to your LLM node's prompt
tools = memory.tools                       # [memory_tool], register with ToolNode
```

| Parameter | Type | Default | Description |
|---|---|---|---|
| `store` | `BaseStore` | Required | The memory store. |
| `retrieval_mode` | `ReadMode \| str` | `ReadMode.NO_RETRIEVAL` | Retrieval mode. |
| `limit` | `int` | 5 | Maximum memories to retrieve. |
| `score_threshold` | `float` | 0.0 | Minimum similarity score. |
| `max_tokens` | `int \| None` | None | Token budget for retrieved context. |
| `query_builder` | `Callable[[AgentState], str] \| None` | None | Custom search query extractor. |
| `preload_prompt_template` | `str \| None` | None | Custom preload template with `{memories}`. |

Properties: `retrieval_mode`, `store`, `system_prompt`, `tools` and `preload_node` (None unless mode is preload). `wire(graph, entry_to, preload_node_name="memory_preload")` adds the preload node in preload mode and otherwise only sets `entry_to` as the entry point.

---

## `BaseStore`

Abstract base class that every store backend implements. Every method takes a `config` dict as its first argument; the store reads `user_id` (and, for some backends, `thread_id`, `agent_id` or `app_id`) from it.

### Core async methods

| Method | Signature | Description |
|---|---|---|
| `astore` | `async (config, content, memory_type=EPISODIC, category="general", metadata=None, **kwargs) -> str` | Add a memory. Returns the memory ID. |
| `asearch` | `async (config, query, memory_type=None, category=None, limit=10, score_threshold=None, filters=None, retrieval_strategy=SIMILARITY, distance_metric=COSINE, max_tokens=4000, **kwargs) -> list[MemorySearchResult]` | Search memories by query. |
| `aget` | `async (config, memory_id, **kwargs) -> MemorySearchResult \| None` | Fetch a memory by ID. |
| `aget_all` | `async (config, limit=100, **kwargs) -> list[MemorySearchResult]` | List memories in the config's scope. |
| `aupdate` | `async (config, memory_id, content, metadata=None, **kwargs) -> Any` | Update a memory's content. |
| `adelete` | `async (config, memory_id, **kwargs) -> Any` | Delete a memory by ID. |
| `aforget_memory` | `async (config, **kwargs) -> Any` | Delete memories for a user or agent scope. Accepted keyword arguments are store-specific. |
| `asetup` | `async () -> Any` | Create the collection or other backing resources. Implemented by `QdrantStore`; the base class raises `NotImplementedError`. |
| `arelease` | `async () -> None` | Release connections. Not abstract, but the base implementation raises `NotImplementedError`; `Mem0Store` overrides it. |

There is no bulk `abatch_store`; call `astore` in a loop or with `asyncio.gather`.

### Sync wrappers

```python
store.store(config, content)
store.search(config, query)
```

Each async method has a sync wrapper named without the `a` prefix (`store`, `search`, `get`, `get_all`, `update`, `delete`, `forget_memory`, `setup`, `release`). Use the async variants in async code.

### Config dictionary

```python
config = {
    "user_id": "alice",      # user scope (required by Mem0Store)
    "thread_id": "thread-1", # optional; QdrantStore reads it
}
```

---

## `MemorySearchResult`

Returned by `asearch`, `aget` and `aget_all`. Each result represents a matching memory.

| Field | Type | Description |
|---|---|---|
| `id` | `str` | Memory ID. |
| `content` | `str` | The memory text. |
| `score` | `float` | Similarity or relevance score, at least 0.0. |
| `memory_type` | `MemoryType` | Categorisation. |
| `metadata` | `dict` | Application-defined metadata. |
| `vector` | `list[float] \| None` | Embedding vector (if returned by backend). |
| `user_id` | `str \| None` | User scope. |
| `thread_id` | `str \| None` | Thread where this memory was created. |
| `timestamp` | `datetime \| None` | Creation time. |

---

## `QdrantStore`

Vector store backed by [Qdrant](https://qdrant.tech). Production-ready for similarity search.

> **Optional dependency**
>
> Install with `pip install "10xgraph[qdrant]"`.

```python title="qdrant_store.py"
import asyncio

from tenxgraph.storage.store import OpenAIEmbedding, QdrantStore

# Local Qdrant (persisted to disk)
store = QdrantStore(
    embedding=OpenAIEmbedding(),
    path="./qdrant_data",
)

# Remote Qdrant
store = QdrantStore(
    embedding=OpenAIEmbedding(),
    host="localhost",
    port=6333,
)

# Qdrant Cloud
store = QdrantStore(
    embedding=OpenAIEmbedding(),
    url="https://xyz.qdrant.io",
    api_key="<your-qdrant-api-key>",
)

asyncio.run(store.asetup())  # creates the collection; pass the store to graph.compile(store=store)
```

`path`, `url` and `host`/`port` are checked in that order: `path` wins, then `url`, otherwise `host` (default `localhost`) and `port` (default `6333`). Extra keyword arguments go to `AsyncQdrantClient`.

### Constructor parameters

| Parameter | Type | Description |
|---|---|---|
| `embedding` | `BaseEmbedding` | Embedding service used to vectorise text before storage and search. |
| `path` | `str \| None` | Local path for embedded Qdrant server. |
| `host` | `str \| None` | Remote Qdrant host. |
| `port` | `int \| None` | Remote Qdrant port (default: `6333`). |
| `url` | `str \| None` | Qdrant Cloud URL. |
| `api_key` | `str \| None` | Qdrant Cloud API key. |
| `collection` | `str \| None` | Qdrant collection name. Defaults to `DEFAULT_COLLECTION`, which is `"agentflow_memories"`. |
| `distance_metric` | `DistanceMetric` | Distance metric for the collection. Default: `COSINE`. |
| `**kwargs` | `Any` | Passed to `AsyncQdrantClient`. |

---

## `Mem0Store`

Managed long-term memory using the [mem0](https://mem0.ai) library. Delegates all vector storage and memory management to Mem0.

> **Optional dependency**
>
> Install with `pip install "10xgraph[mem0]"`.

```python title="mem0_store.py"
from tenxgraph.storage.store import Mem0Store

store = Mem0Store(config={
    "llm": {"provider": "openai", "config": {"model": "gpt-4o-mini"}},
    "embedder": {"provider": "openai", "config": {"model": "text-embedding-3-small"}},
    "vector_store": {"provider": "qdrant", "config": {"host": "localhost", "port": 6333}},
})
# pass the store to graph.compile(store=store); no asetup() call is needed
```

| Parameter | Type | Default | Description |
|---|---|---|---|
| `config` | `mem0 MemoryConfig \| dict` | Required | Mem0 configuration. A dict goes through `AsyncMemory.from_config`. |
| `app_id` | `str \| None` | `"agentflow_app"` | App identifier. A per-call `config["app_id"]` overrides it. |

`Mem0Store` delegates to Mem0's `AsyncMemory` client, created lazily on first use. Every call needs `user_id` in the `config` dict, otherwise it raises `ValueError`. `asetup` is not implemented for `Mem0Store`, so do not call it. `aforget_memory` deletes all memories for the user.

---

## Store factory functions

Convenience functions to create and configure store instances with common patterns.

### `create_local_qdrant_store(path, embedding, collection=...)`

Create a `QdrantStore` with a local embedded Qdrant server.

```python
from tenxgraph.storage.store import create_local_qdrant_store, OpenAIEmbedding

store = create_local_qdrant_store(
    embedding=OpenAIEmbedding(),
    path="./qdrant_data",
)
```

| Parameter | Type | Description |
|---|---|---|
| `embedding` | `BaseEmbedding` | The embedding service. |
| `path` | `str` | Local directory path for Qdrant persistence. |
| `collection` | `str` | Collection name. Default `"agentflow_memories"`. |
| `**kwargs` | `Any` | Passed to `QdrantStore`. |

**Returns:** A configured `QdrantStore` instance.

### `create_remote_qdrant_store(host, port, embedding, collection=...)`

Create a `QdrantStore` connected to a remote Qdrant server.

```python
from tenxgraph.storage.store import create_remote_qdrant_store, OpenAIEmbedding

store = create_remote_qdrant_store(
    embedding=OpenAIEmbedding(),
    host="localhost",
    port=6333,
)
```

| Parameter | Type | Description |
|---|---|---|
| `embedding` | `BaseEmbedding` | The embedding service. |
| `host` | `str` | Remote host address. |
| `port` | `int` | Remote server port. Required (typically 6333). |
| `collection` | `str` | Collection name. Default `"agentflow_memories"`. |
| `**kwargs` | `Any` | Passed to `QdrantStore`. |

**Returns:** A configured `QdrantStore` instance.

### `create_cloud_qdrant_store(url, api_key, embedding, collection=...)`

Create a `QdrantStore` connected to Qdrant Cloud.

```python
from tenxgraph.storage.store import create_cloud_qdrant_store, OpenAIEmbedding

store = create_cloud_qdrant_store(
    embedding=OpenAIEmbedding(),
    url="https://xyz.qdrant.io",
    api_key="<your-qdrant-api-key>",
)
```

| Parameter | Type | Description |
|---|---|---|
| `embedding` | `BaseEmbedding` | The embedding service. |
| `url` | `str` | Qdrant Cloud cluster URL. |
| `api_key` | `str` | Qdrant Cloud API key. |
| `collection` | `str` | Collection name. Default `"agentflow_memories"`. |
| `**kwargs` | `Any` | Passed to `QdrantStore`. |

**Returns:** A configured `QdrantStore` instance.

### `create_mem0_store(config, user_id="default_user", app_id="agentflow_app")`

Create a `Mem0Store` with custom configuration.

```python
from tenxgraph.storage.store import create_mem0_store

store = create_mem0_store(config={
    "llm": {"provider": "openai", "config": {"model": "gpt-4o-mini"}},
    "embedder": {"provider": "openai", "config": {"model": "text-embedding-3-small"}},
    "vector_store": {"provider": "qdrant", "config": {"host": "localhost", "port": 6333}},
})
```

| Parameter | Type | Description |
|---|---|---|
| `config` | `dict` | Mem0 configuration with llm, embedder, and vector_store sections. |
| `user_id` | `str` | Accepted but not used by `Mem0Store`; pass `user_id` in each call's `config`. |
| `app_id` | `str` | Default app identifier. |

**Returns:** A configured `Mem0Store` instance.

### `create_mem0_store_with_qdrant(qdrant_url, ...)`

Create a `Mem0Store` backed by a Qdrant server, with OpenAI as the default LLM and embedder provider.

```python title="mem0_qdrant.py"
from tenxgraph.storage.store import create_mem0_store_with_qdrant

store = create_mem0_store_with_qdrant(
    qdrant_url="http://localhost:6333",
    llm_model="gpt-4o-mini",
)
```

| Parameter | Type | Default | Description |
|---|---|---|---|
| `qdrant_url` | `str` | Required | Qdrant server URL. |
| `qdrant_api_key` | `str \| None` | None | Qdrant API key. |
| `collection_name` | `str` | `"agentflow_memories"` | Qdrant collection used by Mem0. |
| `embedding_model` | `str` | `"text-embedding-ada-002"` | Embedder model name. |
| `llm_model` | `str` | `"gpt-4o-mini"` | LLM model name. |
| `app_id` | `str` | `"agentflow_app"` | App identifier. |
| `**kwargs` | `Any` | | Optional `vector_store_config`, `embedder_provider`, `embedder_config`, `llm_provider`, `llm_config`. |

**Returns:** A configured `Mem0Store` instance.

---|---|---|
| `embedding` | `BaseEmbedding` | The embedding service. |
| `llm_model` | `str` | OpenAI model to use for Mem0. |
| `qdrant_path` | `str` | Local path for the embedded Qdrant server. |

**Returns:** A configured `Mem0Store` instance.

---

## Wiring into Agent memory

Pass a `MemoryConfig` to the `Agent` and the same store to `graph.compile`. With `PRELOAD` the agent searches memory itself before each call; with `POSTLOAD` it needs a `ToolNode` so the memory tools can be registered.

```python title="agent_with_memory.py"
from tenxgraph.core import Agent, StateGraph
from tenxgraph.core.state import AgentState
from tenxgraph.storage.store import MemoryConfig, OpenAIEmbedding, QdrantStore, ReadMode
from tenxgraph.utils.constants import END

store = QdrantStore(embedding=OpenAIEmbedding(), path="./qdrant_data")

agent = Agent(
    model="gpt-4o",
    memory=MemoryConfig(store=store, retrieval_mode=ReadMode.PRELOAD, limit=5),
)

graph = StateGraph(AgentState)
graph.add_node("MAIN", agent)
graph.set_entry_point("MAIN")
graph.add_edge("MAIN", END)

app = graph.compile(store=store)  # the store is injected into memory tools and nodes
```

Memory is scoped by the `user_id` you pass in the invoke `config`. See [Use a memory store](/docs/guides/use-memory-store) for a full run.

---

## Direct store usage in nodes

A node can call the store directly through dependency injection, for example to write custom memory logic.

```python title="remember_node.py"
from injectq import Inject

from tenxgraph.core.state import AgentState
from tenxgraph.storage.store import BaseStore
from tenxgraph.storage.store.store_schema import MemoryType, RetrievalStrategy

async def remember_node(
    state: AgentState,
    config: dict,
    store: BaseStore = Inject[BaseStore],
) -> list:
    text = state.context[-1].text()

    # Retrieve relevant memories
    memories = await store.asearch(
        config={"user_id": config.get("user_id")},
        query=text,
        memory_type=MemoryType.EPISODIC,
        retrieval_strategy=RetrievalStrategy.SIMILARITY,
        limit=5,
    )
    context = "\n".join(m.content for m in memories)  # use this in a prompt or state

    # Store the user's latest message as a memory
    await store.astore(
        config={"user_id": config.get("user_id")},
        content=text,
        memory_type=MemoryType.EPISODIC,
    )

    return []   # no new messages; state enriched by memories above
```

The `store` parameter is injected when you pass `store=` to `graph.compile()`.

---

## Common errors

| Error | Cause | Fix |
|---|---|---|
| `ImportError: qdrant-client package is required for QdrantStore` | `qdrant-client` is not installed. | `pip install "10xgraph[qdrant]"`. |
| `ImportError: mem0 package is required for Mem0Store` | `mem0ai` is not installed. | `pip install "10xgraph[mem0]"`. |
| `ValueError: OpenAI API key must be provided...` | No `api_key` and no `OPENAI_API_KEY`. | Set the variable or pass `api_key`. |
| `ValueError: user_id must be provided in config` | A `Mem0Store` call has no `user_id`. | Put `user_id` in the `config` dict. |
| `RuntimeError: Memory requires an existing ToolNode...` | `Agent(memory=...)` in `POSTLOAD` mode without a tool node. | Pass `tool_node=` or use `PRELOAD`. |
| `NotImplementedError` from `asetup` | Called `asetup()` on `Mem0Store`. | Only `QdrantStore` implements it. |
