Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions hindsight-docs/examples/integrations/langgraph.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
"""
Hindsight LangGraph integration examples.
These snippets are embedded in the documentation via CodeSnippet.
"""

# [docs:setup]
from hindsight_langgraph import create_hindsight_tools

# Uses the default API URL. Set HINDSIGHT_API_KEY env var to authenticate.
tools = create_hindsight_tools(bank_id="user-123")

# To connect to a self-hosted instance, pass the URL explicitly:
# tools = create_hindsight_tools(bank_id="user-123", hindsight_api_url="http://localhost:8888")
# [/docs:setup]

# [docs:react-agent]
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent

agent = create_react_agent(
ChatOpenAI(model="gpt-4o"),
tools=tools,
prompt=(
"You are a helpful assistant with long-term memory. "
"Use hindsight_retain to store important facts about the user. "
"Use hindsight_recall to search your memory before answering. "
"Use hindsight_reflect for thoughtful summaries of what you know."
),
)

result = await agent.ainvoke(
{"messages": [{"role": "user", "content": "Remember that I prefer dark mode"}]}
)
# [/docs:react-agent]

# [docs:memory-nodes]
from hindsight_langgraph import create_recall_node, create_retain_node
from langchain_core.messages import HumanMessage
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, MessagesState, START, END

recall = create_recall_node(
bank_id_from_config="user_id",
budget="mid",
max_results=5,
)
retain = create_retain_node(
bank_id_from_config="user_id",
tags=["source:auto"],
)


async def llm_node(state: MessagesState):
model = ChatOpenAI(model="gpt-4o")
response = await model.ainvoke(state["messages"])
return {"messages": [response]}


builder = StateGraph(MessagesState)
builder.add_node("recall", recall)
builder.add_node("llm", llm_node)
builder.add_node("retain", retain)

builder.add_edge(START, "recall")
builder.add_edge("recall", "llm")
builder.add_edge("llm", "retain")
builder.add_edge("retain", END)

graph = builder.compile()

result = await graph.ainvoke(
{"messages": [HumanMessage(content="What exercise should I do today?")]},
config={"configurable": {"user_id": "user-456"}},
)
# [/docs:memory-nodes]

# [docs:memory-instructions]
from hindsight_langgraph import memory_instructions

get_instructions = memory_instructions(
bank_id="user-123",
base_instructions="You are a helpful assistant with long-term memory.",
budget="mid",
max_results=5,
)

# Use in a LangChain chain (no graph needed)
instructions = await get_instructions()
response = await ChatOpenAI(model="gpt-4o").ainvoke(
[{"role": "system", "content": instructions}, {"role": "user", "content": "What do you know about me?"}]
)
# [/docs:memory-instructions]

# [docs:constructor-options]
tools = create_hindsight_tools(
bank_id="user-123",
budget="high",
max_tokens=2048,
tags=["env:prod", "app:support"],
recall_tags=["env:prod"],
recall_tags_match="any",
retain_metadata={"version": "2.0"},
retain_document_id="session-abc",
recall_types=["experience", "world"],
recall_include_entities=True,
reflect_context="The user is a senior engineer.",
include_retain=True,
include_recall=True,
include_reflect=True,
)
# [/docs:constructor-options]
64 changes: 39 additions & 25 deletions hindsight-integrations/langgraph/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ LangGraph and LangChain integration for [Hindsight](https://github.com/vectorize
Provides three integration patterns:
- **Tools** — retain/recall/reflect as LangChain `@tool` functions for agent-driven memory. Works with **both LangChain and LangGraph**.
- **Nodes** *(LangGraph)* — pre-built graph nodes for automatic memory injection and storage
- **BaseStore** *(LangGraph)* — drop-in `BaseStore` adapter for LangGraph's built-in memory system
- **Memory Instructions** — pre-fetch memories into a system prompt string. Works with **any LangChain model**, no graph needed.

## Prerequisites

- A running Hindsight instance ([self-hosted via Docker](https://github.com/vectorize-io/hindsight#quick-start) or [Hindsight Cloud](https://ui.hindsight.vectorize.io/signup))
- A [Hindsight Cloud](https://ui.hindsight.vectorize.io/signup) account or a [self-hosted](https://github.com/vectorize-io/hindsight#quick-start) Hindsight instance
- Python 3.10+

## Installation
Expand All @@ -23,13 +23,12 @@ pip install hindsight-langgraph
Bind Hindsight memory tools to your LangGraph agent so it can store and retrieve memories on demand.

```python
from hindsight_client import Hindsight
from hindsight_langgraph import create_hindsight_tools
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent

client = Hindsight(base_url="http://localhost:8888")
tools = create_hindsight_tools(client=client, bank_id="user-123")
# Set HINDSIGHT_API_KEY env var to authenticate
tools = create_hindsight_tools(bank_id="user-123")

agent = create_react_agent(
ChatOpenAI(model="gpt-4o"),
Expand All @@ -46,14 +45,11 @@ result = await agent.ainvoke(
Add recall and retain nodes to your graph for automatic memory injection before LLM calls and storage after responses.

```python
from hindsight_client import Hindsight
from hindsight_langgraph import create_recall_node, create_retain_node
from langgraph.graph import StateGraph, MessagesState, START, END

client = Hindsight(base_url="http://localhost:8888")

recall = create_recall_node(client=client, bank_id="user-123")
retain = create_retain_node(client=client, bank_id="user-123")
recall = create_recall_node(bank_id="user-123")
retain = create_retain_node(bank_id="user-123")

builder = StateGraph(MessagesState)
builder.add_node("recall", recall)
Expand All @@ -73,8 +69,8 @@ graph = builder.compile()
Use `bank_id_from_config` to resolve the bank per-request from the graph's config:

```python
recall = create_recall_node(client=client, bank_id_from_config="user_id")
retain = create_retain_node(client=client, bank_id_from_config="user_id")
recall = create_recall_node(bank_id_from_config="user_id")
retain = create_retain_node(bank_id_from_config="user_id")

# Bank ID resolved at runtime
result = await graph.ainvoke(
Expand All @@ -83,22 +79,25 @@ result = await graph.ainvoke(
)
```

## Quick Start: BaseStore
## Quick Start: Memory Instructions

Use Hindsight as a LangGraph `BaseStore` for cross-thread persistent memory with semantic search.
Pre-fetch memories and inject them into a system prompt. Works with any LangChain model — no graph needed.

```python
from hindsight_client import Hindsight
from hindsight_langgraph import HindsightStore

client = Hindsight(base_url="http://localhost:8888")
store = HindsightStore(client=client)
from hindsight_langgraph import memory_instructions
from langchain_openai import ChatOpenAI

graph = builder.compile(checkpointer=checkpointer, store=store)
get_instructions = memory_instructions(
bank_id="user-123",
base_instructions="You are a helpful assistant.",
)

# Store and search memories via the store API
await store.aput(("user", "123", "prefs"), "theme", {"value": "dark mode"})
results = await store.asearch(("user", "123", "prefs"), query="theme preference")
# Each call re-fetches memories, so it stays up to date
instructions = await get_instructions()
response = await ChatOpenAI(model="gpt-4o").ainvoke([
{"role": "system", "content": instructions},
{"role": "user", "content": "What do you know about me?"},
])
```

## Configuration
Expand All @@ -109,13 +108,28 @@ results = await store.asearch(("user", "123", "prefs"), query="theme preference"
from hindsight_langgraph import configure

configure(
hindsight_api_url="http://localhost:8888",
api_key="your-api-key", # or set HINDSIGHT_API_KEY env var
budget="mid",
tags=["source:langgraph"],
)
```

### Self-hosted instance

To connect to a self-hosted Hindsight instance instead of Hindsight Cloud:

```python
configure(
hindsight_api_url="http://localhost:8888",
)
```

Or pass `hindsight_api_url` directly to any factory function:

```python
tools = create_hindsight_tools(bank_id="user-123", hindsight_api_url="http://localhost:8888")
```

### Per-call overrides

All factory functions accept `client`, `hindsight_api_url`, and `api_key` to override the global config.
Expand All @@ -135,7 +149,7 @@ All factory functions accept `client`, `hindsight_api_url`, and `api_key` to ove
- Python 3.10+
- `langchain-core >= 0.3.0`
- `hindsight-client >= 0.4.0`
- `langgraph >= 0.3.0` *(only for nodes and store patterns — install with `pip install hindsight-langgraph[langgraph]`)*
- `langgraph >= 0.3.0` *(only for nodes pattern — install with `pip install hindsight-langgraph[langgraph]`)*

## Documentation

Expand Down
41 changes: 19 additions & 22 deletions hindsight-integrations/langgraph/hindsight_langgraph/__init__.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
"""Hindsight-LangGraph: Persistent memory for LangGraph and LangChain agents.

Provides Hindsight-backed tools, nodes, and a BaseStore adapter,
Provides Hindsight-backed tools, nodes, and a memory instructions helper,
giving agents long-term memory across conversations.

The **tools** pattern works with both LangChain and LangGraph — only
``langchain-core`` is required. The **nodes** and **store** patterns
require ``langgraph`` (install with ``pip install hindsight-langgraph[langgraph]``).
The **tools** and **memory_instructions** patterns work with both LangChain
and LangGraph — only ``langchain-core`` is required. The **nodes** pattern
requires ``langgraph`` (install with ``pip install hindsight-langgraph[langgraph]``).

Basic usage with tools (LangChain or LangGraph)::

from hindsight_client import Hindsight
from hindsight_langgraph import create_hindsight_tools

client = Hindsight(base_url="http://localhost:8888")
tools = create_hindsight_tools(client=client, bank_id="user-123")
# Uses Hindsight Cloud by default (set HINDSIGHT_API_KEY env var)
tools = create_hindsight_tools(bank_id="user-123")

# Or connect to a self-hosted instance:
# tools = create_hindsight_tools(bank_id="user-123", hindsight_api_url="http://localhost:8888")

# Bind tools to your model
model = ChatOpenAI(model="gpt-4o").bind_tools(tools)
Expand All @@ -31,12 +33,15 @@
builder.add_edge("recall", "agent")
builder.add_edge("agent", "retain")

Usage with BaseStore (requires langgraph)::
Usage with memory_instructions (LangChain, no graph needed)::

from hindsight_langgraph import HindsightStore
from hindsight_langgraph import memory_instructions

store = HindsightStore(client=client)
graph = builder.compile(checkpointer=checkpointer, store=store)
get_instructions = memory_instructions(
client=client, bank_id="user-123",
base_instructions="You are a helpful assistant.",
)
instructions = await get_instructions()
"""

from .config import (
Expand All @@ -46,7 +51,7 @@
reset_config,
)
from .errors import HindsightError
from .tools import create_hindsight_tools
from .tools import create_hindsight_tools, memory_instructions


def __getattr__(name: str):
Expand All @@ -60,15 +65,6 @@ def __getattr__(name: str):
) from None
return create_recall_node if name == "create_recall_node" else create_retain_node

if name == "HindsightStore":
try:
from .store import HindsightStore
except ImportError:
raise ImportError(
"HindsightStore requires langgraph. Install with: pip install hindsight-langgraph[langgraph]"
) from None
return HindsightStore

raise AttributeError(f"module 'hindsight_langgraph' has no attribute {name!r}")


Expand All @@ -81,11 +77,12 @@ def __getattr__(name: str):
"HindsightLangGraphConfig",
"HindsightError",
"create_hindsight_tools",
"memory_instructions",
]

try:
import langgraph # noqa: F401

__all__ += ["create_recall_node", "create_retain_node", "HindsightStore"]
__all__ += ["create_recall_node", "create_retain_node"]
except ImportError:
pass
Loading