Memory stores

In shortReference for BaseStore, QdrantStore, Mem0Store, embedding classes, MemoryConfig, MemoryIntegration, preload helpers and store factories.

  • 17 min read
  • 13 sections
  • Updated
  • v0.10.0
  • Markdown

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 and Use a 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:

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

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

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.

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.

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. Production-ready for similarity search.

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 library. Delegates all vector storage and memory management to Mem0.

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.

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.

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

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.
Last updated for v0.10.0Edit this page on GitHubReport an issue