Add LangGraph integration guide (#271)

This commit is contained in:
ajspig 2025-11-19 17:13:46 -05:00 committed by GitHub
parent 2945f6bb3c
commit 2ffa7b7b30
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 1807 additions and 7 deletions

View File

@ -63,7 +63,11 @@
"groups": [
{
"group": "Getting Started",
"pages": ["v2/guides/overview", "v2/guides/mcp"]
"pages": ["v2/guides/overview"]
},
{
"group": "Integrations",
"pages": ["v2/integrations/langgraph", "v2/integrations/mcp"]
},
{
"group": "Application Interfaces",

View File

@ -14,17 +14,26 @@ Whether you're integrating Honcho into existing platforms, exploring advanced fe
Each spellbook focuses on a specific use case with working code you can adapt to your needs. The goal is to get you from idea to working prototype as quickly as possible, then provide the depth you need to scale and customize.
## Getting Started
Quick integration guides to get up and running:
<CardGroup cols={2}>
<Card title="MCP Integration" icon="link" href="/v2/integrations/mcp">
Get Honcho running with a single prompt in Claude Code
</Card>
<Card title="LangGraph" icon="diagram-project" href="/v2/integrations/langgraph">
Add persistent memory and theory of mind to your LangGraph agents
</Card>
</CardGroup>
## Application Interfaces
Ready-to-use integration patterns for popular platforms:
<CardGroup cols={3}>
<CardGroup cols={2}>
<Card title="Discord Bot" icon="discord" href="/v2/guides/discord">
Build a Discord bot that remembers users across conversations
</Card>
<Card title="Telegram Bot" icon="telegram" href="/v2/guides/telegram">
Create a Telegram bot with persistent user understanding
</Card>
<Card title="MCP" icon="link" href="/v2/guides/mcp">
Get Honcho running with a single prompt in Cursor or Claude Code
</Card>
</CardGroup>

View File

@ -0,0 +1,363 @@
---
title: "LangGraph"
icon: 'diagram-project'
description: "Build a stateful conversational AI agent with LangGraph and Honcho"
sidebarTitle: 'LangGraph'
---
Integrate Honcho with LangGraph to build a conversational AI agent that maintains memory across sessions. This guide shows you how to use Honcho's memory layer with LangGraph's orchestration.
<Note>
The full code is available on [GitHub](https://github.com/plastic-labs/honcho/tree/main/examples/langgraph) with examples in both [Python](https://github.com/plastic-labs/honcho/blob/main/examples/langgraph/python/main.py) and [TypeScript](https://github.com/plastic-labs/honcho/blob/main/examples/langgraph/typescript/main.ts)
</Note>
## What We're Building
We'll create a conversational agent that remembers and reasons over past exchanges with the user. Here's how the pieces fit together:
- **LangGraph** orchestrates the conversation flow
- **Honcho** stores messages and retrieves relevant context
- **Your LLM** generates responses using Honcho's formatted context
The key benefit: You don't manually manage conversation history, token limits, or message formatting. Honcho handles memory so you can focus on your agent's logic.
<Note>
This tutorial demonstrates a simple linear conversation flow to show
how Honcho integrates with LangGraph. For production applications,
you'll likely want to add LangGraph features like conditional routing,
tool calling, and multi-agent orchestration.
</Note>
## Setup
Install required packages:
<CodeGroup>
```bash Python (uv)
uv add honcho-ai langgraph langchain-core openai python-dotenv
```
```bash Python (pip)
pip install honcho-ai langgraph langchain-core openai python-dotenv
```
```bash TypeScript (npm)
npm install @honcho-ai/sdk @langchain/langgraph openai dotenv
```
```bash TypeScript (yarn)
yarn add @honcho-ai/sdk @langchain/langgraph openai dotenv
```
```bash TypeScript (pnpm)
pnpm add @honcho-ai/sdk @langchain/langgraph openai dotenv
```
</CodeGroup>
This tutorial uses OpenAI, but Honcho works with any LLM provider. Create a `.env` file with your API keys:
```bash
OPENAI_API_KEY=your_openai_key
```
<Note>
This tutorial uses the Honcho demo server at https://demo.honcho.dev which runs a small instance of Honcho on the latest version. For production, get your Honcho API key at [app.honcho.dev](https://app.honcho.dev). For local development, use `environment="local"`.
</Note>
## Initialize Clients
<CodeGroup>
```python Python
import os
from dotenv import load_dotenv
from typing_extensions import TypedDict
from honcho import Honcho, Peer, Session
from openai import OpenAI
from langgraph.graph import StateGraph, START, END
load_dotenv()
# Initialize Honcho
honcho = Honcho()
# Initialize OpenAI
llm = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
```
```typescript TypeScript
import * as dotenv from "dotenv";
import { Honcho, Peer, Session } from "@honcho-ai/sdk";
import OpenAI from "openai";
import { Annotation } from "@langchain/langgraph";
import { StateGraph, START, END } from "@langchain/langgraph";
import * as readline from "readline/promises";
dotenv.config();
// Initialize Honcho
const honcho = new Honcho({});
// Initialize OpenAI
const llm = new OpenAI({
apiKey: process.env.OPENAI_API_KEY
});
```
</CodeGroup>
## Define LangGraph State
Define your state schema to pass data through the graph. The state stores Honcho objects directly along with the current user message and assistant response.
<Note>
Before proceeding, it's important to understand Honcho's core concepts (`Peers` and `Sessions`). Review the [Honcho Architecture](/v2/documentation/core-concepts/architecture) to familiarize yourself with these primitives.
</Note>
<CodeGroup>
```python Python
class State(TypedDict):
user_message: str
assistant_response: str
user: Peer
assistant: Peer
session: Session
```
```typescript TypeScript
const StateAnnotation = Annotation.Root({
userMessage: Annotation<string>(),
assistantResponse: Annotation<string>(),
user: Annotation<Peer>(),
assistant: Annotation<Peer>(),
session: Annotation<Session>(),
});
type State = typeof StateAnnotation.State;
```
</CodeGroup>
## Build the LangGraph
Define your chatbot logic, using Honcho to retrieve conversation context. This function demonstrates how Honcho can store messages, retrieve context, and generate responses.
<CodeGroup>
```python Python
def chatbot(state: State):
user_message = state["user_message"]
# Get objects from state
user = state["user"]
assistant = state["assistant"]
session = state["session"]
# Step 1: Store the user's message in the session
# This adds it to Honcho's memory for future context retrieval
session.add_messages([user.message(user_message)])
# Step 2: Get context in OpenAI format with token limit
# get_context() retrieves relevant conversation history
# tokens=2000 limits the context to 2000 tokens to manage costs and fit within model limits
# to_openai() converts it to the format expected by OpenAI's API
messages = session.get_context(tokens=2000).to_openai(assistant=assistant)
# Step 3: Generate response using the context
response = llm.chat.completions.create(
model="gpt-5.1",
messages=messages
)
assistant_response = response.choices[0].message.content
# Step 4: Store assistant response in Honcho for future context
session.add_messages([assistant.message(assistant_response)])
return {"assistant_response": assistant_response}
```
```typescript TypeScript
async function chatbot(state: State) {
const userMessage = state.userMessage;
// Get objects from state
const user = state.user;
const assistant = state.assistant;
const session = state.session;
// Step 1: Store the user's message in the session
// This adds it to Honcho's memory for future context retrieval
await session.addMessages([user.message(userMessage)]);
// Step 2: Get context in OpenAI format with token limit
// getContext() retrieves relevant conversation history
// tokens: 2000 limits the context to 2000 tokens to manage costs and fit within model limits
// toOpenAI() converts it to the format expected by OpenAI's API
const messages = (await session.getContext({ tokens: 2000 })).toOpenAI(assistant);
// Step 3: Generate response using the context
const response = await llm.chat.completions.create({
model: "gpt-5.1",
messages: messages
});
const assistantResponse = response.choices[0].message.content!;
// Step 4: Store assistant response for future context
await session.addMessages([assistant.message(assistantResponse)]);
return { assistantResponse: assistantResponse };
}
```
</CodeGroup>
Now let's build the LangGraph:
<CodeGroup>
```python Python
graph = StateGraph(State) \
.add_node("chatbot", chatbot) \
.add_edge(START, "chatbot") \
.add_edge("chatbot", END) \
.compile()
```
```typescript TypeScript
const graph = new StateGraph(StateAnnotation)
.addNode("chatbot", chatbot)
.addEdge(START, "chatbot")
.addEdge("chatbot", END)
.compile();
```
</CodeGroup>
### Understanding get_context()
The [`get_context()`](/v2/documentation/core-concepts/features/get-context) method retrieves comprehensive conversation context and formats it for your LLM. It automatically:
- **Manages conversation history** - Tracks all messages and determines what's relevant
- **Respects token limits** - Stays within context window constraints without manual counting
- **Handles long conversations** - Combines recent detailed messages with summaries of older exchanges
- **Provides peer understanding** - Includes theory-of-mind representations and peer cards when requested
The `SessionContext` object always includes fields for messages, summaries, peer representations, and peer cards. By default, only `messages` and `summary` are populated. To populate peer-specific context, pass a `peer_target` parameter:
**Using `peer_target` for Context:**
- **Without `peer_perspective`**: Returns Honcho's omniscient view of `peer_target` (all observations and context)
- **With `peer_perspective`**: Returns what `peer_perspective` knows about `peer_target` (perspective-based observations and context)
That's it. Call `session.get_context().to_openai(assistant)` and you get properly formatted context tailored for your assistant.
<Tip>
**Adding System Prompts:** Since `get_context()` returns conversation messages, you can easily prepend custom system instructions. Just add your system prompt to the beginning of the messages array before sending it to your LLM: `[{"role": "system", "content": "..."}, ...context_messages]`.
</Tip>
<Note>
For more details on all available parameters, see [`get_context() documentation`](/v2/documentation/core-concepts/features/get-context)
</Note>
## Chat Loop
Now we'll create the main conversation function. To simplify logic, we initialize Honcho objects once per conversation and pass them through the LangGraph state.
The `run_conversation_turn` function initializes a Honcho `Session` and `Peer` objects, passes them to the LangGraph, and returns the assistant's response. By calling it repeatedly with the same `user_id` and in the same session, the chat builds context over time.
<Note>
**Production Usage:** Honcho accepts any nanoid-compatible string for `user_id` and `session_id`. You can use IDs directly from your authentication system (Auth0, Firebase, Clerk, etc.) and session management without modification.
This tutorial uses hardcoded values for simplicity.
</Note>
<CodeGroup>
```python Python
def run_conversation_turn(user_id: str, user_input: str, session_id: str | None = None):
if not session_id:
session_id = f"session_{user_id}"
# Initialize Honcho objects
user = honcho.peer(user_id)
assistant = honcho.peer("assistant")
session = honcho.session(session_id)
result = graph.invoke({
"user_message": user_input,
"user": user,
"assistant": assistant,
"session": session
})
return result["assistant_response"]
if __name__ == "__main__":
print("Welcome to the AI Assistant! How can I help you today?")
user_id = "test-user-123"
while True:
user_input = input("You: ")
if user_input.lower() in ['quit', 'exit']:
break
response = run_conversation_turn(user_id, user_input)
print(f"Assistant: {response}\n")
```
```typescript TypeScript
async function runConversationTurn(
userId: string,
userInput: string,
sessionId?: string
): Promise<string> {
if (!sessionId) {
sessionId = `session_${userId}`;
}
// Initialize Honcho objects
const user = await honcho.peer(userId);
const assistant = await honcho.peer("assistant");
const session = await honcho.session(sessionId);
const result = await graph.invoke({
userMessage: userInput,
user: user,
assistant: assistant,
session: session,
});
return result.assistantResponse;
}
// Interactive chat loop
async function main() {
console.log("Welcome to the AI Assistant! How can I help you today?");
const userId = "test-user-123";
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
while (true) {
const userInput = await rl.question("You: ");
if (userInput.toLowerCase() === "quit" || userInput.toLowerCase() === "exit") {
rl.close();
break;
}
const response = await runConversationTurn(userId, userInput);
console.log(`Assistant: ${response}\n`);
}
}
main();
```
</CodeGroup>
## Next Steps
Now that you have a working LangGraph integration with Honcho, you can:
- **Create custom [LangChain tools](https://docs.langchain.com/oss/python/langchain/tools#customize-tool-properties) for your agent** - to fully utilize Honcho's memory & context management features
- **Build a multi-agent LangGraph** where each agent is a Honcho `Peer` with its own memory
## Related Resources
<CardGroup cols={2}>
<Card title="Get Context" icon="messages" href="/v2/documentation/core-concepts/features/get-context">
Learn more about retrieving and formatting conversation context
</Card>
<Card title="MCP Integration" icon="star-of-life" href="/v2/integrations/mcp">
Use Honcho in Claude Desktop with MCP
</Card>
</CardGroup>

View File

@ -1,8 +1,8 @@
---
title: "Honcho MCP"
title: "Model Context Protocol (MCP)"
icon: 'star-of-life'
description: "Use Honcho in Claude Desktop"
sidebarTitle: 'MCP Integration'
sidebarTitle: 'MCP'
---
You can let Claude use Honcho to manage its own memory in the native desktop app by using the Honcho MCP integration! Follow these steps:

View File

@ -0,0 +1,85 @@
"""
LangGraph Integration with Honcho and OpenAI
This module demonstrates how to build a stateful conversational AI agent using
LangGraph for orchestration, OpenAI for the AI model, and Honcho for memory
management. It creates a chatbot that remembers conversations across sessions.
"""
import os
from dotenv import load_dotenv
from typing_extensions import TypedDict
from honcho import Honcho, Peer, Session
from openai import OpenAI
from langgraph.graph import StateGraph, START, END
load_dotenv()
honcho = Honcho()
llm = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
class State(TypedDict):
user_message: str
assistant_response: str
user: Peer
assistant: Peer
session: Session
def chatbot(state: State):
user_message = state["user_message"]
# Get objects from state
user = state["user"]
assistant = state["assistant"]
session = state["session"]
session.add_messages([user.message(user_message)])
# Get context in OpenAI format with token limit
# tokens=2000 limits the context to 2000 tokens to manage costs and fit within model limits
messages = session.get_context(tokens=2000).to_openai(assistant=assistant)
# Generate response
response = llm.chat.completions.create(
model="gpt-5.1",
messages=messages
)
assistant_response = response.choices[0].message.content
# Store assistant response
session.add_messages([assistant.message(assistant_response)])
return {"assistant_response": assistant_response}
graph = StateGraph(State) \
.add_node("chatbot", chatbot) \
.add_edge(START, "chatbot") \
.add_edge("chatbot", END) \
.compile()
def run_conversation_turn(user_id: str, user_input: str, session_id: str | None = None):
if not session_id:
session_id = f"session_{user_id}"
# Initialize Honcho objects
user = honcho.peer(user_id)
assistant = honcho.peer("assistant")
session = honcho.session(session_id)
result = graph.invoke({
"user_message": user_input,
"user": user,
"assistant": assistant,
"session": session
})
return result["assistant_response"]
if __name__ == "__main__":
print("Welcome to the AI Assistant! How can I help you today?")
user_id = "test-user-1234"
while True:
user_input = input("You: ")
if user_input.lower() in ['quit', 'exit']:
break
response = run_conversation_turn(user_id, user_input)
print(f"Assistant: {response}\n")

View File

@ -0,0 +1,13 @@
[project]
name = "honcho-langgraph-example"
version = "0.1.0"
description = "LangGraph integration with Honcho for stateful conversational AI"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"honcho-ai",
"langchain-core>=1.0.6",
"langgraph>=1.0.3",
"openai>=1.99.7",
"python-dotenv>=1.1.1",
]

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,179 @@
{
"lockfileVersion": 1,
"configVersion": 1,
"workspaces": {
"": {
"name": "honcho-langgraph-example",
"dependencies": {
"@honcho-ai/sdk": "^1.5.0",
"@langchain/langgraph": "^1.0.2",
"dotenv": "^17.2.3",
"openai": "^6.9.1",
"zod": "4.0.0",
},
"devDependencies": {
"@types/node": "^22.10.2",
"typescript": "^5.7.2",
},
},
},
"packages": {
"@cfworker/json-schema": ["@cfworker/json-schema@4.1.1", "", {}, "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og=="],
"@honcho-ai/core": ["@honcho-ai/core@1.5.1", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-lbYtMTcL2AxdcIl5ZKenogeTlVMnE7buJWvAFOCLp0yQxcezyA/R9FPvLz2UGRApLsiplLNWhftML+3QBjIIJA=="],
"@honcho-ai/sdk": ["@honcho-ai/sdk@1.5.0", "", { "dependencies": { "@honcho-ai/core": "^1.5.1", "@types/node": "^24.0.1", "zod": "4.0.0" } }, "sha512-1V3wnIxyoRw0oYDE1p6GG4vRpwnn0PkwYsb4z4NeJkmWaafS9So1G32+IPhm01i2s4RnxsU31kCVL07BRsqtUw=="],
"@langchain/core": ["@langchain/core@1.0.6", "", { "dependencies": { "@cfworker/json-schema": "^4.0.2", "ansi-styles": "^5.0.0", "camelcase": "6", "decamelize": "1.2.0", "js-tiktoken": "^1.0.12", "langsmith": "^0.3.64", "mustache": "^4.2.0", "p-queue": "^6.6.2", "p-retry": "4", "uuid": "^10.0.0", "zod": "^3.25.76 || ^4" } }, "sha512-rDSjXATujCdJlL+OJFfyZhEca8kLmqGr4W2ebJvSHiUgXEDqu/IOWC+ZWgoKKHkGOGFdVTqQ7Qi0j2RnYS9Qlg=="],
"@langchain/langgraph": ["@langchain/langgraph@1.0.2", "", { "dependencies": { "@langchain/langgraph-checkpoint": "^1.0.0", "@langchain/langgraph-sdk": "~1.0.0", "uuid": "^10.0.0" }, "peerDependencies": { "@langchain/core": "^1.0.1", "zod": "^3.25.32 || ^4.1.0", "zod-to-json-schema": "^3.x" }, "optionalPeers": ["zod-to-json-schema"] }, "sha512-syxzzWTnmpCL+RhUEvalUeOXFoZy/KkzHa2Da2gKf18zsf9Dkbh3rfnRDrTyUGS1XSTejq07s4rg1qntdEDs2A=="],
"@langchain/langgraph-checkpoint": ["@langchain/langgraph-checkpoint@1.0.0", "", { "dependencies": { "uuid": "^10.0.0" }, "peerDependencies": { "@langchain/core": "^1.0.1" } }, "sha512-xrclBGvNCXDmi0Nz28t3vjpxSH6UYx6w5XAXSiiB1WEdc2xD2iY/a913I3x3a31XpInUW/GGfXXfePfaghV54A=="],
"@langchain/langgraph-sdk": ["@langchain/langgraph-sdk@1.0.0", "", { "dependencies": { "p-queue": "^6.6.2", "p-retry": "4", "uuid": "^9.0.0" }, "peerDependencies": { "@langchain/core": "^1.0.1", "react": "^18 || ^19", "react-dom": "^18 || ^19" }, "optionalPeers": ["@langchain/core", "react", "react-dom"] }, "sha512-g25ti2W7Dl5wUPlNK+0uIGbeNFqf98imhHlbdVVKTTkDYLhi/pI1KTgsSSkzkeLuBIfvt2b0q6anQwCs7XBlbw=="],
"@types/node": ["@types/node@22.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ=="],
"@types/node-fetch": ["@types/node-fetch@2.6.13", "", { "dependencies": { "@types/node": "*", "form-data": "^4.0.4" } }, "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw=="],
"@types/retry": ["@types/retry@0.12.0", "", {}, "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="],
"@types/uuid": ["@types/uuid@10.0.0", "", {}, "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ=="],
"abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="],
"agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="],
"ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="],
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
"camelcase": ["camelcase@6.3.0", "", {}, "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="],
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
"combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
"console-table-printer": ["console-table-printer@2.15.0", "", { "dependencies": { "simple-wcswidth": "^1.1.2" } }, "sha512-SrhBq4hYVjLCkBVOWaTzceJalvn5K1Zq5aQA6wXC/cYjI3frKWNPEMK3sZsJfNNQApvCQmgBcc13ZKmFj8qExw=="],
"decamelize": ["decamelize@1.2.0", "", {}, "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="],
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
"dotenv": ["dotenv@17.2.3", "", {}, "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w=="],
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
"es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
"event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="],
"eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="],
"form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="],
"form-data-encoder": ["form-data-encoder@1.7.2", "", {}, "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A=="],
"formdata-node": ["formdata-node@4.4.1", "", { "dependencies": { "node-domexception": "1.0.0", "web-streams-polyfill": "4.0.0-beta.3" } }, "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ=="],
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
"has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
"humanize-ms": ["humanize-ms@1.2.1", "", { "dependencies": { "ms": "^2.0.0" } }, "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ=="],
"js-tiktoken": ["js-tiktoken@1.0.21", "", { "dependencies": { "base64-js": "^1.5.1" } }, "sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g=="],
"langsmith": ["langsmith@0.3.80", "", { "dependencies": { "@types/uuid": "^10.0.0", "chalk": "^4.1.2", "console-table-printer": "^2.12.1", "p-queue": "^6.6.2", "p-retry": "4", "semver": "^7.6.3", "uuid": "^10.0.0" }, "peerDependencies": { "@opentelemetry/api": "*", "@opentelemetry/exporter-trace-otlp-proto": "*", "@opentelemetry/sdk-trace-base": "*", "openai": "*" }, "optionalPeers": ["@opentelemetry/api", "@opentelemetry/exporter-trace-otlp-proto", "@opentelemetry/sdk-trace-base", "openai"] }, "sha512-BWpbB9/Hkx06S5X4nJE3W5Wm1mH/j6SIqWcM/WAuT+yulohE9knstIJGmBpmSBULb46nCj+cfjRkyF1Nrc4UmA=="],
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
"mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
"mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"mustache": ["mustache@4.2.0", "", { "bin": { "mustache": "bin/mustache" } }, "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ=="],
"node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
"node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
"openai": ["openai@6.9.1", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-vQ5Rlt0ZgB3/BNmTa7bIijYFhz3YBceAA3Z4JuoMSBftBF9YqFHIEhZakSs+O/Ad7EaoEimZvHxD5ylRjN11Lg=="],
"p-finally": ["p-finally@1.0.0", "", {}, "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow=="],
"p-queue": ["p-queue@6.6.2", "", { "dependencies": { "eventemitter3": "^4.0.4", "p-timeout": "^3.2.0" } }, "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ=="],
"p-retry": ["p-retry@4.6.2", "", { "dependencies": { "@types/retry": "0.12.0", "retry": "^0.13.1" } }, "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ=="],
"p-timeout": ["p-timeout@3.2.0", "", { "dependencies": { "p-finally": "^1.0.0" } }, "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg=="],
"retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="],
"semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
"simple-wcswidth": ["simple-wcswidth@1.1.2", "", {}, "sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw=="],
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
"uuid": ["uuid@10.0.0", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ=="],
"web-streams-polyfill": ["web-streams-polyfill@4.0.0-beta.3", "", {}, "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug=="],
"webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
"whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
"zod": ["zod@4.0.0", "", {}, "sha512-9diLdTPc/L7w/5jI4C3gHYNiGHDV9IZYxo1e5LSD8cabi65WVTWWb+g2BGPEpUUCOxR4D+6O5B0AzyMdUAXwrw=="],
"@honcho-ai/core/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="],
"@honcho-ai/sdk/@types/node": ["@types/node@24.10.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="],
"@langchain/langgraph-sdk/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
"@types/node-fetch/@types/node": ["@types/node@24.10.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="],
"chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
"@honcho-ai/core/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
"@honcho-ai/sdk/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
"@types/node-fetch/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
}
}

View File

@ -0,0 +1,110 @@
/**
* LangGraph Integration with Honcho and OpenAI
*
* This module demonstrates how to build a stateful conversational AI agent using
* LangGraph for orchestration, OpenAI for the AI model, and Honcho for memory
* management. It creates a chatbot that remembers conversations across sessions.
*/
import * as dotenv from "dotenv";
import { Honcho, Peer, Session } from "@honcho-ai/sdk";
import OpenAI from "openai";
import { Annotation } from "@langchain/langgraph";
import { StateGraph, START, END } from "@langchain/langgraph";
import * as readline from "readline/promises";
dotenv.config();
const honcho = new Honcho({});
const llm = new OpenAI({
apiKey: process.env.OPENAI_API_KEY
});
const StateAnnotation = Annotation.Root({
userMessage: Annotation<string>(),
assistantResponse: Annotation<string>(),
user: Annotation<Peer>(),
assistant: Annotation<Peer>(),
session: Annotation<Session>(),
});
type State = typeof StateAnnotation.State;
async function chatbot(state: State) {
const userMessage = state.userMessage;
// Get objects from state
const user = state.user;
const assistant = state.assistant;
const session = state.session;
await session.addMessages([user.message(userMessage)]);
// Get context in OpenAI format with token limit
// tokens: 2000 limits the context to 2000 tokens to manage costs and fit within model limits
const messages = (await session.getContext({ tokens: 2000 })).toOpenAI(assistant);
// Generate response
const response = await llm.chat.completions.create({
model: "gpt-4o",
messages: messages
});
const assistantResponse = response.choices[0].message.content!;
// Store assistant response
await session.addMessages([assistant.message(assistantResponse)]);
return { assistantResponse: assistantResponse };
}
const graph = new StateGraph(StateAnnotation)
.addNode("chatbot", chatbot)
.addEdge(START, "chatbot")
.addEdge("chatbot", END)
.compile();
async function runConversationTurn(
userId: string,
userInput: string,
sessionId?: string
): Promise<string> {
if (!sessionId) {
sessionId = `session_${userId}`;
}
// Initialize Honcho objects
const user = await honcho.peer(userId);
const assistant = await honcho.peer("assistant");
const session = await honcho.session(sessionId);
const result = await graph.invoke({
userMessage: userInput,
user: user,
assistant: assistant,
session: session,
});
return result.assistantResponse;
}
async function main() {
console.log("Welcome to the AI Assistant! How can I help you today?");
const userId = "test-user-1234";
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
while (true) {
const userInput = await rl.question("You: ");
if (userInput.toLowerCase() === "quit" || userInput.toLowerCase() === "exit") {
rl.close();
break;
}
const response = await runConversationTurn(userId, userInput);
console.log(`Assistant: ${response}\n`);
}
}
main();

View File

@ -0,0 +1,31 @@
{
"name": "honcho-langgraph-example",
"version": "1.0.0",
"description": "LangGraph integration with Honcho for stateful conversational AI",
"main": "main.ts",
"type": "module",
"scripts": {
"start": "bun run main.ts",
"dev": "bun --watch main.ts"
},
"keywords": [
"honcho",
"langgraph",
"openai",
"chatbot",
"ai"
],
"author": "",
"license": "MIT",
"dependencies": {
"@honcho-ai/sdk": "^1.5.0",
"@langchain/langgraph": "^1.0.2",
"dotenv": "^17.2.3",
"openai": "^6.9.1",
"zod": "4.0.0"
},
"devDependencies": {
"@types/node": "^22.10.2",
"typescript": "^5.7.2"
}
}