Skip to main content

LangChain integration for Membrain — persistent semantic memory for AI agents

Project description

membrain-langchain

LangChain integration for Membrain — give your AI agents persistent, semantic long-term memory.

PyPI version Python License: MIT

Membrain is a semantic memory system backed by PostgreSQL and pgvector. It stores memories as atomic notes, links related ones automatically via an LLM-powered Guardian, and supports graph-native retrieval — letting your agents recall facts, relationships, and context across sessions.

This package wires Membrain into LangChain with proper primitives: a BaseChatMessageHistory, a BaseRetriever, agent tools, and a high-level MembrainMemory class.


Install

pip install membrain-langchain

# with OpenAI support
pip install "membrain-langchain[openai]"

# with uv
uv add membrain-langchain
uv add "membrain-langchain[openai]"

Quick Start

Get a Membrain API key at mem-brain.io, then:

import asyncio
from membrain_langchain import MembrainClient, MembrainMemory
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage, HumanMessage

client = MembrainClient(api_key="mb_live_...")
memory = MembrainMemory(client=client, user_id="user-123")
llm = ChatOpenAI(model="gpt-4o")

async def chat(user_message: str) -> str:
    # Fetch relevant memories and inject into the system prompt
    system_prompt = await memory.build_system_prompt(
        query=user_message,
        base_prompt="You are a helpful personal assistant.",
    )
    response = await llm.ainvoke([
        SystemMessage(content=system_prompt),
        HumanMessage(content=user_message),
    ])
    # Store the interaction for future recall
    await memory.store_interaction(user_message, response.content)
    return response.content

asyncio.run(chat("I love mani dum biryani"))
# Next message: the assistant already knows you love biryani

What is Membrain?

Feature Membrain Typical vector store
Memory model Atomic notes + semantic graph Document/chunk store
Relationship search Links are first-class searchable entities No
Smart merge Guardian auto-decides update vs create Manual
Interpreted search LLM summary of retrieved memories No
Graph operations Path-finding, hubs, neighborhood No

Classes

MembrainClient

The async HTTP client. All other classes use this internally.

from membrain_langchain import MembrainClient

client = MembrainClient(
    api_key="mb_live_...",
    base_url="https://mem-brain-api-cutover-v4-production.up.railway.app",  # default
    poll_interval=0.5,   # seconds between ingest job polls
    poll_timeout=30.0,   # max seconds to wait when wait=True
)

# Store a memory (waits for Guardian to complete linking)
result = await client.add_memory(
    content="User prefers dark roast coffee",
    scope=["user:user-123"],
    category="preference",
)

# Fire-and-forget (returns immediately after job is submitted)
await client.add_memory(content="...", wait=False)

# Semantic search
results = await client.search(query="coffee preferences", k=5, scope=["user:user-123"])

# Graph operations
await client.graph_neighborhood(memory_id="abc-123", hops=2)
await client.graph_hubs(limit=10)
await client.graph_path(from_id="abc", to_id="xyz")

# Stats
await client.get_stats()

MembrainMemory

High-level drop-in. Handles context retrieval and interaction storage automatically.

from membrain_langchain import MembrainMemory

memory = MembrainMemory(
    client=client,
    user_id="user-123",
    context_k=5,   # number of memories to inject per turn
)

# Get relevant memories as a formatted string
context = await memory.get_context("What coffee should I try?")

# Get a full system prompt with memory context appended
system_prompt = await memory.build_system_prompt(
    query="What coffee should I try?",
    base_prompt="You are a helpful assistant.",
)
# Returns: "You are a helpful assistant.\n\n## What you know about this user:\n- ..."

# Store a conversation turn (fire-and-forget)
await memory.store_interaction(user_msg="I love biryani", ai_msg="Noted!")

# Store an arbitrary fact (waits for confirmation)
await memory.add(content="User is vegetarian", category="dietary")

MembrainRetriever

A proper BaseRetriever subclass. Plugs into any RAG chain.

from membrain_langchain import MembrainRetriever
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser

retriever = MembrainRetriever(
    client=client,
    user_id="user-123",
    k=5,
    scope=None,           # defaults to ["user:{user_id}"]
    category=None,        # optional category filter
    include_related=True, # include linked neighbor memories
)

# Use in a RAG chain
chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)
answer = await chain.ainvoke("What does the user like to eat?")

Each result comes back as a Document with metadata:

Document(
    page_content="User loves mani dum biryani",
    metadata={
        "memory_id": "abc-123",
        "scope": ["user:user-123"],
        "semantic_score": 0.94,
        "related_memories": ["User is vegetarian"],
        "source": "membrain",
    }
)

Note: MembrainRetriever is async-only. Use chain.ainvoke() or await retriever.aget_relevant_documents(query).


MembrainChatMessageHistory

A proper BaseChatMessageHistory subclass for use with RunnableWithMessageHistory. Persists conversation turns in Membrain scoped by user and session.

from membrain_langchain import MembrainChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory

def get_history(session_id: str) -> MembrainChatMessageHistory:
    return MembrainChatMessageHistory(
        client=client,
        user_id="user-123",
        session_id=session_id,
        max_history=20,
    )

chain_with_history = RunnableWithMessageHistory(
    chain,
    get_history,
    input_messages_key="input",
    history_messages_key="history",
)

await chain_with_history.ainvoke(
    {"input": "What did I say earlier?"},
    config={"configurable": {"session_id": "session-abc"}},
)

Scope format: ["user:{user_id}", "session:{session_id}"]

  • Search with scope=["user:user-123"] — all sessions for a user (long-term memory)
  • Search with both scopes — isolated to one conversation

Note: Async-only. RunnableWithMessageHistory handles this automatically via ainvoke.


get_membrain_tools()

Returns LangChain StructuredTool objects for use in ReAct / tool-calling agents.

from membrain_langchain import get_membrain_tools
from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI

tools = get_membrain_tools(
    client=client,
    user_id="user-123",
    include_graph_tools=False,  # set True to add neighborhood + hubs tools
)

agent = create_react_agent(ChatOpenAI(model="gpt-4o"), tools)
result = await agent.ainvoke({
    "messages": [{"role": "user", "content": "What do you remember about me?"}]
})

Available tools:

Tool Description
membrain_search Semantic memory search
membrain_add Store a new memory
membrain_stats Memory system statistics
membrain_neighborhood Expand graph around a memory node (opt-in)
membrain_hubs Most-connected memory nodes (opt-in)

Patterns

Pattern A — MembrainMemory (automatic, always-on)

Retrieval and storage happen on every turn. The LLM receives memory as pre-injected context and never calls a tool.

User message → search Membrain → inject context → LLM → store interaction

Best for: simple chat apps, customer support bots, personal assistants.

Pattern B — Agent tools (LLM-driven)

The agent decides when to search, what to search for, and when to store new facts.

User message → Agent reasons → calls membrain_search → reasons → responds → optionally calls membrain_add

Best for: ReAct agents, research assistants, autonomous agents.

Pattern C — RAG pipeline

MembrainRetriever slots into a standard retrieval-augmented generation chain.

Question → retrieve relevant memories as Documents → LLM answers with context

Best for: knowledge-base Q&A, document assistants, domain-specific bots.


Async-only

All Membrain API calls are async. This maps cleanly to modern LangChain (which uses ainvoke, astream, etc.) and FastAPI.

Sync stubs (messages property, add_message, clear, get_relevant_documents) raise NotImplementedError with a clear message pointing to the async equivalent.


License

MIT


Links

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

membrain_langchain-0.1.0.tar.gz (11.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

membrain_langchain-0.1.0-py3-none-any.whl (14.5 kB view details)

Uploaded Python 3

File details

Details for the file membrain_langchain-0.1.0.tar.gz.

File metadata

  • Download URL: membrain_langchain-0.1.0.tar.gz
  • Upload date:
  • Size: 11.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.1 {"installer":{"name":"uv","version":"0.11.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for membrain_langchain-0.1.0.tar.gz
Algorithm Hash digest
SHA256 13ae55ecf652a6f83f4a4f3fb252ead54b50ae2ce41eab5e76515e0001f253cc
MD5 e4e5e04461cbef9febb06f073b212b36
BLAKE2b-256 b7eb0020d357ea10d4aa2b3185c118fd8585e1e7f47c5c41f88c32076384cead

See more details on using hashes here.

File details

Details for the file membrain_langchain-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: membrain_langchain-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 14.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.1 {"installer":{"name":"uv","version":"0.11.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for membrain_langchain-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 599c8aa7294e257676f418a0b61084cc046f24589d56eca6a9f377c85a1274d2
MD5 5bae7ae06a40dfec3a330b5a7e0c4efe
BLAKE2b-256 ac646aaf9f7cc5a7963be56e452fe9c325352cefda298124a6d8dc9f342dae1f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page