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

# LangChain Integration

> Add persistent memory to LangChain agents using Memanto.

# LangChain + Memanto

<img src="https://mintcdn.com/memanto/II1GP7bpRVzYh2QP/logo/integrations/langchain.svg?fit=max&auto=format&n=II1GP7bpRVzYh2QP&q=85&s=209836977dd1b5a3f0991868f40296a3" alt="LangChain" width="140" style={{marginBottom: "1.5rem"}} data-path="logo/integrations/langchain.svg" />

Add persistent, cross-session memory to your LangChain agents and chains using Memanto.

LangChain built-in memory classes reset between runs. Memanto plugs in as a custom memory backend that stores and retrieves context semantically so your chains remember what matters, even days later.

## How It Works

```
LangChain Chain / Agent -> MemantoMemory -> Memanto Server -> Moorcheh.ai
```

You drop `MemantoMemory` in wherever LangChain expects a `BaseMemory`. It handles session activation, storing new messages, and injecting recalled context into your prompts.

<Note>
  The Moorcheh API key (`MOORCHEH_API_KEY`) is configured on the **Memanto server**, not in your LangChain code. The only credential the client sends is `X-Session-Token`.
</Note>

## Prerequisites

* Python 3.8+
* [Moorcheh API key](https://console.moorcheh.ai/api-keys) configured on the Memanto server
* Memanto server running locally

## Install

```bash theme={null}
pip install memanto langchain langchain-openai httpx
```

## Step 1: Start Memanto Server

```bash theme={null}
export MOORCHEH_API_KEY=your_moorcheh_key
memanto serve
```

## Step 2: Create the Memory Class

Create `memanto_memory.py`:

```python theme={null}
import httpx
from langchain.memory import BaseMemory

class MemantoMemory(BaseMemory):
    """LangChain-compatible memory backend powered by Memanto."""

    agent_id: str = "langchain-agent"
    memanto_url: str = "http://localhost:8000"
    memory_key: str = "memory"
    session_token: str = ""

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self._client = httpx.Client()
        self._activate()

    def _activate(self):
        response = self._client.post(
            f"{self.memanto_url}/api/v2/agents/{self.agent_id}/activate"
        )
        response.raise_for_status()
        self.session_token = response.json()["session_token"]

    @property
    def _headers(self) -> dict:
        return {
            "X-Session-Token": self.session_token,
            "Content-Type": "application/json",
        }

    @property
    def memory_variables(self) -> list[str]:
        return [self.memory_key]

    def load_memory_variables(self, inputs: dict) -> dict:
        """Called before each LLM call - recalls relevant memories."""
        query = inputs.get("input", inputs.get("human_input", ""))
        if not query:
            return {self.memory_key: ""}

        response = self._client.post(
            f"{self.memanto_url}/api/v2/agents/{self.agent_id}/recall",
            headers=self._headers,
            json={"query": query, "limit": 5},
        )
        response.raise_for_status()
        memories = response.json().get("memories", [])
        if not memories:
            return {self.memory_key: ""}

        context = "\n".join(f"- {m['content']}" for m in memories)
        return {self.memory_key: f"Relevant memory:\n{context}"}

    def save_context(self, inputs: dict, outputs: dict) -> None:
        """Called after each LLM call - stores the conversation turn."""
        human = inputs.get("input", inputs.get("human_input", ""))
        ai = outputs.get("output", outputs.get("response", ""))

        if human:
            self._client.post(
                f"{self.memanto_url}/api/v2/agents/{self.agent_id}/remember",
                headers=self._headers,
                json={"content": f"User said: {human}", "type": "fact"},
            )
        if ai:
            self._client.post(
                f"{self.memanto_url}/api/v2/agents/{self.agent_id}/remember",
                headers=self._headers,
                json={"content": f"Assistant replied: {ai}", "type": "fact"},
            )

    def clear(self) -> None:
        pass  # Memories persist in Memanto - clear via CLI if needed
```

## Step 3: Use in a Chain

Create `agent.py`:

```python theme={null}
from langchain_openai import ChatOpenAI
from langchain.chains import ConversationChain
from langchain.prompts import PromptTemplate
from memanto_memory import MemantoMemory

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
memory = MemantoMemory(agent_id="my-assistant")

prompt = PromptTemplate(
    input_variables=["memory", "input"],
    template=(
        "You are a helpful assistant with long-term memory.\n\n"
        "{memory}\n\n"
        "Human: {input}\n"
        "Assistant:"
    ),
)

chain = ConversationChain(llm=llm, memory=memory, prompt=prompt, verbose=True)

# First run - Alice introduces herself
response = chain.invoke({"input": "My name is Alice and I prefer dark mode."})
print(response["output"])

# Second run - Memanto recalls that Alice prefers dark mode
response = chain.invoke({"input": "What UI settings should I use?"})
print(response["output"])
```

## Step 4: Run

```bash theme={null}
export OPENAI_API_KEY=sk_your_openai_key
# MOORCHEH_API_KEY is read by the Memanto server, not by this script.
python agent.py
```

## Using with LCEL (LangChain Expression Language)

Inject recalled memory directly into an LCEL pipeline:

```python theme={null}
import httpx
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableLambda

AGENT_ID = "lcel-agent"
BASE_URL = "http://localhost:8000/api/v2"

token = httpx.post(
    f"{BASE_URL}/agents/{AGENT_ID}/activate"
).json()["session_token"]

HEADERS = {"X-Session-Token": token, "Content-Type": "application/json"}

def recall_context(inputs: dict) -> dict:
    resp = httpx.post(
        f"{BASE_URL}/agents/{AGENT_ID}/recall",
        headers=HEADERS,
        json={"query": inputs["question"], "limit": 5},
    )
    memories = resp.json().get("memories", [])
    context = "\n".join(f"- {m['content']}" for m in memories) or "No prior context."
    return {**inputs, "context": context}

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant.\n\nMemory:\n{context}"),
    ("human", "{question}"),
])

chain = RunnableLambda(recall_context) | prompt | ChatOpenAI(model="gpt-4o-mini")

result = chain.invoke({"question": "What are my UI preferences?"})
print(result.content)
```

## Using Memanto's Built-in Answer (Optional)

For cases where you want a direct, grounded response from memory without routing through your chain, Memanto exposes an `answer` endpoint that uses its native RAG model. No external LLM call is made on your side.

This is useful as a quick lookup tool — for example, answering a simple factual question about a user before deciding whether to invoke the full chain.

```python theme={null}
import httpx

AGENT_ID = "my-assistant"
BASE_URL = "http://localhost:8000/api/v2"

token = httpx.post(
    f"{BASE_URL}/agents/{AGENT_ID}/activate"
).json()["session_token"]

HEADERS = {"X-Session-Token": token, "Content-Type": "application/json"}

def memanto_answer(question: str) -> str:
    """Get a synthesized answer from stored memories using Memanto's native RAG."""
    response = httpx.post(
        f"{BASE_URL}/agents/{AGENT_ID}/answer",
        headers=HEADERS,
        json={"question": question},
    )
    response.raise_for_status()
    return response.json().get("answer", "")

answer = memanto_answer("What UI preferences does Alice have?")
print(answer)
# -> "Alice prefers dark mode and concise responses."
```

You can also use this inside an LCEL chain as a conditional step — call `memanto_answer` first, and only invoke the full LLM if the memory answer is empty:

```python theme={null}
from langchain_core.runnables import RunnableLambda

def answer_or_recall(inputs: dict) -> dict:
    quick = memanto_answer(inputs["question"])
    if quick:
        return {**inputs, "context": f"Memory answer: {quick}"}
    resp = httpx.post(
        f"{BASE_URL}/agents/{AGENT_ID}/recall",
        headers=HEADERS,
        json={"query": inputs["question"], "limit": 5},
    )
    memories = resp.json().get("memories", [])
    context = "\n".join(f"- {m['content']}" for m in memories) or "No prior context."
    return {**inputs, "context": context}
```

> **When to use `answer` vs `recall`**
>
> * Use `recall` (via `load_memory_variables`) when your LLM should reason over the raw memories itself.
> * Use `answer` when you want a ready-made response from memory, or to short-circuit the chain for simple factual lookups.

## Persistent Memory Across Sessions

Memories stored via `save_context` survive process restarts and are available in future sessions for the same `agent_id`:

```bash theme={null}
# View stored memories
memanto recall "all context" --agent my-assistant

# Export to file
memanto memory export --agent my-assistant
```

## Next Steps

* [Remember API](/api-reference/data/remember)
* [Recall API](/api-reference/search/recall)
* [Memory Types Reference](/reference/memory-types)
* [Session Management](/guides/session-management)
