mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-09-13 13:17:52 +02:00
docs: remove shared state how-to (#1851)
This commit is contained in:
@@ -81,5 +81,4 @@ Other guides that may prove helpful!
|
||||
- [How to convert LangGraph calls to LangGraph cloud calls](./langgraph_to_langgraph_cloud.ipynb)
|
||||
- [How to integrate webhooks](./webhooks.md)
|
||||
- [How to copy threads](./copy_threads.md)
|
||||
- [How to check status of your threads](./check_thread_status.md)
|
||||
- [How to share state between threads](./shared_state.md)
|
||||
- [How to check status of your threads](./check_thread_status.md)
|
||||
@@ -1,492 +0,0 @@
|
||||
# How to share state between threads
|
||||
|
||||
By default, state in a graph is scoped to a specific thread. LangGraph also allows you to specify a "scope" for a given key/value pair that exists between threads. This can be useful for storing information that is shared between threads. For instance, you may want to store information about a user's preferences expressed in one thread, and then use that information in another thread.
|
||||
|
||||
In this notebook we will go through an example of how to use a graph that has been deployed with shared state.
|
||||
|
||||
## Setup
|
||||
|
||||
First, make sure that you have a deployed graph that has a shared state key. Your state definition should look something like this (support for shared state channels in JS is coming soon!):
|
||||
|
||||
```python
|
||||
class AgentState(TypedDict):
|
||||
# This is scoped to a user_id, so it will be information specific to each user
|
||||
info: Annotated[dict, SharedValue.on("user_id")]
|
||||
# ... other state keys ...
|
||||
```
|
||||
!!! note "Typing shared state keys"
|
||||
Shared state channels (keys) MUST be dictionaries (see `info` channel in the AgentState example above)
|
||||
|
||||
Now we can setup our client and an initial thread to run the graph on:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
|
||||
client = get_client(url=<DEPLOYMENT_URL>)
|
||||
# Using the graph deployed with the name "agent"
|
||||
assistant_id = "agent";
|
||||
# create thread
|
||||
thread = await client.threads.create()
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL> });
|
||||
// Using the graph deployed with the name "agent"
|
||||
const assistantID = "agent";
|
||||
// create thread
|
||||
let thread = await client.threads.create();
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Now, let's run the graph on the first thread, and provide it some information about the users preferences:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
input = {"messages": [{"role": "user", "content": "i like pepperoni pizza"}]}
|
||||
config = {"configurable": {"user_id": "123"}}
|
||||
# stream values
|
||||
async for chunk in client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistant_id,
|
||||
input=input,
|
||||
config=config,
|
||||
):
|
||||
print(f"Receiving new event of type: {chunk.event}...")
|
||||
print(chunk.data)
|
||||
print("\n\n")
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
// create input
|
||||
let input = {
|
||||
messages: [
|
||||
{
|
||||
role: "human",
|
||||
content: "i like pepperoni pizza",
|
||||
}
|
||||
]
|
||||
};
|
||||
let config = { configurable: { user_id: "123" } };
|
||||
|
||||
const streamResponse = client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistantID,
|
||||
{
|
||||
input,
|
||||
config
|
||||
}
|
||||
);
|
||||
for await (const chunk of streamResponse) {
|
||||
console.log(`Receiving new event of type: ${chunk.event}...`);
|
||||
console.log(chunk.data);
|
||||
console.log("\n\n");
|
||||
}
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": {\"messages\": [{\"role\": \"human\", \"content\": \"i like pepperoni pizza\"}]},
|
||||
\"config\":{\"configurable\":{\"user_id\":\"123\"}}
|
||||
}" | \
|
||||
sed 's/\r$//' | \
|
||||
awk '
|
||||
/^event:/ {
|
||||
if (data_content != "") {
|
||||
print data_content "\n"
|
||||
}
|
||||
sub(/^event: /, "Receiving event of type: ", $0)
|
||||
printf "%s...\n", $0
|
||||
data_content = ""
|
||||
}
|
||||
/^data:/ {
|
||||
sub(/^data: /, "", $0)
|
||||
data_content = $0
|
||||
}
|
||||
END {
|
||||
if (data_content != "") {
|
||||
print data_content "\n"
|
||||
}
|
||||
}
|
||||
'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
Receiving new event of type: metadata...
|
||||
{'run_id': '1ef6bdb2-ba0e-6177-84a9-c574772223b3'}
|
||||
|
||||
|
||||
|
||||
Receiving new event of type: values...
|
||||
{'messages': [{'content': 'i like pepperoni pizza', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'human', 'name': None, 'id': 'f1244b8b-e54e-4ebe-ada4-63aadf4a7701', 'example': False}]}
|
||||
|
||||
|
||||
|
||||
Receiving new event of type: values...
|
||||
{'messages': [{'content': 'i like pepperoni pizza', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'human', 'name': None, 'id': 'f1244b8b-e54e-4ebe-ada4-63aadf4a7701', 'example': False}, {'content': '', 'additional_kwargs': {'tool_calls': [{'index': 0, 'id': 'call_ujnk8CIx0xeguFHHe8P0ecgm', 'function': {'arguments': '{"fact":"Isaac likes pepperoni pizza","topic":"Food"}', 'name': 'Info'}, 'type': 'function'}]}, 'response_metadata': {'finish_reason': 'tool_calls', 'model_name': 'gpt-3.5-turbo-0125'}, 'type': 'ai', 'name': None, 'id': 'run-f086646f-cb38-4419-9a92-fc7cb19340ee', 'example': False, 'tool_calls': [{'name': 'Info', 'args': {'fact': 'Isaac likes pepperoni pizza', 'topic': 'Food'}, 'id': 'call_ujnk8CIx0xeguFHHe8P0ecgm', 'type': 'tool_call'}], 'invalid_tool_calls': [], 'usage_metadata': None}]}
|
||||
|
||||
|
||||
|
||||
Receiving new event of type: values...
|
||||
{'messages': [{'content': 'i like pepperoni pizza', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'human', 'name': None, 'id': 'f1244b8b-e54e-4ebe-ada4-63aadf4a7701', 'example': False}, {'content': '', 'additional_kwargs': {'tool_calls': [{'index': 0, 'id': 'call_ujnk8CIx0xeguFHHe8P0ecgm', 'function': {'arguments': '{"fact":"Isaac likes pepperoni pizza","topic":"Food"}', 'name': 'Info'}, 'type': 'function'}]}, 'response_metadata': {'finish_reason': 'tool_calls', 'model_name': 'gpt-3.5-turbo-0125'}, 'type': 'ai', 'name': None, 'id': 'run-f086646f-cb38-4419-9a92-fc7cb19340ee', 'example': False, 'tool_calls': [{'name': 'Info', 'args': {'fact': 'Isaac likes pepperoni pizza', 'topic': 'Food'}, 'id': 'call_ujnk8CIx0xeguFHHe8P0ecgm', 'type': 'tool_call'}], 'invalid_tool_calls': [], 'usage_metadata': None}, {'content': 'Saved!', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'tool', 'name': None, 'id': 'bed77d11-916c-4bad-b8f8-f0c850a8e494', 'tool_call_id': 'call_ujnk8CIx0xeguFHHe8P0ecgm', 'artifact': None, 'status': 'success'}]}
|
||||
|
||||
|
||||
|
||||
Let's stay on the same thread and provide some additional information. Note that we are not redefining the config since we want to continue the conversation on the same thread with the same user.
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
input = {"messages": [{"role": "user", "content": "i also just moved to SF"}]}
|
||||
# stream values
|
||||
async for chunk in client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistant_id, # the graph name
|
||||
input=input,
|
||||
config=config,
|
||||
):
|
||||
print(f"Receiving new event of type: {chunk.event}...")
|
||||
print(chunk.data)
|
||||
print("\n\n")
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
input = {
|
||||
messages: [
|
||||
{
|
||||
role: "human",
|
||||
content: "i also just moved to SF",
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const streamResponse = client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistantID,
|
||||
{
|
||||
input,
|
||||
config
|
||||
}
|
||||
);
|
||||
|
||||
for await (const chunk of streamResponse) {
|
||||
console.log(`Receiving new event of type: ${chunk.event}...`);
|
||||
console.log(chunk.data);
|
||||
console.log("\n\n");
|
||||
}
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": {\"messages\": [{\"role\": \"human\", \"content\": \"i also just moved to SF\"}]},
|
||||
\"config\":{\"configurable\":{\"user_id\":\"123\"}}
|
||||
}" | \
|
||||
sed 's/\r$//' | \
|
||||
awk '
|
||||
/^event:/ {
|
||||
if (data_content != "") {
|
||||
print data_content "\n"
|
||||
}
|
||||
sub(/^event: /, "Receiving event of type: ", $0)
|
||||
printf "%s...\n", $0
|
||||
data_content = ""
|
||||
}
|
||||
/^data:/ {
|
||||
sub(/^data: /, "", $0)
|
||||
data_content = $0
|
||||
}
|
||||
END {
|
||||
if (data_content != "") {
|
||||
print data_content "\n"
|
||||
}
|
||||
}
|
||||
'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
Receiving new event of type: metadata...
|
||||
{'run_id': '1ef6bdb2-f068-60b6-93a6-b2e2f02f117d'}
|
||||
|
||||
|
||||
|
||||
Receiving new event of type: values...
|
||||
{'messages': [{'content': 'i like pepperoni pizza', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'human', 'name': None, 'id': 'f1244b8b-e54e-4ebe-ada4-63aadf4a7701', 'example': False}, {'content': '', 'additional_kwargs': {'tool_calls': [{'index': 0, 'id': 'call_ujnk8CIx0xeguFHHe8P0ecgm', 'function': {'arguments': '{"fact":"Isaac likes pepperoni pizza","topic":"Food"}', 'name': 'Info'}, 'type': 'function'}]}, 'response_metadata': {'finish_reason': 'tool_calls', 'model_name': 'gpt-3.5-turbo-0125'}, 'type': 'ai', 'name': None, 'id': 'run-f086646f-cb38-4419-9a92-fc7cb19340ee', 'example': False, 'tool_calls': [{'name': 'Info', 'args': {'fact': 'Isaac likes pepperoni pizza', 'topic': 'Food'}, 'id': 'call_ujnk8CIx0xeguFHHe8P0ecgm', 'type': 'tool_call'}], 'invalid_tool_calls': [], 'usage_metadata': None}, {'content': 'Saved!', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'tool', 'name': None, 'id': 'bed77d11-916c-4bad-b8f8-f0c850a8e494', 'tool_call_id': 'call_ujnk8CIx0xeguFHHe8P0ecgm', 'artifact': None, 'status': 'success'}, {'content': 'i also just moved to SF', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'human', 'name': None, 'id': 'f1e98940-d9fc-454c-bb65-0036e2c048c6', 'example': False}]}
|
||||
|
||||
|
||||
|
||||
Receiving new event of type: values...
|
||||
{'messages': [{'content': 'i like pepperoni pizza', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'human', 'name': None, 'id': 'f1244b8b-e54e-4ebe-ada4-63aadf4a7701', 'example': False}, {'content': '', 'additional_kwargs': {'tool_calls': [{'index': 0, 'id': 'call_ujnk8CIx0xeguFHHe8P0ecgm', 'function': {'arguments': '{"fact":"Isaac likes pepperoni pizza","topic":"Food"}', 'name': 'Info'}, 'type': 'function'}]}, 'response_metadata': {'finish_reason': 'tool_calls', 'model_name': 'gpt-3.5-turbo-0125'}, 'type': 'ai', 'name': None, 'id': 'run-f086646f-cb38-4419-9a92-fc7cb19340ee', 'example': False, 'tool_calls': [{'name': 'Info', 'args': {'fact': 'Isaac likes pepperoni pizza', 'topic': 'Food'}, 'id': 'call_ujnk8CIx0xeguFHHe8P0ecgm', 'type': 'tool_call'}], 'invalid_tool_calls': [], 'usage_metadata': None}, {'content': 'Saved!', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'tool', 'name': None, 'id': 'bed77d11-916c-4bad-b8f8-f0c850a8e494', 'tool_call_id': 'call_ujnk8CIx0xeguFHHe8P0ecgm', 'artifact': None, 'status': 'success'}, {'content': 'i also just moved to SF', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'human', 'name': None, 'id': 'f1e98940-d9fc-454c-bb65-0036e2c048c6', 'example': False}, {'content': '', 'additional_kwargs': {'tool_calls': [{'index': 0, 'id': 'call_yPNnY10h9KszyuVf5p6H2c1E', 'function': {'arguments': '{"fact":"Isaac just moved to SF","topic":"Location"}', 'name': 'Info'}, 'type': 'function'}]}, 'response_metadata': {'finish_reason': 'tool_calls', 'model_name': 'gpt-3.5-turbo-0125'}, 'type': 'ai', 'name': None, 'id': 'run-d247f67f-ba1a-4ce7-84c2-0f30180d10c6', 'example': False, 'tool_calls': [{'name': 'Info', 'args': {'fact': 'Isaac just moved to SF', 'topic': 'Location'}, 'id': 'call_yPNnY10h9KszyuVf5p6H2c1E', 'type': 'tool_call'}], 'invalid_tool_calls': [], 'usage_metadata': None}]}
|
||||
|
||||
|
||||
|
||||
Receiving new event of type: values...
|
||||
{'messages': [{'content': 'i like pepperoni pizza', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'human', 'name': None, 'id': 'f1244b8b-e54e-4ebe-ada4-63aadf4a7701', 'example': False}, {'content': '', 'additional_kwargs': {'tool_calls': [{'index': 0, 'id': 'call_ujnk8CIx0xeguFHHe8P0ecgm', 'function': {'arguments': '{"fact":"Isaac likes pepperoni pizza","topic":"Food"}', 'name': 'Info'}, 'type': 'function'}]}, 'response_metadata': {'finish_reason': 'tool_calls', 'model_name': 'gpt-3.5-turbo-0125'}, 'type': 'ai', 'name': None, 'id': 'run-f086646f-cb38-4419-9a92-fc7cb19340ee', 'example': False, 'tool_calls': [{'name': 'Info', 'args': {'fact': 'Isaac likes pepperoni pizza', 'topic': 'Food'}, 'id': 'call_ujnk8CIx0xeguFHHe8P0ecgm', 'type': 'tool_call'}], 'invalid_tool_calls': [], 'usage_metadata': None}, {'content': 'Saved!', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'tool', 'name': None, 'id': 'bed77d11-916c-4bad-b8f8-f0c850a8e494', 'tool_call_id': 'call_ujnk8CIx0xeguFHHe8P0ecgm', 'artifact': None, 'status': 'success'}, {'content': 'i also just moved to SF', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'human', 'name': None, 'id': 'f1e98940-d9fc-454c-bb65-0036e2c048c6', 'example': False}, {'content': '', 'additional_kwargs': {'tool_calls': [{'index': 0, 'id': 'call_yPNnY10h9KszyuVf5p6H2c1E', 'function': {'arguments': '{"fact":"Isaac just moved to SF","topic":"Location"}', 'name': 'Info'}, 'type': 'function'}]}, 'response_metadata': {'finish_reason': 'tool_calls', 'model_name': 'gpt-3.5-turbo-0125'}, 'type': 'ai', 'name': None, 'id': 'run-d247f67f-ba1a-4ce7-84c2-0f30180d10c6', 'example': False, 'tool_calls': [{'name': 'Info', 'args': {'fact': 'Isaac just moved to SF', 'topic': 'Location'}, 'id': 'call_yPNnY10h9KszyuVf5p6H2c1E', 'type': 'tool_call'}], 'invalid_tool_calls': [], 'usage_metadata': None}, {'content': 'Saved!', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'tool', 'name': None, 'id': '7c4b82b0-5ee5-4d97-902b-dbec1499fe39', 'tool_call_id': 'call_yPNnY10h9KszyuVf5p6H2c1E', 'artifact': None, 'status': 'success'}]}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Now, let's run the graph on a completely different thread, and see that it remembered the information we provided it:
|
||||
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
# new thread for new conversation
|
||||
thread = await client.threads.create()
|
||||
input = {"messages": [{"role": "user", "content": "where and what should i eat for dinner? Can you list some restaurants?"}]}
|
||||
# stream values
|
||||
async for chunk in client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistant_id,
|
||||
input=input,
|
||||
config=config,
|
||||
):
|
||||
print(f"Receiving new event of type: {chunk.event}...")
|
||||
print(chunk.data)
|
||||
print("\n\n")
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
// new thread for new conversation
|
||||
thread = await client.threads.create();
|
||||
|
||||
// create input
|
||||
let input = {
|
||||
messages: [
|
||||
{
|
||||
role: "human",
|
||||
content: "where and what should i eat for dinner? Can you list some restaurants?",
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const streamResponse = client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistantID,
|
||||
{
|
||||
input,
|
||||
config
|
||||
}
|
||||
);
|
||||
for await (const chunk of streamResponse) {
|
||||
console.log(`Receiving new event of type: ${chunk.event}...`);
|
||||
console.log(chunk.data);
|
||||
console.log("\n\n");
|
||||
}
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}' \
|
||||
| jq -r '.thread_id' \
|
||||
| xargs -I {} \
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/{}/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"assistant_id": "agent",
|
||||
"input": {
|
||||
"messages": [{
|
||||
"role": "user",
|
||||
"content": "where and what should i eat for dinner? Can you list some restaurants?"
|
||||
}]
|
||||
},
|
||||
"config": {
|
||||
"configurable": {
|
||||
"user_id": "123"
|
||||
}
|
||||
}
|
||||
}' \
|
||||
| sed 's/\r$//' \
|
||||
| awk '
|
||||
/^event:/ {
|
||||
if (data_content != "") {
|
||||
print data_content "\n"
|
||||
}
|
||||
sub(/^event: /, "Receiving event of type: ", $0)
|
||||
printf "%s...\n", $0
|
||||
data_content = ""
|
||||
}
|
||||
/^data:/ {
|
||||
sub(/^data: /, "", $0)
|
||||
data_content = $0
|
||||
}
|
||||
END {
|
||||
if (data_content != "") {
|
||||
print data_content "\n"
|
||||
}
|
||||
}
|
||||
'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
Receiving new event of type: metadata...
|
||||
{'run_id': '1ef6bde9-d866-623c-8647-a56e33322334'}
|
||||
|
||||
|
||||
|
||||
Receiving new event of type: values...
|
||||
{'messages': [{'content': 'where and what should i eat for dinner? Can you list some restaurants?', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'human', 'name': None, 'id': 'aaf07830-ddbf-4ec6-b520-2371490abaa8', 'example': False}]}
|
||||
|
||||
|
||||
|
||||
Receiving new event of type: values...
|
||||
{'messages': [{'content': 'where and what should i eat for dinner? Can you list some restaurants?', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'human', 'name': None, 'id': 'aaf07830-ddbf-4ec6-b520-2371490abaa8', 'example': False}, {'content': "Sure! Since you just moved to SF, I can suggest some popular restaurants in the area. Here are a few options:\n\n1. Tony's Pizza Napoletana - Known for their delicious pizzas, including pepperoni pizza.\n2. The House - Offers Asian fusion cuisine in a cozy setting.\n3. Tadich Grill - A historic seafood restaurant serving classic dishes.\n4. Swan Oyster Depot - A seafood counter known for its fresh seafood selections.\n5. Zuni Cafe - A popular spot for American and Mediterranean-inspired dishes.\n\nDo any of these options sound good to you? Let me know if you need more recommendations or information about any specific cuisine!", 'additional_kwargs': {}, 'response_metadata': {'finish_reason': 'stop', 'model_name': 'gpt-3.5-turbo-0125'}, 'type': 'ai', 'name': None, 'id': 'run-dbac2e4c-0e4b-4c4d-b17f-172456222f53', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': None}]}
|
||||
|
||||
|
||||
|
||||
Perfect! The AI recommended restaurants in SF, and included a pizza restaurant at the top of it's list.
|
||||
|
||||
Let's now run the graph for another user to verify that the preferences of the first user are self contained:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
# new thread for new conversation
|
||||
thread = await client.threads.create()
|
||||
# create input
|
||||
input = {"messages": [{"role": "user", "content": "where do I live? what do I like to eat?"}]}
|
||||
config = {"configurable": {"user_id": "321"}}
|
||||
# stream values
|
||||
async for chunk in client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistant_id,
|
||||
input=input,
|
||||
config=config,
|
||||
):
|
||||
print(f"Receiving new event of type: {chunk.event}...")
|
||||
print(chunk.data)
|
||||
print("\n\n")
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
// new thread for new conversation
|
||||
thread = await client.threads.create();
|
||||
// create input
|
||||
let input = {
|
||||
messages: [
|
||||
{
|
||||
role: "human",
|
||||
content: "where do I live? what do I like to eat?",
|
||||
}
|
||||
]
|
||||
};
|
||||
let config = { configurable: { user_id: "321" } };
|
||||
|
||||
const streamResponse = client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistantID,
|
||||
{
|
||||
input,
|
||||
config
|
||||
}
|
||||
);
|
||||
for await (const chunk of streamResponse) {
|
||||
console.log(`Receiving new event of type: ${chunk.event}...`);
|
||||
console.log(chunk.data);
|
||||
console.log("\n\n");
|
||||
}
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}' \
|
||||
| jq -r '.thread_id' \
|
||||
| xargs -I {} \
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/{}/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": {\"messages\": [{\"role\": \"human\", \"content\": \"where do I live? what do I like to eat?\"}]},
|
||||
\"config\":{\"configurable\":{\"user_id\":\"321\"}}
|
||||
}" | \
|
||||
sed 's/\r$//' | \
|
||||
awk '
|
||||
/^event:/ {
|
||||
if (data_content != "") {
|
||||
print data_content "\n"
|
||||
}
|
||||
sub(/^event: /, "Receiving event of type: ", $0)
|
||||
printf "%s...\n", $0
|
||||
data_content = ""
|
||||
}
|
||||
/^data:/ {
|
||||
sub(/^data: /, "", $0)
|
||||
data_content = $0
|
||||
}
|
||||
END {
|
||||
if (data_content != "") {
|
||||
print data_content "\n"
|
||||
}
|
||||
}
|
||||
'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
Receiving new event of type: metadata...
|
||||
{'run_id': '1ef6bdf3-6aae-63ab-adc4-0a1467251531'}
|
||||
|
||||
|
||||
|
||||
Receiving new event of type: values...
|
||||
{'messages': [{'content': 'where do I live? what do I like to eat?', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'human', 'name': None, 'id': '043fd6a6-b59b-411b-9ec3-f6947260e6d3', 'example': False}]}
|
||||
|
||||
|
||||
|
||||
Receiving new event of type: values...
|
||||
{'messages': [{'content': 'where do I live? what do I like to eat?', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'human', 'name': None, 'id': '043fd6a6-b59b-411b-9ec3-f6947260e6d3', 'example': False}, {'content': "I don't have that information yet. Can you please provide me with details about where you live and what you like to eat?", 'additional_kwargs': {}, 'response_metadata': {'finish_reason': 'stop', 'model_name': 'gpt-3.5-turbo-0125'}, 'type': 'ai', 'name': None, 'id': 'run-4dd3415d-e75b-44d5-9744-14f840e6c696', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': None}]}
|
||||
|
||||
Perfect! The agent does not have access to the first users preferences as we expect!
|
||||
|
||||
@@ -27,7 +27,6 @@ LangGraph makes it easy to persist state across graph runs. The guide below show
|
||||
- [How to use Postgres checkpointer for persistence](persistence_postgres.ipynb)
|
||||
- [How to create a custom checkpointer using MongoDB](persistence_mongodb.ipynb)
|
||||
- [How to create a custom checkpointer using Redis](persistence_redis.ipynb)
|
||||
- [How to share state between threads](memory/shared-state.ipynb)
|
||||
|
||||
## Human in the Loop
|
||||
|
||||
|
||||
@@ -1,373 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "7240d5b5-9dac-4070-8a9e-2350fb01e0be",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# How to share state between threads\n",
|
||||
"\n",
|
||||
"By default, state is scoped to a single thread. LangGraph also lets you customize the scope for a given key-value pair. You can use this to share information between threads.\n",
|
||||
"\n",
|
||||
"For instance, you can persist each user’s preferences to shared state and reuse them in new conversational threads.\n",
|
||||
"\n",
|
||||
"In this notebook, we will show how to construct and use such a graph.\n",
|
||||
"\n",
|
||||
"## Setup\n",
|
||||
"\n",
|
||||
"First, let's install the required packages and set our API keys"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "3457aadf",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%%capture --no-stderr\n",
|
||||
"%pip install -U langchain_openai langgraph"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "aa2c64a7",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import getpass\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def _set_env(var: str):\n",
|
||||
" if not os.environ.get(var):\n",
|
||||
" os.environ[var] = getpass.getpass(f\"{var}: \")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"_set_env(\"OPENAI_API_KEY\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "51b6817d",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<div class=\"admonition tip\">\n",
|
||||
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
|
||||
" <p style=\"padding-top: 5px;\">\n",
|
||||
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
|
||||
" </p>\n",
|
||||
"</div> "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "c4c550b5-1954-496b-8b9d-800361af17dc",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Create graph\n",
|
||||
"\n",
|
||||
"In this example we will create a graph that will let us store information about a user's preferences. We will do so by defining a state key that will be scoped to a user_id, and allowing the model to populate this field as it deems fit (by providing the model with a tool to save information about the user).\n",
|
||||
"\n",
|
||||
" \n",
|
||||
"<div class=\"admonition note\">\n",
|
||||
" <p class=\"admonition-title\">Typing shared state keys</p>\n",
|
||||
" <p style=\"margin-top: 5px;\">\n",
|
||||
" Shared state channels (keys) MUST be dictionaries (see <code>info</code> channel in the State example below)\n",
|
||||
" </p>\n",
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "a7f303d6-612e-4e34-bf36-29d4ed25d802",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langgraph.graph.message import MessagesState\n",
|
||||
"from langgraph.graph.state import StateGraph\n",
|
||||
"from langgraph.store.memory import MemoryStore\n",
|
||||
"from langgraph.managed.shared_value import SharedValue\n",
|
||||
"from typing import Literal, TypedDict, Annotated\n",
|
||||
"import uuid\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"from langgraph.checkpoint.memory import MemorySaver\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class State(MessagesState):\n",
|
||||
" # We use an info key to track information\n",
|
||||
" # This is scoped to a user_id, so it will be information specific to each user\n",
|
||||
" info: Annotated[dict[str, dict], SharedValue.on(\"user_id\")]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# We will give this as a tool to the agent\n",
|
||||
"# This will let the agent call this tool to save a fact\n",
|
||||
"class Info(TypedDict):\n",
|
||||
" \"\"\"This tool should be called when you want to save a new fact about the user.\n",
|
||||
"\n",
|
||||
" Attributes:\n",
|
||||
" fact (str): A fact about the user.\n",
|
||||
" topic (str): The topic related the fact is about, i.e. Food, Location, Movies, etc.\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" fact: str\n",
|
||||
" topic: str\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# This is the prompt we give the agent\n",
|
||||
"# We will pass known info into the prompt\n",
|
||||
"# We will tell it to use the Info tool to save more\n",
|
||||
"prompt = \"\"\"You are a helpful assistant that learns about users to provide better assistance.\n",
|
||||
"\n",
|
||||
"Current user information:\n",
|
||||
"<info>\n",
|
||||
"{info}\n",
|
||||
"</info>\n",
|
||||
"\n",
|
||||
"Instructions:\n",
|
||||
"1. Use the `Info` tool to save new information the user shares.\n",
|
||||
"2. Save facts, opinions, preferences, and experiences.\n",
|
||||
"3. Your goal: Improve assistance by building a user profile over time.\n",
|
||||
"\n",
|
||||
"Remember: Every piece of information helps you serve the user better in future interactions.\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# We give the model access to the Info tool\n",
|
||||
"model = ChatOpenAI().bind_tools([Info])\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def call_model(state: State):\n",
|
||||
" \"\"\"Call the model.\"\"\"\n",
|
||||
" # The info value here is scoped to the user_id\n",
|
||||
" info = \"\\n\".join([d[\"fact\"] for d in state[\"info\"].values()])\n",
|
||||
" # Format system prompt\n",
|
||||
" system_msg = prompt.format(info=info)\n",
|
||||
" # Call model\n",
|
||||
" response = model.invoke(\n",
|
||||
" [{\"role\": \"system\", \"content\": system_msg}] + state[\"messages\"]\n",
|
||||
" )\n",
|
||||
" return {\"messages\": [response]}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Routing function to decide what to do next\n",
|
||||
"# If no tool calls, then we end\n",
|
||||
"# If tool calls, then we update memory\n",
|
||||
"def route(state) -> Literal[\"__end__\", \"update_memory\"]:\n",
|
||||
" if len(state[\"messages\"][-1].tool_calls) == 0:\n",
|
||||
" return \"__end__\"\n",
|
||||
" else:\n",
|
||||
" return \"update_memory\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def update_memory(state: State):\n",
|
||||
" \"\"\"Update the memory.\"\"\"\n",
|
||||
" tool_calls = []\n",
|
||||
" memories = {}\n",
|
||||
" # Each tool call is a new memory to save\n",
|
||||
" for tc in state[\"messages\"][-1].tool_calls:\n",
|
||||
" # We append ToolMessages (to pass back to the LLM)\n",
|
||||
" # This is needed because OpenAI requires each tool call be followed by a ToolMessage\n",
|
||||
" tool_calls.append(\n",
|
||||
" {\"role\": \"tool\", \"content\": \"Saved!\", \"tool_call_id\": tc[\"id\"]}\n",
|
||||
" )\n",
|
||||
" # We create a new memory from this tool call\n",
|
||||
" memories[str(uuid.uuid4())] = {\n",
|
||||
" \"fact\": tc[\"args\"][\"fact\"],\n",
|
||||
" \"topic\": tc[\"args\"][\"topic\"],\n",
|
||||
" }\n",
|
||||
" # Return the messages and memories to update the state with\n",
|
||||
" return {\"messages\": tool_calls, \"info\": memories}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# This is the in memory checkpointer we will use\n",
|
||||
"# We need this because we want to enable threads (conversations)\n",
|
||||
"memory = MemorySaver()\n",
|
||||
"\n",
|
||||
"# This is the in memory Key Value store\n",
|
||||
"# This is needed to save the memories\n",
|
||||
"kv = MemoryStore()\n",
|
||||
"\n",
|
||||
"# Construct this relatively simple graph\n",
|
||||
"graph = StateGraph(State)\n",
|
||||
"graph.add_node(call_model)\n",
|
||||
"graph.add_node(update_memory)\n",
|
||||
"graph.add_edge(\"update_memory\", \"__end__\")\n",
|
||||
"graph.add_edge(\"__start__\", \"call_model\")\n",
|
||||
"graph.add_conditional_edges(\"call_model\", route, [\"__end__\", \"update_memory\"])\n",
|
||||
"graph = graph.compile(checkpointer=memory, store=kv)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "552d4e33-556d-4fa5-8094-2a076bc21529",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Run graph on one thread\n",
|
||||
"\n",
|
||||
"We can now run the graph on one thread and give it some information"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "18bd8679-3a73-4033-bfb4-5093ac1f5d7f",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"{'call_model': {'messages': [AIMessage(content='Hello! How can I assist you today?', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 10, 'prompt_tokens': 181, 'total_tokens': 191, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-865472b7-68e0-4b93-bf63-13bd1dc4f3f0-0', usage_metadata={'input_tokens': 181, 'output_tokens': 10, 'total_tokens': 191})]}}\n",
|
||||
"{'call_model': {'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_BcSTNM6xueW6lgcaA8GdBuy4', 'function': {'arguments': '{\"fact\":\"likes pepperoni pizza\",\"topic\":\"Food\"}', 'name': 'Info'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 20, 'prompt_tokens': 203, 'total_tokens': 223, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-8adf64eb-db04-4232-99ce-9555ac7a9146-0', tool_calls=[{'name': 'Info', 'args': {'fact': 'likes pepperoni pizza', 'topic': 'Food'}, 'id': 'call_BcSTNM6xueW6lgcaA8GdBuy4', 'type': 'tool_call'}], usage_metadata={'input_tokens': 203, 'output_tokens': 20, 'total_tokens': 223})]}}\n",
|
||||
"{'update_memory': {'messages': [{'role': 'tool', 'content': 'Saved!', 'tool_call_id': 'call_BcSTNM6xueW6lgcaA8GdBuy4'}]}}\n",
|
||||
"{'call_model': {'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_eN6R6i9jLvLNpxvXVU4A3y08', 'function': {'arguments': '{\"fact\":\"just moved to San Francisco\",\"topic\":\"Location\"}', 'name': 'Info'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 21, 'prompt_tokens': 247, 'total_tokens': 268, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-8ab84d8c-1a21-4b07-940c-74f6248ce6ea-0', tool_calls=[{'name': 'Info', 'args': {'fact': 'just moved to San Francisco', 'topic': 'Location'}, 'id': 'call_eN6R6i9jLvLNpxvXVU4A3y08', 'type': 'tool_call'}], usage_metadata={'input_tokens': 247, 'output_tokens': 21, 'total_tokens': 268})]}}\n",
|
||||
"{'update_memory': {'messages': [{'role': 'tool', 'content': 'Saved!', 'tool_call_id': 'call_eN6R6i9jLvLNpxvXVU4A3y08'}]}}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"config = {\"configurable\": {\"thread_id\": \"1\", \"user_id\": \"1\"}}\n",
|
||||
"\n",
|
||||
"# First let's just say hi to the AI\n",
|
||||
"for update in graph.stream(\n",
|
||||
" {\"messages\": [{\"role\": \"user\", \"content\": \"hi\"}]}, config, stream_mode=\"updates\"\n",
|
||||
"):\n",
|
||||
" print(update)\n",
|
||||
"\n",
|
||||
"# Let's continue the conversation (by passing the same config) and tell the AI we like pepperoni pizza\n",
|
||||
"for update in graph.stream(\n",
|
||||
" {\"messages\": [{\"role\": \"user\", \"content\": \"i like pepperoni pizza\"}]},\n",
|
||||
" config,\n",
|
||||
" stream_mode=\"updates\",\n",
|
||||
"):\n",
|
||||
" print(update)\n",
|
||||
"\n",
|
||||
"# Let's continue the conversation even further (by passing the same config) and tell the AI we live in SF\n",
|
||||
"for update in graph.stream(\n",
|
||||
" {\"messages\": [{\"role\": \"user\", \"content\": \"i also just moved to SF\"}]},\n",
|
||||
" config,\n",
|
||||
" stream_mode=\"updates\",\n",
|
||||
"):\n",
|
||||
" print(update)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "b8c416fa-086a-491d-a7d3-57091f6413e3",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Run graph on a different thread\n",
|
||||
"\n",
|
||||
"We can now run the graph on a different thread and see that it remembers facts about the user (specifically that the user likes pepperoni pizza and lives in SF):"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "e240f025-ff8b-4d17-beb7-2420c0575dd9",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"{'call_model': {'messages': [AIMessage(content=\"I can help with that! Since you just moved to San Francisco, how about trying some local favorites? Here are a few restaurants you might enjoy:\\n\\n1. Tony's Pizza Napoletana - Known for their delicious pepperoni pizza.\\n2. The House - Offers a mix of Asian fusion dishes.\\n3. Tadich Grill - A historic seafood restaurant with a cozy atmosphere.\\n4. Zuni Cafe - Famous for its roast chicken and innovative cuisine.\\n5. La Taqueria - A popular spot for authentic Mexican tacos.\\n\\nFeel free to explore these options and let me know if you'd like more recommendations or information about any specific cuisine!\", additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 131, 'prompt_tokens': 206, 'total_tokens': 337, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-8530de74-bc07-4b31-b58d-4f4c064918b6-0', usage_metadata={'input_tokens': 206, 'output_tokens': 131, 'total_tokens': 337})]}}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"config = {\"configurable\": {\"thread_id\": \"2\", \"user_id\": \"1\"}}\n",
|
||||
"\n",
|
||||
"for update in graph.stream(\n",
|
||||
" {\n",
|
||||
" \"messages\": [\n",
|
||||
" {\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": \"where and what should i eat for dinner? Can you list some restaurants?\",\n",
|
||||
" }\n",
|
||||
" ]\n",
|
||||
" },\n",
|
||||
" config,\n",
|
||||
" stream_mode=\"updates\",\n",
|
||||
"):\n",
|
||||
" print(update)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "091995d3",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Perfect! The AI recommended restaurants in SF, and included a pizza restaurant at the top of it's list.\n",
|
||||
"\n",
|
||||
"Notice that the `messages` in this new thread do NOT contain the messages from the previous thread since we didn't store them as shared values across the `user_id`. However, the `info` we saved in the previous thread was saved since we passed in the same `user_id` in this new thread.\n",
|
||||
"\n",
|
||||
"Let's now run the graph for another user to verify that the preferences of the first user are self contained:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "f9bf2c15",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"{'call_model': {'messages': [AIMessage(content='I can help you with that! Could you please provide me with your location or a preferred cuisine for dinner?', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 23, 'prompt_tokens': 195, 'total_tokens': 218, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-3f7eaa92-d3f0-4cca-ab13-0ecd7b922b9a-0', usage_metadata={'input_tokens': 195, 'output_tokens': 23, 'total_tokens': 218})]}}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"config = {\"configurable\": {\"thread_id\": \"3\", \"user_id\": \"2\"}}\n",
|
||||
"\n",
|
||||
"for update in graph.stream(\n",
|
||||
" {\n",
|
||||
" \"messages\": [\n",
|
||||
" {\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": \"where and what should i eat for dinner? Can you list some restaurants?\",\n",
|
||||
" }\n",
|
||||
" ]\n",
|
||||
" },\n",
|
||||
" config,\n",
|
||||
" stream_mode=\"updates\",\n",
|
||||
"):\n",
|
||||
" print(update)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "b7086cea",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Perfect! The graph has forgotten all of the previous preferences and has to ask the user for it's location and dietary preferences."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.2"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -140,7 +140,6 @@ nav:
|
||||
- Use Postgres checkpointer for persistence: how-tos/persistence_postgres.ipynb
|
||||
- Create custom checkpointer using MongoDB: how-tos/persistence_mongodb.ipynb
|
||||
- Create custom checkpointer using Redis: how-tos/persistence_redis.ipynb
|
||||
- Share state between threads: how-tos/memory/shared-state.ipynb
|
||||
- Human-in-the-loop:
|
||||
- Add breakpoints: how-tos/human_in_the_loop/breakpoints.ipynb
|
||||
- Add dynamic breakpoints: how-tos/human_in_the_loop/dynamic_breakpoints.ipynb
|
||||
@@ -252,7 +251,6 @@ nav:
|
||||
- Integrate Webhooks: 'cloud/how-tos/webhooks.md'
|
||||
- Copy Threads: 'cloud/how-tos/copy_threads.md'
|
||||
- Check Status of Threads: "cloud/how-tos/check_thread_status.md"
|
||||
- Share State Between Threads: "cloud/how-tos/shared_state.md"
|
||||
- Conceptual Guides:
|
||||
- API Concepts: "cloud/concepts/api.md"
|
||||
- Cloud Concepts: "cloud/concepts/cloud.md"
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "9dd11610",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/memory/shared-state.ipynb"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.9"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
Reference in New Issue
Block a user