langgraph integration (#719)

This commit is contained in:
nexxeln 2026-02-03 01:00:59 +00:00
parent 60d6192f5d
commit 16da766fde
3 changed files with 428 additions and 0 deletions

View file

@ -153,6 +153,7 @@
"integrations/supermemory-sdk",
"integrations/ai-sdk",
"integrations/openai",
"integrations/langgraph",
"integrations/openai-agents-sdk",
"integrations/mastra",
"integrations/langchain",

View file

@ -0,0 +1 @@
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>LangGraph</title><path clip-rule="evenodd" d="M6.099 6H17.9C21.264 6 24 8.692 24 12s-2.736 6-6.099 6H6.1C2.736 18 0 15.308 0 12s2.736-6 6.099-6zm5.419 9.3c.148.154.367.146.561.106l.002.001c.09-.072-.038-.163-.16-.25-.074-.052-.145-.102-.166-.147.068-.08-.133-.265-.289-.408a1.52 1.52 0 01-.15-.148c-.11-.119-.155-.268-.2-.418-.03-.1-.06-.2-.11-.292-.304-.694-.653-1.383-1.143-1.97-.315-.39-.674-.74-1.033-1.09a19.384 19.384 0 01-.683-.688c-.226-.229-.362-.511-.499-.794-.114-.236-.228-.473-.396-.68-.507-.735-2.107-.936-2.342.104 0 .032-.01.052-.039.073-.13.094-.245.2-.342.327-.238.326-.274.877.022 1.17l.001-.019c.01-.147.02-.286.139-.391.228.193.576.262.841.117.32.45.422.995.525 1.54.085.456.17.912.382 1.316l.014.022c.124.203.25.41.41.587.059.089.178.184.297.279.157.125.314.25.329.359v.143c-.001.285-.002.58.184.813.103.205-.15.41-.352.385-.112.015-.233-.014-.354-.042-.165-.04-.329-.078-.462-.003-.038.04-.091.04-.145.042-.064.002-.129.004-.167.07-.008.019-.026.04-.045.063-.042.05-.087.105-.033.146l.015-.01c.082-.062.16-.12.27-.084-.014.08.039.102.092.123l.027.012a.344.344 0 01-.008.056c-.009.045-.017.088.018.127a.598.598 0 00.046-.054c.037-.046.073-.092.139-.11.144.19.289.111.471.013.206-.111.459-.248.81-.055-.135-.006-.255.01-.345.12-.023.024-.042.052-.002.084.207-.132.294-.085.375-.04.06.032.115.063.212.024l.07-.036c.155-.083.314-.166.499-.137-.139.039-.188.125-.242.218-.026.047-.054.095-.094.14-.021.021-.03.046-.007.08.29-.023.4-.095.548-.192.07-.046.15-.099.261-.154.124-.075.248-.027.368.02.13.05.255.098.371-.014.037-.033.083-.034.129-.034.016 0 .033 0 .05-.002-.037-.19-.24-.188-.448-.186-.24.003-.483.006-.475-.289.222-.149.224-.407.226-.651 0-.06 0-.117.005-.173.163.09.336.16.508.229.162.065.323.13.474.21.158.25.404.58.732.558.008-.026.016-.047.026-.073.019.004.039.008.059.014.086.02.178.044.223-.056zm6.429-2.829c.19.186.447.29.716.29.269 0 .526-.104.716-.29a.98.98 0 00.297-.7.98.98 0 00-.297-.7 1.024 1.024 0 00-1.08-.224l-.58-.831-.405.272.583.835a.978.978 0 00.05 1.348zm-1.817-2.69a1.03 1.03 0 001.056-.095.991.991 0 00.363-.507.97.97 0 00-.016-.62.994.994 0 00-.39-.488 1.028 1.028 0 00-1.298.14.987.987 0 00-.263.856.98.98 0 00.187.42c.095.125.218.225.36.294zm0 5.752a1.032 1.032 0 001.056-.095.991.991 0 00.363-.507.97.97 0 00-.016-.62.994.994 0 00-.39-.488 1.027 1.027 0 00-1.298.14.986.986 0 00-.263.856.98.98 0 00.187.42c.095.125.218.225.36.294zm.93-3.516v-.492h-1.55a.977.977 0 00-.217-.404l.584-.847-.425-.276-.583.847a1.023 1.023 0 00-1.047.23.973.973 0 00-.296.696c0 .261.107.512.296.696a1.023 1.023 0 001.047.23l.583.847.42-.276-.579-.847a.977.977 0 00.217-.404h1.55z"></path></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View file

@ -0,0 +1,426 @@
---
title: "LangGraph"
sidebarTitle: "LangGraph"
description: "Add persistent memory to LangGraph agents with Supermemory"
icon: "/images/langgraph.svg"
---
Build stateful agents with LangGraph that remember context across sessions. Supermemory handles memory storage and retrieval while LangGraph manages your graph-based conversation flow.
## Overview
This guide shows how to integrate Supermemory with LangGraph to create agents that:
- Maintain user context through automatic profiling
- Store and retrieve relevant memories at each node
- Use conditional logic to decide what's worth remembering
- Combine short-term (session) and long-term (cross-session) memory
## Setup
Install the required packages:
```bash
pip install langgraph langchain-openai supermemory python-dotenv
```
Configure your environment:
```bash
# .env
SUPERMEMORY_API_KEY=your-supermemory-api-key
OPENAI_API_KEY=your-openai-api-key
```
<Note>Get your Supermemory API key from [console.supermemory.ai](https://console.supermemory.ai).</Note>
## Basic integration
A minimal agent that fetches user context before responding and stores the conversation after:
```python
from typing import Annotated, TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage, HumanMessage
from supermemory import Supermemory
from dotenv import load_dotenv
load_dotenv()
llm = ChatOpenAI(model="gpt-4o")
memory = Supermemory()
class State(TypedDict):
messages: Annotated[list, add_messages]
user_id: str
def agent(state: State):
user_id = state["user_id"]
messages = state["messages"]
user_query = messages[-1].content
# Fetch user profile with relevant memories
profile_result = memory.profile(container_tag=user_id, q=user_query)
# Build context from profile
static_facts = profile_result.profile.static or []
dynamic_context = profile_result.profile.dynamic or []
search_results = profile_result.search_results.results if profile_result.search_results else []
context = f"""
User Background:
{chr(10).join(static_facts) if static_facts else 'No profile yet.'}
Recent Context:
{chr(10).join(dynamic_context) if dynamic_context else 'No recent activity.'}
Relevant Memories:
{chr(10).join([r.memory or r.chunk for r in search_results]) if search_results else 'None found.'}
"""
system = SystemMessage(content=f"You are a helpful assistant.\n\n{context}")
response = llm.invoke([system] + messages)
# Store the interaction
memory.add(
content=f"User: {user_query}\nAssistant: {response.content}",
container_tag=user_id
)
return {"messages": [response]}
# Build the graph
graph = StateGraph(State)
graph.add_node("agent", agent)
graph.add_edge(START, "agent")
graph.add_edge("agent", END)
app = graph.compile()
# Run it
result = app.invoke({
"messages": [HumanMessage(content="Hi! I'm working on a Python project.")],
"user_id": "user_123"
})
print(result["messages"][-1].content)
```
---
## Core concepts
### User profiles
Supermemory automatically builds user profiles from stored memories:
- **Static facts**: Long-term information (preferences, expertise, background)
- **Dynamic context**: Recent activity and current focus
```python
result = memory.profile(
container_tag="user_123",
q="optional search query" # Also returns relevant memories
)
print(result.profile.static) # ["User is a Python developer", "Prefers functional style"]
print(result.profile.dynamic) # ["Working on async patterns", "Debugging rate limiting"]
```
### Memory storage
Content you add gets processed into searchable memories:
```python
# Store a conversation
memory.add(
content="User asked about graph traversal. Explained BFS vs DFS.",
container_tag="user_123",
metadata={"topic": "algorithms", "type": "conversation"}
)
# Store a document
memory.add(
content="https://langchain-ai.github.io/langgraph/",
container_tag="user_123"
)
```
### Memory search
Search returns both extracted memories and document chunks:
```python
results = memory.search.memories(
q="graph algorithms",
container_tag="user_123",
search_mode="hybrid",
limit=5
)
for r in results.results:
print(r.memory or r.chunk, r.similarity)
```
---
## Complete example: support agent
A support agent that learns from past tickets and adapts to each user's technical level:
```python
from typing import Annotated, TypedDict, Optional
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.checkpoint.memory import MemorySaver
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage, HumanMessage
from supermemory import Supermemory
from dotenv import load_dotenv
load_dotenv()
class SupportAgent:
def __init__(self):
self.llm = ChatOpenAI(model="gpt-4o", temperature=0.3)
self.memory = Supermemory()
self.app = self._build_graph()
def _build_graph(self):
class State(TypedDict):
messages: Annotated[list, add_messages]
user_id: str
context: str
category: Optional[str]
def retrieve_context(state: State):
"""Fetch user profile and relevant past tickets."""
user_id = state["user_id"]
query = state["messages"][-1].content
result = self.memory.profile(
container_tag=user_id,
q=query,
threshold=0.5
)
static = result.profile.static or []
dynamic = result.profile.dynamic or []
memories = result.search_results.results if result.search_results else []
context = f"""
## User Profile
{chr(10).join(f"- {fact}" for fact in static) if static else "New user, no history."}
## Current Context
{chr(10).join(f"- {ctx}" for ctx in dynamic) if dynamic else "No recent activity."}
## Related Past Tickets
{chr(10).join(f"- {m.memory}" for m in memories[:3]) if memories else "No similar issues found."}
"""
return {"context": context}
def categorize(state: State):
"""Determine ticket category for routing."""
query = state["messages"][-1].content.lower()
if any(word in query for word in ["billing", "payment", "charge", "invoice"]):
return {"category": "billing"}
elif any(word in query for word in ["bug", "error", "broken", "crash"]):
return {"category": "technical"}
else:
return {"category": "general"}
def respond(state: State):
"""Generate a response using context."""
category = state.get("category", "general")
context = state.get("context", "")
system_prompt = f"""You are a support agent. Category: {category}
{context}
Guidelines:
- Match explanation depth to the user's technical level
- Reference past interactions when relevant
- Be direct and helpful"""
system = SystemMessage(content=system_prompt)
response = self.llm.invoke([system] + state["messages"])
return {"messages": [response]}
def store_interaction(state: State):
"""Save the ticket for future context."""
user_msg = state["messages"][-2].content
ai_msg = state["messages"][-1].content
category = state.get("category", "general")
self.memory.add(
content=f"Support ticket ({category}): {user_msg}\nResolution: {ai_msg[:300]}",
container_tag=state["user_id"],
metadata={"type": "support_ticket", "category": category}
)
return {}
# Build the graph
graph = StateGraph(State)
graph.add_node("retrieve", retrieve_context)
graph.add_node("categorize", categorize)
graph.add_node("respond", respond)
graph.add_node("store", store_interaction)
graph.add_edge(START, "retrieve")
graph.add_edge("retrieve", "categorize")
graph.add_edge("categorize", "respond")
graph.add_edge("respond", "store")
graph.add_edge("store", END)
checkpointer = MemorySaver()
return graph.compile(checkpointer=checkpointer)
def handle(self, user_id: str, message: str, thread_id: str) -> str:
"""Process a support request."""
config = {"configurable": {"thread_id": thread_id}}
result = self.app.invoke(
{"messages": [HumanMessage(content=message)], "user_id": user_id},
config=config
)
return result["messages"][-1].content
# Usage
if __name__ == "__main__":
agent = SupportAgent()
# First interaction
response = agent.handle(
user_id="customer_alice",
message="The API is returning 429 errors when I make requests",
thread_id="ticket_001"
)
print(response)
# Follow-up (agent remembers context)
response = agent.handle(
user_id="customer_alice",
message="I'm only making 10 requests per minute though",
thread_id="ticket_001"
)
print(response)
```
---
## Advanced patterns
### Conditional memory storage
Not everything is worth remembering. Use conditional edges to filter:
```python
def should_store(state: State) -> str:
"""Skip storing trivial messages."""
last_msg = state["messages"][-1].content.lower()
skip_phrases = ["thanks", "ok", "got it", "bye"]
if len(last_msg) < 20 or any(p in last_msg for p in skip_phrases):
return "skip"
return "store"
graph.add_conditional_edges("respond", should_store, {
"store": "store",
"skip": END
})
```
### Parallel memory operations
Fetch memories and categorize at the same time:
```python
from langgraph.graph import StateGraph, START, END
graph = StateGraph(State)
graph.add_node("retrieve", retrieve_context)
graph.add_node("categorize", categorize)
graph.add_node("respond", respond)
# Both run in parallel after START
graph.add_edge(START, "retrieve")
graph.add_edge(START, "categorize")
# Both must complete before respond
graph.add_edge("retrieve", "respond")
graph.add_edge("categorize", "respond")
graph.add_edge("respond", END)
```
### Metadata filtering
Organize memories by project, topic, or any custom field:
```python
# Store with metadata
memory.add(
content="User prefers detailed error messages with stack traces",
container_tag="user_123",
metadata={
"type": "preference",
"project": "api-v2",
"priority": "high"
}
)
# Search with filters
results = memory.search.memories(
q="error handling preferences",
container_tag="user_123",
filters={
"AND": [
{"key": "type", "value": "preference"},
{"key": "project", "value": "api-v2"}
]
}
)
```
### Combining session and long-term memory
LangGraph's checkpointer handles within-session state. Supermemory handles cross-session memory. Use both:
```python
from langgraph.checkpoint.memory import MemorySaver
# Session memory (cleared when thread ends)
checkpointer = MemorySaver()
app = graph.compile(checkpointer=checkpointer)
# Long-term memory (persists across sessions)
# Handled by Supermemory in your nodes
```
---
## Next steps
<CardGroup cols={2}>
<Card title="User profiles" icon="user" href="/user-profiles">
Deep dive into automatic user profiling
</Card>
<Card title="Search API" icon="search" href="/search">
Advanced search patterns and filtering
</Card>
<Card title="OpenAI SDK" icon="message-bot" href="/integrations/openai">
Native OpenAI integration with memory tools
</Card>
<Card title="AI SDK" icon="triangle" href="/integrations/ai-sdk">
Memory middleware for Next.js apps
</Card>
</CardGroup>