mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-18 05:35:43 +02:00
Compare commits
459
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc230328ba | ||
|
|
6a53669eeb | ||
|
|
95edac5e03 | ||
|
|
6d380dfb41 | ||
|
|
d6119d55e3 | ||
|
|
dba20d0577 | ||
|
|
5145dac12b | ||
|
|
440c7ff12a | ||
|
|
5eef290c4e | ||
|
|
a8b3746356 | ||
|
|
7541331643 | ||
|
|
76814676c2 | ||
|
|
0804984f9d | ||
|
|
8f11b6a003 | ||
|
|
aa6b122e4c | ||
|
|
23491e5c9a | ||
|
|
1491f30a07 | ||
|
|
f63635d3c8 | ||
|
|
6672032568 | ||
|
|
311ce7b04f | ||
|
|
14b732740e | ||
|
|
370825a48a | ||
|
|
264bae5a7e | ||
|
|
f6aa19709e | ||
|
|
8495f6f95d | ||
|
|
6710908d40 | ||
|
|
d24ad3d980 | ||
|
|
2d8288fd0f | ||
|
|
c1ef10a0ec | ||
|
|
a3d7b6f44e | ||
|
|
bc9d45b476 | ||
|
|
7d3f0089aa | ||
|
|
ed678f4701 | ||
|
|
22411ba0fd | ||
|
|
5b9021ff37 | ||
|
|
aaff464115 | ||
|
|
3a23a256e2 | ||
|
|
f6857395b9 | ||
|
|
cdaa7ba003 | ||
|
|
39745ed794 | ||
|
|
738bb8a343 | ||
|
|
b2dde8d9af | ||
|
|
5312edc830 | ||
|
|
276310e116 | ||
|
|
39977ded8c | ||
|
|
439038fc3a | ||
|
|
2e5445c565 | ||
|
|
d38510ad03 | ||
|
|
951486d107 | ||
|
|
745a1e7a29 | ||
|
|
24731d6a28 | ||
|
|
a4689a5d10 | ||
|
|
b05ce0bf60 | ||
|
|
4501e41991 | ||
|
|
bc83287fc8 | ||
|
|
11547e1990 | ||
|
|
fadd9c4577 | ||
|
|
fd2933a792 | ||
|
|
40fa69f8ee | ||
|
|
0824161984 | ||
|
|
b028f502e1 | ||
|
|
9dc3fed6b8 | ||
|
|
0232201b7b | ||
|
|
869b0f2de4 | ||
|
|
90e3adcd71 | ||
|
|
cb918601d1 | ||
|
|
4a4c8db635 | ||
|
|
56a9ce57b1 | ||
|
|
d1ee1cf1f1 | ||
|
|
29c3a579b3 | ||
|
|
9e3cb1f034 | ||
|
|
508e333220 | ||
|
|
aa1bbe3d01 | ||
|
|
139cad373b | ||
|
|
2a86abb8c4 | ||
|
|
d1f0799002 | ||
|
|
be088801ba | ||
|
|
1ee6bfeb8d | ||
|
|
2153d36726 | ||
|
|
819eae891e | ||
|
|
457edaa75b | ||
|
|
90ba4c5205 | ||
|
|
b3c5298100 | ||
|
|
9d9476e664 | ||
|
|
03bec97767 | ||
|
|
dd9b5c42e8 | ||
|
|
951a3f2d1c | ||
|
|
adaa340c15 | ||
|
|
2c85cba9ca | ||
|
|
cb7b924006 | ||
|
|
c61ac946af | ||
|
|
ec0a30008c | ||
|
|
00c7909c27 | ||
|
|
58d396fcf1 | ||
|
|
9f0abf014d | ||
|
|
8d6cd15669 | ||
|
|
61676b8db0 | ||
|
|
3b85e53360 | ||
|
|
250a17d711 | ||
|
|
f63bec8578 | ||
|
|
dc0f0c5944 | ||
|
|
777fe692d4 | ||
|
|
fdbe31a3aa | ||
|
|
78a9933144 | ||
|
|
5717eefa79 | ||
|
|
5978012619 | ||
|
|
b447a1c7cf | ||
|
|
f87608a16f | ||
|
|
a5fe3316b6 | ||
|
|
e0699fbdaf | ||
|
|
b4eb57da67 | ||
|
|
cec3bef7ea | ||
|
|
adc732272c | ||
|
|
71dc92b349 | ||
|
|
79b4642e55 | ||
|
|
48446bcbd2 | ||
|
|
cf95c870fe | ||
|
|
a34c38a53d | ||
|
|
db5276ded1 | ||
|
|
9c67b9ce4b | ||
|
|
08667fe786 | ||
|
|
c6d674cd3e | ||
|
|
294078adab | ||
|
|
48dabc0538 | ||
|
|
d2cc02d789 | ||
|
|
92c66d13ec | ||
|
|
7f821deded | ||
|
|
12a601c8a3 | ||
|
|
d64447c4c2 | ||
|
|
0d2db35d93 | ||
|
|
6e9e1ca146 | ||
|
|
b290e1ffdc | ||
|
|
a5eb6a75bf | ||
|
|
7a136aaff6 | ||
|
|
e973e936c3 | ||
|
|
066f3b21f8 | ||
|
|
2eecaa8500 | ||
|
|
d935a2d110 | ||
|
+3 |
d5b8733a40 | ||
|
|
40c0e44b9a | ||
|
|
b5afec4b2a | ||
|
|
d674e1bddd | ||
|
|
8c68f739b9 | ||
|
|
1500ebd3d7 | ||
|
|
0837263542 | ||
|
|
18633bc99e | ||
|
|
2558f81889 | ||
|
|
b832fefc58 | ||
|
|
444d699fe8 | ||
|
|
e315fb7397 | ||
|
|
0d8dfa7bba | ||
|
|
2c2ace2a40 | ||
|
|
74218fadad | ||
|
|
7a39e5fc6e | ||
|
|
06144b3b13 | ||
|
|
b19572351e | ||
|
|
a5ce13eb45 | ||
|
|
e91f72dd48 | ||
|
|
3d9cb305b4 | ||
|
|
de1460937a | ||
|
|
e0bf4a7bc3 | ||
|
|
06f311fffb | ||
|
|
5bdb887638 | ||
|
|
6c4034420c | ||
|
|
fa5d93ac2f | ||
|
|
d14a6e2c04 | ||
|
|
54dadadac4 | ||
|
|
d073e1bd8a | ||
|
|
28c276f377 | ||
|
|
a71eb09488 | ||
|
|
579c7831b9 | ||
|
|
16da5f4779 | ||
|
|
f59a1339c9 | ||
|
|
d166e9b8e9 | ||
|
|
4da35babda | ||
|
|
c3d882e87a | ||
|
|
fbade9e300 | ||
|
|
0b6a9e345d | ||
|
|
9b9bf88aee | ||
|
|
5f00938aa2 | ||
|
|
67a86f2dc2 | ||
|
|
ad44d1fe66 | ||
|
|
5c45f7c330 | ||
|
|
bb2f448175 | ||
|
|
8a9f3dbf6f | ||
|
|
63f051ad28 | ||
|
|
e5ded1888b | ||
|
|
0ab9770056 | ||
|
|
8ca5e56f52 | ||
|
|
0733ec65ad | ||
|
|
543cbe9032 | ||
|
|
d1710e2eac | ||
|
|
6eace78c53 | ||
|
|
e5947bcd30 | ||
|
|
1240f8bdca | ||
|
|
4d7c107bb8 | ||
|
|
7a4fd25185 | ||
|
|
7a66213535 | ||
|
|
c8d32f104d | ||
|
|
2fee649980 | ||
|
|
0d8a8c5847 | ||
|
|
9cb6365914 | ||
|
|
b6dc566ec7 | ||
|
|
fb1c0ae9f9 | ||
|
|
4de1bd6e66 | ||
|
+8 |
f21fc056bf | ||
|
|
1f1d032430 | ||
|
|
a84b744eb6 | ||
|
|
6f86a8c4cb | ||
|
|
d5ab8b42e0 | ||
|
|
87f2e69395 | ||
|
|
4c73b176ff | ||
|
|
4321ed0f87 | ||
|
|
cba4d9e3bc | ||
|
|
fa36a50444 | ||
|
|
5413f9db9f | ||
|
|
8118e90543 | ||
|
|
c13c474626 | ||
|
|
cd58fad69d | ||
|
|
f3a7925d86 | ||
|
|
de6c25689d | ||
|
|
141afa8c62 | ||
|
|
e99f6292c5 | ||
|
|
1800df7048 | ||
|
|
7f57e00975 | ||
|
|
844417591d | ||
|
|
c9966c4feb | ||
|
|
4abf948462 | ||
|
|
269590c4d9 | ||
|
|
d8756f257e | ||
|
|
cac0cd5522 | ||
|
|
c91208429e | ||
|
|
690b6f4ea1 | ||
|
|
f5b888dd72 | ||
|
|
7a8f29847b | ||
|
|
f001246794 | ||
|
|
07cd4d83e1 | ||
|
|
fcdfc1d5e4 | ||
|
|
dc95d1af88 | ||
|
|
042e8ef315 | ||
|
|
37d1ac1dce | ||
|
|
ecfabdf73a | ||
|
|
543e4c4e7e | ||
|
|
22e09d2739 | ||
|
|
89451f4ea2 | ||
|
|
b7d11b4141 | ||
|
|
1d3fd9a46b | ||
|
|
813a1d6d0c | ||
|
|
e28af0ffc3 | ||
|
|
8c4e698c5a | ||
|
|
339de4c204 | ||
|
|
0885e7833b | ||
|
|
669cf817e8 | ||
|
|
000f5c3043 | ||
|
|
b3708bd7f6 | ||
|
|
8271e39e00 | ||
|
|
60560ea755 | ||
|
|
e2acfb24cc | ||
|
|
191192b142 | ||
|
|
df368bdd30 | ||
|
|
4ec897033f | ||
|
|
c989f1c898 | ||
|
|
c16e42e6d5 | ||
|
|
376469ea90 | ||
|
|
7e2af0ce8d | ||
|
|
1b205a99cb | ||
|
|
0a8ba20f5f | ||
|
|
048cb3584c | ||
|
|
22c35b7bc8 | ||
|
|
f3ed32e611 | ||
|
|
276675b618 | ||
|
|
882de42996 | ||
|
|
70be50f37b | ||
|
|
1c7234e9c5 | ||
|
|
3d88f75254 | ||
|
|
407abbe9ff | ||
|
|
6182cd1dcb | ||
|
|
1d276dd753 | ||
|
|
a48d8cb69b | ||
|
|
a05a251caf | ||
|
|
c7bbb26ac0 | ||
|
|
ac9b6c416e | ||
|
|
d4b4eebe4a | ||
|
|
f8e1e803e1 | ||
|
|
141a6af4f7 | ||
|
|
8a763ad358 | ||
|
|
16d02e63a1 | ||
|
|
03421c2b04 | ||
|
|
2508aa45ea | ||
|
|
9e035264f8 | ||
|
|
84e14f47bf | ||
|
|
83bbe42eab | ||
|
|
70894af72c | ||
|
|
e466327524 | ||
|
|
b6655fe083 | ||
|
|
9b7cc1c82e | ||
|
|
80d6bddd1b | ||
|
|
c406aede96 | ||
|
|
96bfc8bad9 | ||
|
|
0fd6306623 | ||
|
|
f01ab13cab | ||
|
|
1beaf48cf9 | ||
|
|
847b0e8243 | ||
|
|
02a2467610 | ||
|
|
fa5e7a97c2 | ||
|
|
233a92ccb9 | ||
|
|
e277a6bd9b | ||
|
|
16250fe038 | ||
|
|
216d1be0a5 | ||
|
|
0a4cd5fcaa | ||
|
|
f8503670af | ||
|
|
0f22841f78 | ||
|
|
4a26ca5bc2 | ||
|
|
a622218746 | ||
|
|
7fda427601 | ||
|
|
1c9bfba23a | ||
|
|
cee9ac0b7a | ||
|
|
3ddb6b1477 | ||
|
|
8a5519da29 | ||
|
|
abc5a5ff44 | ||
|
|
38ab90217f | ||
|
|
005edb2979 | ||
|
|
c5a851cd91 | ||
|
|
06a42aee53 | ||
|
|
04b9947a41 | ||
|
|
239df52e74 | ||
|
|
8a67a5ac25 | ||
|
|
adb2eee54b | ||
|
|
263a583f01 | ||
|
|
565d52975c | ||
|
|
865fba9d50 | ||
|
|
929bba8337 | ||
|
|
634511350f | ||
|
|
c9dcd3fbb5 | ||
|
|
10a0a8633a | ||
|
|
23327f5647 | ||
|
|
e23da72ccd | ||
|
|
0aefe68a5f | ||
|
|
5b8edf3c72 | ||
|
|
b64e1bcae4 | ||
|
|
d877ea479b | ||
|
|
cb63c1ab72 | ||
|
|
a7ea5e44ce | ||
|
|
c3ae5e6b71 | ||
|
|
ac328c3fd8 | ||
|
|
ebd7977936 | ||
|
|
a6381c32b0 | ||
|
|
294d346650 | ||
|
|
866c8009dc | ||
|
|
9a0cee5cd0 | ||
|
|
73bed2cf7c | ||
|
|
4cffe58065 | ||
|
|
e73964a971 | ||
|
|
de91f21f6b | ||
|
|
41eed326b8 | ||
|
|
946d23213d | ||
|
|
c78197a583 | ||
|
|
50756207ee | ||
|
|
903cec0cfa | ||
|
|
f63952595d | ||
|
|
4a252bd03a | ||
|
|
f0f329d9e1 | ||
|
|
78bef6bc0c | ||
|
|
7bd364c457 | ||
|
|
77306c5142 | ||
|
|
eba18c3213 | ||
|
|
a1c856c088 | ||
|
|
92010f84ec | ||
|
|
3b8f3f9de3 | ||
|
|
6dcff8a839 | ||
|
|
1309243b29 | ||
|
|
771c6150a4 | ||
|
|
edfb65fd3a | ||
|
|
0f92470e49 | ||
|
|
dfcaf97c73 | ||
|
|
63a0028372 | ||
|
|
1134017d07 | ||
|
|
33feba4877 | ||
|
|
4fec8e9dec | ||
|
|
c137169325 | ||
|
|
1e2672e63d | ||
|
|
06803ab683 | ||
|
|
3488ee47e0 | ||
|
|
289bdd0cea | ||
|
|
417103066b | ||
|
|
25a59447c1 | ||
|
|
21906d2b7b | ||
|
|
0cad7019cb | ||
|
|
7e735672bf | ||
|
|
5498893780 | ||
|
|
e80f47aa01 | ||
|
|
a0b2f742a3 | ||
|
|
b7973d65db | ||
|
|
3fa3a586b5 | ||
|
|
053b606b46 | ||
|
|
4548a0ebe8 | ||
|
|
0171e9a323 | ||
|
|
c439cb0872 | ||
|
|
2a4d7e8889 | ||
|
|
7f3578e0f1 | ||
|
|
e2f96b5ae5 | ||
|
|
0d5f7e55bf | ||
|
|
9209f11187 | ||
|
|
bb1c5b8cdf | ||
|
|
d6bb008ff4 | ||
|
|
6130e08fa6 | ||
|
|
3ad061f0d7 | ||
|
|
116b5d1cac | ||
|
|
0aff02e180 | ||
|
|
074af5c122 | ||
|
|
29ffaa0e0b | ||
|
|
45cd4e1928 | ||
|
|
480271f753 | ||
|
|
66fdf60e47 | ||
|
|
0894daf3fc | ||
|
|
850c55d630 | ||
|
|
c0d65ff409 | ||
|
|
be7b60a722 | ||
|
|
d467ec6556 | ||
|
|
b8683ab67a | ||
|
|
666279a241 | ||
|
|
6a9ca8d67e | ||
|
|
3b98044f2f | ||
|
|
a4a8934bd3 | ||
|
|
470b9a4b97 | ||
|
|
516175780d | ||
|
|
571780f74c | ||
|
|
d719438307 | ||
|
|
85c809a651 | ||
|
|
0441fd156f | ||
|
|
37b5d3886c | ||
|
|
b95267a3cc | ||
|
|
2172bc89ed | ||
|
|
4138ef9c43 | ||
|
|
0ff181b7ce | ||
|
|
2e33c520a5 | ||
|
|
67b1dc602e | ||
|
|
1519b90414 | ||
|
|
0035ab9825 | ||
|
|
c42cd57a32 | ||
|
|
acc56e094a | ||
|
|
6b30d4fd8f | ||
|
|
fcc37cd06b | ||
|
|
c17ee1bf5a | ||
|
|
88c603b00b | ||
|
|
c12f7cb2b9 | ||
|
|
6d7d689578 | ||
|
|
f1b7eca7fc | ||
|
|
93766a6df1 | ||
|
|
a9d4e0da29 | ||
|
|
9105e60a34 | ||
|
|
b735452153 | ||
|
|
5920d8aa92 | ||
|
|
533f5b3d6f | ||
|
|
be5889a7df | ||
|
|
0bf268feca | ||
|
|
5e7566f4a3 | ||
|
|
494c8ef0d2 | ||
|
|
45e60ff9e1 |
@@ -1,29 +1,29 @@
|
||||
name: "\U0001F41B Bug Report"
|
||||
description: Report a bug in LangGraph. To report a security issue, please instead use the security option below. For questions, please use the GitHub Discussions.
|
||||
labels: ["02 Bug Report"]
|
||||
description: Report a bug in LangGraph. To report a security issue, please instead use the security option below. For questions, please use the LangChain Forum at forum.langchain.com.
|
||||
labels: [pending,bug]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: >
|
||||
value: |
|
||||
Thank you for taking the time to file a bug report.
|
||||
|
||||
Use this to report BUGS in LangGraph. For usage questions, feature requests and general design questions, please use [GitHub Discussions](https://github.com/langchain-ai/langgraph/discussions).
|
||||
Use this to report BUGS in LangGraph. For usage questions, feature requests and general design questions, please use the [LangChain Forum](https://forum.langchain.com/).
|
||||
|
||||
Relevant links to check before filing a bug report to see if your issue has already been reported, fixed or
|
||||
if there's another way to solve your problem:
|
||||
|
||||
[LangGraph Github Discussions](https://github.com/langchain-ai/langgraph/discussions),
|
||||
[LangGraph Github Issues](https://github.com/langchain-ai/langgraph/issues),
|
||||
[LangGraph how-to guides](https://langchain-ai.github.io/langgraph/how-tos/).
|
||||
[LangChain documentation with the integrated search](https://python.langchain.com/docs/get_started/introduction),
|
||||
[GitHub search](https://github.com/langchain-ai/langgraph),
|
||||
* [LangChain Forum](https://forum.langchain.com/),
|
||||
* [LangGraph Github Issues](https://github.com/langchain-ai/langgraph/issues),
|
||||
* [LangGraph how-to guides](https://langchain-ai.github.io/langgraph/how-tos/).
|
||||
* [LangChain documentation with the integrated search](https://python.langchain.com/docs/get_started/introduction),
|
||||
* [GitHub search](https://github.com/langchain-ai/langgraph),
|
||||
- type: checkboxes
|
||||
id: checks
|
||||
attributes:
|
||||
label: Checked other resources
|
||||
description: Before submitting this issue, please confirm that you have completed all the steps below by checking each option. These steps help ensure your issue is well-defined, relevant, and actionable.
|
||||
options:
|
||||
- label: This is a bug, not a usage question. For questions, please use GitHub Discussions.
|
||||
- label: This is a bug, not a usage question. For questions, please use the LangChain Forum (https://forum.langchain.com/).
|
||||
required: true
|
||||
- label: I added a clear and detailed title that summarizes the issue.
|
||||
required: true
|
||||
@@ -38,7 +38,7 @@ body:
|
||||
attributes:
|
||||
label: Example Code
|
||||
description: |
|
||||
Please add a self-contained, [minimal, reproducible, example](https://stackoverflow.com/help/minimal-reproducible-example) with your use case.
|
||||
Please add a self-contained, [minimal, reproducible, example](https://stackoverflow.com/help/minimal-reproducible-example) with your use case. Replace this code with your own!
|
||||
placeholder: |
|
||||
from langgraph.graph import StateGraph
|
||||
|
||||
@@ -78,7 +78,7 @@ body:
|
||||
attributes:
|
||||
label: System Info
|
||||
description: |
|
||||
python -m langchain_core.sys_info
|
||||
Run on your machine: `python -m langchain_core.sys_info`
|
||||
placeholder: |
|
||||
python -m langchain_core.sys_info
|
||||
validations:
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
blank_issues_enabled: false
|
||||
version: 2.1
|
||||
contact_links:
|
||||
- name: 🤔 Question or Problem
|
||||
about: Ask a question or ask about a problem in GitHub Discussions.
|
||||
url: https://github.com/langchain-ai/langgraph/discussions/categories/q-a
|
||||
- name: Feature Request
|
||||
url: https://github.com/langchain-ai/langgraph/discussions/categories/ideas
|
||||
about: Suggest a feature or an idea
|
||||
- name: Show and tell
|
||||
about: Show what you built with LangChain
|
||||
url: https://github.com/langchain-ai/langgraph/discussions/categories/show-and-tell
|
||||
- name: Slack
|
||||
url: https://www.langchain.com/join-community
|
||||
about: General community discussions
|
||||
- name: LangChain Forum
|
||||
url: https://forum.langchain.com/
|
||||
about: General community discussions, support, and feature requests
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: Documentation
|
||||
description: Report an issue related to the LangGraph documentation.
|
||||
title: "DOC: <Please write a comprehensive title after the 'DOC: ' prefix>"
|
||||
labels: [03 - Documentation]
|
||||
labels: [documentation]
|
||||
|
||||
body:
|
||||
- type: textarea
|
||||
|
||||
@@ -1,25 +1,29 @@
|
||||
name: 🔒 Privileged
|
||||
description: You are a LangChain maintainer, or was asked directly by a maintainer to create an issue here. If not, check the other options.
|
||||
description: You are a LangGraph maintainer, or was asked directly by a maintainer to create an issue here. If not, check the other options.
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for your interest in LangChain! 🚀
|
||||
|
||||
If you are not a LangChain maintainer or were not asked directly by a maintainer to create an issue, then please start the conversation in a [Question in GitHub Discussions](https://github.com/langchain-ai/langchain/discussions/categories/q-a) instead.
|
||||
|
||||
You are a LangChain maintainer if you maintain any of the packages inside of the LangChain repository
|
||||
or are a regular contributor to LangChain with previous merged merged pull requests.
|
||||
Thanks for your interest in LangGraph! 🚀
|
||||
|
||||
If you are not a LangGraph maintainer or were not asked directly by a maintainer to create an issue, then please start the conversation on the [LangChain Forum](https://forum.langchain.com/) instead.
|
||||
|
||||
You are a LangGraph maintainer if you maintain any of the packages inside of the LangGraph repository
|
||||
or are a regular contributor to LangGraph with previous merged merged pull requests.
|
||||
- type: checkboxes
|
||||
id: privileged
|
||||
attributes:
|
||||
label: Privileged issue
|
||||
description: Confirm that you are allowed to create an issue here.
|
||||
options:
|
||||
- label: I am a LangChain maintainer, or was asked directly by a LangChain maintainer to create an issue here.
|
||||
- label: I am a LangGraph maintainer, or was asked directly by a LangGraph maintainer to create an issue here.
|
||||
required: true
|
||||
- type: textarea
|
||||
id: content
|
||||
attributes:
|
||||
label: Issue Content
|
||||
description: Add the content of the issue here.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Community members should **NOT** work on Privileged issues unless these issues have been explicitly marked with a "help-wanted" tag.
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
Thank you for contributing to LangGraph! Follow these steps to mark your pull request as ready for review. **If any of these steps are not completed, your PR will not be considered for review.**
|
||||
|
||||
- [ ] **PR title**: Follows the format: {TYPE}({SCOPE}): {DESCRIPTION}
|
||||
- Examples:
|
||||
- feat(core): add multi-tenant support
|
||||
- fix(cli): resolve flag parsing error
|
||||
- docs(openai): update API usage examples
|
||||
- Allowed `{TYPE}` values:
|
||||
- feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert, release
|
||||
- Allowed `{SCOPE}` values (optional):
|
||||
- langgraph, docs, cli, checkpoint, checkpoint-postgres, checkpoint-sqlite, prebuilt, scheduler-kafka, sdk-py
|
||||
- Once you've written the title, please delete this checklist item; do not include it in the PR.
|
||||
|
||||
- [ ] **PR message**: ***Delete this entire checklist*** and replace with
|
||||
- **Description:** a description of the change. Include a [closing keyword](https://docs.github.com/en/issues/tracking-your-work-with-issues/using-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword) if applicable.
|
||||
- **Issue:** the issue # it fixes, if applicable
|
||||
- **Dependencies:** any dependencies required for this change
|
||||
- **Twitter handle:** if your PR gets announced, and you'd like a mention, we'll gladly shout you out!
|
||||
|
||||
- [ ] **Add tests and docs**: If you're adding a new integration, you must include:
|
||||
1. A test for the integration, preferably unit tests that do not rely on network access,
|
||||
2. An example notebook showing its use. It lives in `docs/docs/integrations` directory.
|
||||
|
||||
- [ ] **Lint and test**: Run `make format`, `make lint` and `make test` from the root of the package(s) you've modified. We will not consider a PR unless these three are passing in CI. See [contribution guidelines](https://github.com/langchain-ai/langgraph/blob/main/CONTRIBUTING.md) for more.
|
||||
|
||||
Additional guidelines:
|
||||
|
||||
- Make sure optional dependencies are imported within a function.
|
||||
- Please do not add dependencies to `pyproject.toml` files (even optional ones) unless they are **required** for unit tests.
|
||||
- Most PRs should not touch more than one package.
|
||||
- Changes should be backwards compatible.
|
||||
+12
-5
@@ -1,11 +1,18 @@
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
# and
|
||||
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
|
||||
- package-ecosystem: "pip"
|
||||
directories:
|
||||
- "libs/checkpoint"
|
||||
- "libs/checkpoint-postgres"
|
||||
- "libs/checkpoint-sqlite"
|
||||
- "libs/cli"
|
||||
- "libs/langgraph"
|
||||
- "libs/prebuilt"
|
||||
- "libs/sdk-py"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
|
||||
@@ -3,6 +3,9 @@ name: CLI integration test
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -8,6 +8,9 @@ on:
|
||||
type: string
|
||||
description: "From which folder this pipeline executes"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
# This env var allows us to get inline annotations when ruff has complaints.
|
||||
RUFF_OUTPUT_FORMAT: github
|
||||
|
||||
@@ -8,6 +8,9 @@ on:
|
||||
type: string
|
||||
description: "From which folder this pipeline executes"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -3,6 +3,9 @@ name: test
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -11,6 +11,9 @@ on:
|
||||
env:
|
||||
PYTHON_VERSION: "3.10"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: github.ref == 'refs/heads/main'
|
||||
|
||||
@@ -7,6 +7,9 @@ on:
|
||||
paths:
|
||||
- "libs/**"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
benchmark:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -5,6 +5,9 @@ on:
|
||||
paths:
|
||||
- "libs/**"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
benchmark:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
+12
-60
@@ -3,9 +3,12 @@ name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
branches: [main, v1]
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
# If another push to the same PR or branch happens while this workflow is still running,
|
||||
# cancel the earlier run in favor of the next run.
|
||||
#
|
||||
@@ -21,7 +24,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
python: ${{ steps.filter.outputs.python }}
|
||||
sdk-js: ${{ steps.filter.outputs.sdk-js }}
|
||||
deps: ${{ steps.filter.outputs.deps }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dorny/paths-filter@v3
|
||||
@@ -36,8 +39,9 @@ jobs:
|
||||
- 'libs/checkpoint-sqlite/**'
|
||||
- 'libs/checkpoint-postgres/**'
|
||||
- 'libs/prebuilt/**'
|
||||
sdk-js:
|
||||
- 'libs/sdk-js/**'
|
||||
deps:
|
||||
- '**/pyproject.toml'
|
||||
- '**/uv.lock'
|
||||
|
||||
lint:
|
||||
needs: changes
|
||||
@@ -55,7 +59,7 @@ jobs:
|
||||
|
||||
"libs/prebuilt",
|
||||
]
|
||||
if: needs.changes.outputs.python == 'true'
|
||||
if: needs.changes.outputs.python == 'true' || needs.changes.outputs.deps == 'true'
|
||||
uses: ./.github/workflows/_lint.yml
|
||||
with:
|
||||
working-directory: ${{ matrix.working-directory }}
|
||||
@@ -74,7 +78,7 @@ jobs:
|
||||
"libs/checkpoint-postgres",
|
||||
"libs/prebuilt",
|
||||
]
|
||||
if: needs.changes.outputs.python == 'true'
|
||||
if: needs.changes.outputs.python == 'true' || needs.changes.outputs.deps == 'true'
|
||||
uses: ./.github/workflows/_test.yml
|
||||
with:
|
||||
working-directory: ${{ matrix.working-directory }}
|
||||
@@ -83,7 +87,7 @@ jobs:
|
||||
# NOTE: we're testing langgraph separately because it requires a different matrix
|
||||
test-langgraph:
|
||||
needs: changes
|
||||
if: needs.changes.outputs.python == 'true'
|
||||
if: needs.changes.outputs.python == 'true' || needs.changes.outputs.deps == 'true'
|
||||
name: "cd libs/langgraph"
|
||||
uses: ./.github/workflows/_test_langgraph.yml
|
||||
secrets: inherit
|
||||
@@ -140,73 +144,21 @@ jobs:
|
||||
|
||||
integration-test:
|
||||
needs: changes
|
||||
if: needs.changes.outputs.python == 'true'
|
||||
if: needs.changes.outputs.python == 'true' || needs.changes.outputs.deps == 'true'
|
||||
name: CLI integration test
|
||||
uses: ./.github/workflows/_integration_test.yml
|
||||
secrets: inherit
|
||||
|
||||
lint-js:
|
||||
needs: changes
|
||||
if: needs.changes.outputs.sdk-js == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
working-directory:
|
||||
- "libs/sdk-js"
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ matrix.working-directory }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js (LTS)
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
cache: "yarn"
|
||||
cache-dependency-path: ${{ matrix.working-directory }}/yarn.lock
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
- name: Run lint
|
||||
run: yarn lint
|
||||
- name: Build
|
||||
run: yarn build
|
||||
|
||||
test-js:
|
||||
needs: changes
|
||||
if: needs.changes.outputs.sdk-js == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
working-directory:
|
||||
- "libs/sdk-js"
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ matrix.working-directory }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js (LTS)
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
cache: "yarn"
|
||||
cache-dependency-path: ${{ matrix.working-directory }}/yarn.lock
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
- name: Run tests
|
||||
run: yarn test
|
||||
|
||||
ci_success:
|
||||
name: "CI Success"
|
||||
needs:
|
||||
[
|
||||
lint,
|
||||
lint-js,
|
||||
test,
|
||||
test-langgraph,
|
||||
check-sdk-methods,
|
||||
check-schema,
|
||||
integration-test,
|
||||
test-js,
|
||||
]
|
||||
if: |
|
||||
always()
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
LangChain
|
||||
LangGraph
|
||||
LangSmith
|
||||
thead
|
||||
stdio
|
||||
nd
|
||||
jupyter
|
||||
lets
|
||||
lite
|
||||
uis
|
||||
deque
|
||||
@@ -34,10 +34,16 @@
|
||||
id: extract_ignore_words
|
||||
|
||||
- name: Codespell
|
||||
uses: codespell-project/actions-codespell@v2
|
||||
uses: codespell-project/actions-codespell@v2.1
|
||||
with:
|
||||
skip: '*.ambr,*.lock,*.ipynb,*.yaml,*.zlib,*.md'
|
||||
skip: '*.ambr,*.lock,*.ipynb,*.yaml,*.zlib,*.css.map,*.js.map'
|
||||
ignore_words_list: ${{ steps.extract_ignore_words.outputs.ignore_words_list }}
|
||||
# We do this to avoid spellchecking cell outputs
|
||||
- name: Codespell Notebooks
|
||||
run: make codespell
|
||||
run: make codespell
|
||||
|
||||
- name: Codespell LangGraph Library
|
||||
run: |
|
||||
# Change to root directory to check the main LangGraph library
|
||||
cd ..
|
||||
codespell --skip="*.ambr,*.lock,*.ipynb,*.yaml,*.zlib,*.css.map,*.js.map,*.pyc,__pycache__/*" --ignore-words-list="${{ steps.extract_ignore_words.outputs.ignore_words_list }}" libs/langgraph/langgraph/
|
||||
@@ -4,11 +4,9 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v0
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- v0
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
@@ -37,16 +35,7 @@ jobs:
|
||||
with:
|
||||
filter: "docs/docs/**"
|
||||
|
||||
# TODO: Uncomment this to run on PRs
|
||||
# run-changed-notebooks:
|
||||
# needs: get-changed-files
|
||||
# uses: ./.github/workflows/run_notebooks.yml
|
||||
# secrets: inherit
|
||||
# with:
|
||||
# changed-files: ${{ needs.get-changed-files.outputs.changed-files }}
|
||||
|
||||
deploy:
|
||||
# needs: run-changed-notebooks
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10 # Job will be cancelled if it runs for more than 10 minutes
|
||||
env:
|
||||
@@ -84,9 +73,9 @@ jobs:
|
||||
run: make llms-text
|
||||
- name: Build site
|
||||
run: |
|
||||
# If this is v0 branch, then we want to download stats. we do this
|
||||
# If this is main branch, then we want to download stats. we do this
|
||||
# with the env variable DOWNLOAD_STATS=true
|
||||
if [ "${{ github.ref }}" == "refs/heads/v0" ]; then
|
||||
if [ "${{ github.ref }}" == "refs/heads/main" ]; then
|
||||
DOWNLOAD_STATS=true make build-docs
|
||||
else
|
||||
make build-docs
|
||||
@@ -146,7 +135,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Configure GitHub Pages
|
||||
if: github.ref == 'refs/heads/v0'
|
||||
if: github.ref == 'refs/heads/main'
|
||||
uses: actions/configure-pages@v5
|
||||
|
||||
- name: Upload Pages Artifact
|
||||
@@ -156,6 +145,6 @@ jobs:
|
||||
path: ./docs/site/
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
if: github.ref == 'refs/heads/v0'
|
||||
if: github.ref == 'refs/heads/main'
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
|
||||
@@ -11,6 +11,9 @@ on:
|
||||
- cron: "0 5 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
markdown-link-check:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
name: PR Title Lint
|
||||
|
||||
permissions:
|
||||
pull-requests: read
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, edited, synchronize]
|
||||
|
||||
jobs:
|
||||
lint-pr-title:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Validate PR Title
|
||||
uses: amannn/action-semantic-pull-request@v5
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
types: |
|
||||
feat
|
||||
fix
|
||||
docs
|
||||
style
|
||||
refactor
|
||||
perf
|
||||
test
|
||||
build
|
||||
ci
|
||||
chore
|
||||
revert
|
||||
release
|
||||
scopes: |
|
||||
checkpoint
|
||||
checkpoint-postgres
|
||||
checkpoint-sqlite
|
||||
cli
|
||||
langgraph
|
||||
prebuilt
|
||||
scheduler-kafka
|
||||
sdk-py
|
||||
docs
|
||||
ci
|
||||
requireScope: false
|
||||
ignoreLabels: |
|
||||
ignore-lint-pr-title
|
||||
@@ -8,6 +8,9 @@ on:
|
||||
type: string
|
||||
default: "libs/langgraph"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
PYTHON_VERSION: "3.11"
|
||||
|
||||
@@ -134,7 +137,9 @@ jobs:
|
||||
needs:
|
||||
- build
|
||||
- release-notes
|
||||
permissions: write-all
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
uses: ./.github/workflows/_test_release.yml
|
||||
with:
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
name: JS Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
# Disallow publishing from branches that aren't `main`.
|
||||
if: github.ref == 'refs/heads/main'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
working-directory:
|
||||
- "libs/sdk-js"
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ matrix.working-directory }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# JS Build
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
cache: "yarn"
|
||||
cache-dependency-path: ${{ matrix.working-directory }}/yarn.lock
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
- name: Build
|
||||
run: yarn build
|
||||
- name: Publish package to NPM
|
||||
run: |
|
||||
echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > .npmrc
|
||||
npm publish
|
||||
@@ -11,6 +11,9 @@ on:
|
||||
schedule:
|
||||
- cron: "0 13 * * *"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: docs
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
name: UV Lock Upgrade
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# run at midnight every Sunday
|
||||
- cron: '0 0 * * 0'
|
||||
# allow manual triggering
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
upgrade-dependencies:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
# use minimum supported Python version
|
||||
python-version: "3.9"
|
||||
enable-cache: true
|
||||
cache-suffix: "uv-lock-upgrade"
|
||||
|
||||
- name: Run uv lock --upgrade in all Python packages
|
||||
run: make lock-upgrade
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
commit-message: "chore[deps]: upgrade dependencies with `uv lock --upgrade`"
|
||||
title: "chore[deps]: upgrade dependencies with `uv lock --upgrade`"
|
||||
body: |
|
||||
This PR updates the dependencies in all Python packages using `uv lock --upgrade`.
|
||||
|
||||
This is an automated PR created by the UV Lock Upgrade workflow.
|
||||
branch: deps/uv-lock-upgrade
|
||||
delete-branch: true
|
||||
labels: |
|
||||
dependencies
|
||||
@@ -0,0 +1,55 @@
|
||||
# AGENTS Instructions
|
||||
|
||||
This repository is a monorepo. Each library lives in a subdirectory under `libs/`.
|
||||
|
||||
When you modify code in any library, run the following commands in that library's directory before creating a pull request:
|
||||
|
||||
- `make format` – run code formatters
|
||||
- `make lint` – run the linter
|
||||
- `make test` – execute the test suite
|
||||
|
||||
To run a particular test file or to pass additional pytest options you can specify the `TEST` variable:
|
||||
|
||||
```
|
||||
TEST=path/to/test.py make test
|
||||
```
|
||||
|
||||
Other pytest arguments can also be supplied inside the `TEST` variable.
|
||||
|
||||
## Libraries
|
||||
|
||||
The repository contains several Python and JavaScript/TypeScript libraries.
|
||||
Below is a high-level overview:
|
||||
|
||||
- **checkpoint** – base interfaces for LangGraph checkpointers.
|
||||
- **checkpoint-postgres** – Postgres implementation of the checkpoint saver.
|
||||
- **checkpoint-sqlite** – SQLite implementation of the checkpoint saver.
|
||||
- **cli** – official command-line interface for LangGraph.
|
||||
- **langgraph** – core framework for building stateful, multi-actor agents.
|
||||
- **prebuilt** – high-level APIs for creating and running agents and tools.
|
||||
- **sdk-js** – JS/TS SDK for interacting with the LangGraph REST API.
|
||||
- **sdk-py** – Python SDK for the LangGraph Platform API.
|
||||
|
||||
### Dependency map
|
||||
|
||||
The diagram below lists downstream libraries for each production dependency as
|
||||
declared in that library's `pyproject.toml` (or `package.json`).
|
||||
|
||||
```text
|
||||
checkpoint
|
||||
├── checkpoint-postgres
|
||||
├── checkpoint-sqlite
|
||||
├── prebuilt
|
||||
└── langgraph
|
||||
|
||||
prebuilt
|
||||
└── langgraph
|
||||
|
||||
sdk-py
|
||||
├── langgraph
|
||||
└── cli
|
||||
|
||||
sdk-js (standalone)
|
||||
```
|
||||
|
||||
Changes to a library may impact all of its dependents shown above.
|
||||
+9
-10
@@ -9,7 +9,7 @@ Here are some things to keep in mind for all types of contributions:
|
||||
- Follow the ["fork and pull request"](https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project) workflow.
|
||||
- Fill out the checked-in pull request template when opening pull requests. Note related issues and tag relevant maintainers.
|
||||
- Ensure your PR passes formatting, linting, and testing checks before requesting a review.
|
||||
- If you would like comments or feedback, please open an issue or discussion and tag a maintainer.
|
||||
- If you would like comments or feedback, please tag a maintainer.
|
||||
- Backwards compatibility is key. Your changes must not be breaking, except in case of critical bug and security fixes.
|
||||
- Look for duplicate PRs or issues that have already been opened before opening a new one.
|
||||
- Keep scope as isolated as possible. As a general rule, your changes should not affect more than one package at a time.
|
||||
@@ -20,7 +20,7 @@ For bug fixes, please open up an issue before proposing a fix to ensure the prop
|
||||
|
||||
### New features
|
||||
|
||||
For new features, please start a new [discussion](https://github.com/langchain-ai/langgraph/discussions), where the maintainers will help with scoping out the necessary changes.
|
||||
For new features, please start a new [discussion](https://forum.langchain.com/), where the maintainers will help with scoping out the necessary changes.
|
||||
|
||||
## Contribute Documentation
|
||||
|
||||
@@ -60,7 +60,7 @@ In LangGraph, these are often higher level guides that show off end-to-end use c
|
||||
Some examples include:
|
||||
|
||||
- [Build a Customer Support Bot](https://langchain-ai.github.io/langgraph/tutorials/customer-support/customer-support/)
|
||||
- [Build a SQL Agent](https://langchain-ai.github.io/langgraph/tutorials/sql-agent/)
|
||||
- [Build a SQL Agent](https://langchain-ai.github.io/langgraph/tutorials/sql/sql-agent/)
|
||||
|
||||
Here are some high-level tips on writing a good tutorial:
|
||||
|
||||
@@ -111,7 +111,6 @@ in a more abstract way than how-to guides or tutorials, and should be geared tow
|
||||
gaining a deeper understanding of the framework. Try to avoid excessively large code examples. The goal here is to
|
||||
impart perspective to the user rather than to finish a practical project. These guides should cover **why** things work the way they do.
|
||||
|
||||
|
||||
To quote the Diataxis website:
|
||||
|
||||
> The perspective of explanation is higher and wider than that of the other types. It does not take the user’s eye-level view, as in a how-to guide, or a close-up view of the machinery, like reference material. Its scope in each case is a topic - “an area of knowledge”, that somehow has to be bounded in a reasonable, meaningful way.
|
||||
@@ -187,9 +186,9 @@ Be concise, including in code samples.
|
||||
|
||||
## Setup
|
||||
|
||||
LangChain documentation consists of two components:
|
||||
LangGraph documentation consists of two components:
|
||||
|
||||
1. Main Documentation: Hosted at [https://langchain-ai.github.io](https://langchain-ai.github.io/langgraph/),
|
||||
1. Main Documentation: Hosted at [https://langchain-ai.github.io/langgraph/](https://langchain-ai.github.io/langgraph/),
|
||||
this comprehensive resource serves as the primary user-facing documentation.
|
||||
It covers a wide array of topics, including tutorials, use cases, integrations,
|
||||
and more, offering extensive guidance on building with LangGraph.
|
||||
@@ -250,17 +249,17 @@ make serve-docs
|
||||
|
||||
#### Linting
|
||||
|
||||
The documentation is linted from the **monorepo root**. To lint it, run the following from there:
|
||||
To spell check the docs, run the following from the `docs` directory:
|
||||
|
||||
```bash
|
||||
make spellcheck
|
||||
codespell --skip="*.ambr,*.lock,*.ipynb,*.yaml,*.zlib,*.css.map,*.js.map" --ignore-words-list="infor,thead,stdio,nd,jupyter,lets,lite,uis,deque" .
|
||||
```
|
||||
|
||||
### ️In-code Documentation
|
||||
|
||||
The in-code documentation is autogenerated from docstrings.
|
||||
|
||||
For the API reference to be useful, the codebase must be well-documented. This means that all functions, classes, and methods should have a docstring that explains what they do, what the arguments are, and what the return value is. This is a good practice in general, but it is especially important for LangChain because the API reference is the primary resource for developers to understand how to use the codebase.
|
||||
For the API reference to be useful, the codebase must be well-documented. This means that all functions, classes, and methods should have a docstring that explains what they do, what the arguments are, and what the return value is. This is a good practice in general, but it is especially important for LangGraph because the API reference is the primary resource for developers to understand how to use the codebase.
|
||||
|
||||
We generally follow the [Google Python Style Guide](https://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings) for docstrings.
|
||||
|
||||
@@ -291,4 +290,4 @@ def my_function(arg1: int, arg2: str) -> float:
|
||||
This is a description of the return value.
|
||||
"""
|
||||
return 3.14
|
||||
```
|
||||
```
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
# Define the directories containing projects
|
||||
LIBS_DIRS := $(wildcard libs/*)
|
||||
|
||||
# Default target
|
||||
.PHONY: all
|
||||
all: lint format lock test
|
||||
|
||||
# Install dependencies for all projects
|
||||
.PHONY: install
|
||||
install:
|
||||
@echo "Creating virtual environment..."
|
||||
@uv venv
|
||||
@for dir in $(LIBS_DIRS); do \
|
||||
if [ -f $$dir/pyproject.toml ]; then \
|
||||
echo "Installing dependencies for $$dir"; \
|
||||
uv pip install -e $$dir; \
|
||||
fi; \
|
||||
done
|
||||
|
||||
# Lint all projects
|
||||
.PHONY: lint
|
||||
lint:
|
||||
@for dir in $(LIBS_DIRS); do \
|
||||
if [ -f $$dir/Makefile ]; then \
|
||||
echo "Running lint in $$dir"; \
|
||||
$(MAKE) -C $$dir lint; \
|
||||
fi; \
|
||||
done
|
||||
|
||||
# Format all projects
|
||||
.PHONY: format
|
||||
format:
|
||||
@for dir in $(LIBS_DIRS); do \
|
||||
if [ -f $$dir/Makefile ]; then \
|
||||
echo "Running format in $$dir"; \
|
||||
$(MAKE) -C $$dir format; \
|
||||
fi; \
|
||||
done
|
||||
|
||||
# Lock all projects
|
||||
.PHONY: lock
|
||||
lock:
|
||||
@for dir in $(LIBS_DIRS); do \
|
||||
if [ -f $$dir/Makefile ]; then \
|
||||
echo "Running lock in $$dir"; \
|
||||
(cd $$dir && uv lock); \
|
||||
fi; \
|
||||
done
|
||||
|
||||
# Lock all projects and upgrade dependencies
|
||||
.PHONY: lock-upgrade
|
||||
lock-upgrade:
|
||||
@for dir in $(LIBS_DIRS); do \
|
||||
if [ -f $$dir/Makefile ]; then \
|
||||
echo "Running lock-upgrade in $$dir"; \
|
||||
(cd $$dir && uv lock --upgrade); \
|
||||
fi; \
|
||||
done
|
||||
|
||||
# Test all projects
|
||||
.PHONY: test
|
||||
test:
|
||||
@for dir in $(LIBS_DIRS); do \
|
||||
if [ -f $$dir/Makefile ]; then \
|
||||
echo "Running test in $$dir"; \
|
||||
$(MAKE) -C $$dir test; \
|
||||
fi; \
|
||||
done
|
||||
@@ -12,7 +12,6 @@
|
||||
[](https://pepy.tech/project/langgraph)
|
||||
[](https://github.com/langchain-ai/langgraph/issues)
|
||||
[](https://langchain-ai.github.io/langgraph/)
|
||||
[](https://gitmcp.io/langchain-ai/langgraph)
|
||||
|
||||
Trusted by companies shaping the future of agents – including Klarna, Replit, Elastic, and more – LangGraph is a low-level orchestration framework for building, managing, and deploying long-running, stateful agents.
|
||||
|
||||
@@ -64,7 +63,7 @@ LangGraph provides low-level supporting infrastructure for *any* long-running, s
|
||||
While LangGraph can be used standalone, it also integrates seamlessly with any LangChain product, giving developers a full suite of tools for building agents. To improve your LLM application development, pair LangGraph with:
|
||||
|
||||
- [LangSmith](http://www.langchain.com/langsmith) — Helpful for agent evals and observability. Debug poor-performing LLM app runs, evaluate agent trajectories, gain visibility in production, and improve performance over time.
|
||||
- [LangGraph Platform](https://langchain-ai.github.io/langgraph/concepts/#langgraph-platform) — Deploy and scale agents effortlessly with a purpose-built deployment platform for long running, stateful workflows. Discover, reuse, configure, and share agents across teams — and iterate quickly with visual prototyping in [LangGraph Studio](https://langchain-ai.github.io/langgraph/concepts/langgraph_studio/).
|
||||
- [LangGraph Platform](https://langchain-ai.github.io/langgraph/concepts/langgraph_platform/) — Deploy and scale agents effortlessly with a purpose-built deployment platform for long running, stateful workflows. Discover, reuse, configure, and share agents across teams — and iterate quickly with visual prototyping in [LangGraph Studio](https://langchain-ai.github.io/langgraph/concepts/langgraph_studio/).
|
||||
- [LangChain](https://python.langchain.com/docs/introduction/) – Provides integrations and composable components to streamline LLM application development.
|
||||
|
||||
> [!NOTE]
|
||||
@@ -74,11 +73,12 @@ While LangGraph can be used standalone, it also integrates seamlessly with any L
|
||||
|
||||
- [Guides](https://langchain-ai.github.io/langgraph/how-tos/): Quick, actionable code snippets for topics such as streaming, adding memory & persistence, and design patterns (e.g. branching, subgraphs, etc.).
|
||||
- [Reference](https://langchain-ai.github.io/langgraph/reference/graphs/): Detailed reference on core classes, methods, how to use the graph and checkpointing APIs, and higher-level prebuilt components.
|
||||
- [Examples](https://langchain-ai.github.io/langgraph/tutorials/overview/): Guided examples on getting started with LangGraph.
|
||||
- [Examples](https://langchain-ai.github.io/langgraph/examples/): Guided examples on getting started with LangGraph.
|
||||
- [LangChain Forum](https://forum.langchain.com/): Connect with the community and share all of your technical questions, ideas, and feedback.
|
||||
- [LangChain Academy](https://academy.langchain.com/courses/intro-to-langgraph): Learn the basics of LangGraph in our free, structured course.
|
||||
- [Templates](https://langchain-ai.github.io/langgraph/concepts/template_applications/): Pre-built reference apps for common agentic workflows (e.g. ReAct agent, memory, retrieval etc.) that can be cloned and adapted.
|
||||
- [Case studies](https://www.langchain.com/built-with-langgraph): Hear how industry leaders use LangGraph to ship AI applications at scale.
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
LangGraph is inspired by [Pregel](https://research.google/pubs/pub37252/) and [Apache Beam](https://beam.apache.org/). The public interface draws inspiration from [NetworkX](https://networkx.org/documentation/latest/). LangGraph is built by LangChain Inc, the creators of LangChain, but can be used without LangChain.
|
||||
LangGraph is inspired by [Pregel](https://research.google/pubs/pub37252/) and [Apache Beam](https://beam.apache.org/). The public interface draws inspiration from [NetworkX](https://networkx.org/documentation/latest/). LangGraph is built by LangChain Inc, the creators of LangChain, but can be used without LangChain.
|
||||
@@ -1,4 +1,3 @@
|
||||
site/
|
||||
docs/cloud/reference/sdk/js_ts_sdk_ref.md
|
||||
|
||||
.vercel
|
||||
|
||||
+3
-9
@@ -1,10 +1,4 @@
|
||||
.PHONY: lint-docs format-docs build-docs serve-docs serve-clean-docs clean-docs codespell build-typedoc llms-text build-prebuilt tests
|
||||
|
||||
build-typedoc:
|
||||
cd ../libs/sdk-js && yarn install --include-dev && yarn typedoc
|
||||
cd ../libs/sdk-js && yarn --silent concat-md --decrease-title-levels --ignore=js_ts_sdk_ref.md --start-title-level-at 2 docs > ../../docs/docs/cloud/reference/sdk/js_ts_sdk_ref.md 2>/dev/null
|
||||
# Add links to the monorepo
|
||||
sed -e '1,10s|@langchain/langgraph-sdk|[@langchain/langgraph-sdk](https://github.com/langchain-ai/langgraph/tree/main/libs/sdk-js)|g' docs/cloud/reference/sdk/js_ts_sdk_ref.md > temp_file && mv temp_file docs/cloud/reference/sdk/js_ts_sdk_ref.md
|
||||
.PHONY: lint-docs format-docs build-docs serve-docs serve-clean-docs clean-docs codespell llms-text build-prebuilt tests
|
||||
|
||||
build-prebuilt:
|
||||
# Use to create an update to date prebuilt page.
|
||||
@@ -21,7 +15,7 @@ build-prebuilt:
|
||||
fi
|
||||
uv run python -m _scripts.third_party_page.create_third_party_page stats.yml docs/agents/prebuilt.md --language python
|
||||
|
||||
build-docs: build-typedoc build-prebuilt
|
||||
build-docs: build-prebuilt
|
||||
uv run python -m mkdocs build --clean -f mkdocs.yml --strict
|
||||
|
||||
llms-text:
|
||||
@@ -45,7 +39,7 @@ vercel-build-docs: install-vercel-deps
|
||||
serve-clean-docs: clean-docs
|
||||
uv run python -m mkdocs serve -c -f mkdocs.yml --strict -w ../libs/langgraph
|
||||
|
||||
serve-docs: build-typedoc
|
||||
serve-docs:
|
||||
uv run python -m mkdocs serve -f mkdocs.yml -w ../libs/langgraph -w ../libs/checkpoint -w ../libs/sdk-py --dirty
|
||||
|
||||
clean-docs:
|
||||
|
||||
+119
-122
@@ -1,157 +1,154 @@
|
||||
"""Add typescript translation to a given markdown file."""
|
||||
"""Translate Python markdown to TypeScript and/or consolidate Python-JS markdown into a single document."""
|
||||
|
||||
import argparse
|
||||
import re
|
||||
|
||||
import requests
|
||||
from langchain_anthropic import ChatAnthropic
|
||||
|
||||
# Load reference TypeScript snippets
|
||||
URL = "https://gist.githubusercontent.com/eyurtsev/e7486731415463a9bc5b4682358859c8/raw/b5a5fda9c7e3387cfcb781f25082814d43675d50/gistfile1.txt"
|
||||
response = requests.get(URL)
|
||||
response.raise_for_status()
|
||||
reference_snippets = response.text
|
||||
|
||||
model = ChatAnthropic(model="claude-3-5-sonnet-latest")
|
||||
# Initialize model
|
||||
model = ChatAnthropic(model="claude-sonnet-4-0", max_tokens=64_000)
|
||||
|
||||
TRANSLATION_PROMPT = (
|
||||
"You are a helpful assistant that translates Python-based technical "
|
||||
"documentation written in Markdown to equivalent TypeScript-based documentation. "
|
||||
"The input is a Markdown file written in mkdocs format. It contains "
|
||||
"Python code snippets embedded in prose. "
|
||||
"Your task is to rewrite the content by translating the Python code to "
|
||||
"idiomatic TypeScript, using the provided TypeScript reference snippets "
|
||||
"to ensure accurate and consistent usage (e.g., correct imports, function "
|
||||
"names, and patterns). "
|
||||
"Remove the original Python code and replace it with the corresponding "
|
||||
"TypeScript version. "
|
||||
"Do not alter the surrounding prose unless a change is necessary to "
|
||||
"reflect differences between Python and TypeScript. "
|
||||
"Preserve the structure and formatting of the original Markdown document. "
|
||||
"Do not make stylistic or structural changes unless they directly support "
|
||||
"the translation. "
|
||||
"Use the reference TypeScript snippets as guidance whenever possible to "
|
||||
"maintain alignment with existing conventions.\n\n"
|
||||
f"Here are the reference TypeScript snippets:\n\n{reference_snippets}\n\n"
|
||||
)
|
||||
|
||||
CONSOLIDATION_PROMPT = (
|
||||
"You are a helpful assistant that consolidates parallel Python and JavaScript (TypeScript) technical documentation "
|
||||
"written in Markdown into a single unified Markdown document. "
|
||||
"The input consists of two documents: the first is for Python users, and the second is for JavaScript/TypeScript users. "
|
||||
"Your task is to merge these into one Markdown file using language-specific fenced blocks to separate the content where needed. "
|
||||
"Use the following syntax to distinguish content for each language:\n\n"
|
||||
":::python\n"
|
||||
"# Python-specific content\n"
|
||||
":::\n\n"
|
||||
":::js\n"
|
||||
"# JavaScript/TypeScript-specific content\n"
|
||||
":::\n\n"
|
||||
"Follow these consolidation rules:\n"
|
||||
"- When content (prose or code) is the same or nearly identical in both versions, include it only once—outside of any fenced block.\n"
|
||||
"- When content differs between the Python and JS versions, wrap each version in its corresponding fenced block.\n"
|
||||
"- Prefer **paragraph-level separation** of language-specific content. Do not combine Python and JS snippets or terminology in the same sentence or paragraph using conditional phrases.\n"
|
||||
" For example, avoid inline constructs like:\n"
|
||||
" `The :::python add_messages ::: :::js reducer ::: function...`\n"
|
||||
" Instead, write two distinct paragraphs:\n\n"
|
||||
" :::python\n"
|
||||
" The `add_messages` function in our `State` will append the LLM's response messages to whatever messages are already in the state.\n"
|
||||
" ::: \n\n"
|
||||
" :::js\n"
|
||||
" The `reducer` function in our `StateAnnotation` will append the LLM's response messages to whatever messages are already in the state.\n"
|
||||
" :::\n\n"
|
||||
"- Preserve the overall structure, ordering, and formatting of the original Markdown documents.\n"
|
||||
"- Do not rephrase or unify content unless it is logically and semantically identical.\n"
|
||||
"- Use the fenced blocks for both prose and code as needed, and ensure output is clean, readable Markdown suitable for tools that parse these directives.\n"
|
||||
"Your goal is to produce a cleanly merged documentation file that serves both Python and JavaScript users without redundancy, while maximizing clarity and separation of language-specific details."
|
||||
)
|
||||
|
||||
|
||||
def _get_tqdm():
|
||||
try:
|
||||
from tqdm import tqdm
|
||||
except ImportError:
|
||||
# If not available return a simple identity function
|
||||
def tqdm(iterable, *args, **kwargs):
|
||||
return iterable
|
||||
|
||||
return tqdm
|
||||
|
||||
|
||||
_tqdm = _get_tqdm()
|
||||
|
||||
opening_pattern = re.compile(r"^\s*```python(?:\s+.*)?\s*$")
|
||||
closing_pattern = re.compile(r"^\s*```\s*$")
|
||||
|
||||
|
||||
def extract_python_snippets(markdown: str) -> list[str]:
|
||||
"""
|
||||
Extract all python code blocks (including their fence lines) from the markdown content.
|
||||
A python block is defined as any block that starts with a line containing an opening fence
|
||||
with '```python' (optionally with extra parameters) and ends with a closing fence '```'.
|
||||
"""
|
||||
snippets = []
|
||||
inside_block = False
|
||||
current_snippet = []
|
||||
|
||||
for line in markdown.splitlines(keepends=True):
|
||||
if not inside_block:
|
||||
if opening_pattern.match(line):
|
||||
inside_block = True
|
||||
current_snippet = [line]
|
||||
else:
|
||||
current_snippet.append(line)
|
||||
if closing_pattern.match(line):
|
||||
inside_block = False
|
||||
snippets.append("".join(current_snippet))
|
||||
current_snippet = []
|
||||
return snippets
|
||||
|
||||
|
||||
def translate_snippet(python_snippet: str) -> str:
|
||||
"""Translate a python code block into a TypeScript code block using Langchain.
|
||||
The response is expected to be a properly fenced TypeScript code block (i.e.
|
||||
starting with ```typescript and ending with ```).
|
||||
"""
|
||||
ai_message = model.invoke(
|
||||
def translate_python_to_ts(markdown_content: str) -> str:
|
||||
response = model.invoke(
|
||||
[
|
||||
{
|
||||
"role": "system",
|
||||
"content": (
|
||||
f"You have access to the following up-to-date example TypeScript code "
|
||||
f"snippets that show examples of building with langgraph "
|
||||
f"and langchain:\n\n{reference_snippets}\n\n"
|
||||
"Use this context to translate the following Python code to equivalent "
|
||||
"TypeScript. Ensure that your output is a valid fenced TypeScript "
|
||||
"code block (i.e. starts with ```typescript and ends with ```)."
|
||||
),
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": f"Translate this Python snippet to TypeScript:\n\n{python_snippet}",
|
||||
"content": TRANSLATION_PROMPT,
|
||||
"cache_control": {"type": "ephemeral"},
|
||||
},
|
||||
{"role": "user", "content": markdown_content},
|
||||
]
|
||||
)
|
||||
|
||||
# Use a regular expression to search for a TypeScript code block in the response.
|
||||
pattern = r"```typescript\s*(.*?)\s*```"
|
||||
match = re.search(pattern, ai_message.content, re.DOTALL)
|
||||
if match:
|
||||
# Reconstruct the code block with proper fences.
|
||||
typescript_code = match.group(1).strip()
|
||||
return f"```typescript\n{typescript_code}\n```"
|
||||
else:
|
||||
raise ValueError("No TypeScript code block found in the model's response.")
|
||||
return response.content
|
||||
|
||||
|
||||
def insert_translations_into_markdown(
|
||||
markdown: str, typescript_snippets: list[str]
|
||||
) -> str:
|
||||
"""Walks through the original markdown content and, after each
|
||||
Python snippet block, inserts the corresponding translated TypeScript snippet.
|
||||
It assumes that the ordering of the Python snippets
|
||||
(from extract_python_snippets) matches the order they appear in the markdown.
|
||||
"""
|
||||
output_lines = []
|
||||
lines = markdown.splitlines(keepends=True)
|
||||
inside_block = False
|
||||
snippet_index = 0
|
||||
|
||||
for line in lines:
|
||||
output_lines.append(line)
|
||||
if not inside_block and opening_pattern.match(line):
|
||||
# We've encountered the start of a python code block.
|
||||
inside_block = True
|
||||
elif inside_block:
|
||||
if closing_pattern.match(line):
|
||||
# End of a python snippet block.
|
||||
inside_block = False
|
||||
if snippet_index < len(typescript_snippets):
|
||||
# Insert an extra newline for clarity, then the translated TypeScript snippet.
|
||||
output_lines.append("\n")
|
||||
output_lines.append(typescript_snippets[snippet_index])
|
||||
output_lines.append("\n")
|
||||
snippet_index += 1
|
||||
return "".join(output_lines)
|
||||
def consolidate_python_and_ts(combined_content: str) -> str:
|
||||
response = model.invoke(
|
||||
[
|
||||
{
|
||||
"role": "system",
|
||||
"content": CONSOLIDATION_PROMPT,
|
||||
"cache_control": {"type": "ephemeral"},
|
||||
},
|
||||
{"role": "user", "content": combined_content},
|
||||
]
|
||||
)
|
||||
return response.content
|
||||
|
||||
|
||||
def main(file_path: str) -> None:
|
||||
# Read the markdown file.
|
||||
with open(file_path, "r") as f:
|
||||
def main(file_path: str, translate_only: bool, consolidate_only: bool) -> None:
|
||||
with open(file_path, "r", encoding="utf-8") as f:
|
||||
markdown_content = f.read()
|
||||
|
||||
# 1. Extract all Python snippets.
|
||||
python_snippets = extract_python_snippets(markdown_content)[:1]
|
||||
if translate_only:
|
||||
translated = translate_python_to_ts(markdown_content)
|
||||
output_path = file_path.replace(".md", ".translated.md")
|
||||
with open(output_path, "w", encoding="utf-8") as f:
|
||||
f.write(translated)
|
||||
print(f"Translated JS/TS version written to: {output_path}")
|
||||
|
||||
# 2. Translate each Python snippet to TypeScript.
|
||||
typescript_snippets = []
|
||||
# Replace with .batch() for faster translation
|
||||
for python_snippet in _tqdm(python_snippets):
|
||||
ts_snippet = translate_snippet(python_snippet)
|
||||
typescript_snippets.append(ts_snippet)
|
||||
elif consolidate_only:
|
||||
consolidated = consolidate_python_and_ts(markdown_content)
|
||||
with open(file_path, "w", encoding="utf-8") as f:
|
||||
f.write(consolidated)
|
||||
print(f"Consolidated content written to: {file_path}")
|
||||
|
||||
# 3. Insert the TypeScript translations after their respective Python snippets.
|
||||
updated_markdown = insert_translations_into_markdown(
|
||||
markdown_content, typescript_snippets
|
||||
)
|
||||
|
||||
# Overwrite the original markdown file with the updated content.
|
||||
with open(file_path, "w") as f:
|
||||
f.write(updated_markdown)
|
||||
else:
|
||||
# Default behavior: translate first, then consolidate both
|
||||
translated = translate_python_to_ts(markdown_content)
|
||||
combined = f"{markdown_content.strip()}\n\n\n{translated.strip()}"
|
||||
consolidated = consolidate_python_and_ts(combined)
|
||||
with open(file_path, "w", encoding="utf-8") as f:
|
||||
f.write(consolidated)
|
||||
print(f"Translated and consolidated content written to: {file_path}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Translate Python snippets in a markdown file to TypeScript and insert them after each Python snippet."
|
||||
description=(
|
||||
"Translate Python markdown to TypeScript and/or consolidate "
|
||||
"Python-JS markdown into one file."
|
||||
)
|
||||
)
|
||||
parser.add_argument("file_path", type=str, help="Path to the markdown file.")
|
||||
parser.add_argument(
|
||||
"--translate-only",
|
||||
action="store_true",
|
||||
help="Only generate the JS translation.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--consolidate-only",
|
||||
action="store_true",
|
||||
help="Only consolidate pre-paired Python and JS content.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
main(args.file_path)
|
||||
if args.translate_only and args.consolidate_only:
|
||||
raise ValueError(
|
||||
"Cannot use both --translate-only and --consolidate-only at the same time."
|
||||
)
|
||||
|
||||
main(
|
||||
args.file_path,
|
||||
translate_only=args.translate_only,
|
||||
consolidate_only=args.consolidate_only,
|
||||
)
|
||||
|
||||
@@ -3,16 +3,15 @@
|
||||
import asyncio
|
||||
import glob
|
||||
import os
|
||||
from typing import TypedDict, List, Optional
|
||||
import pydantic
|
||||
import re
|
||||
from pydantic import BaseModel, Field
|
||||
from langchain_core.rate_limiters import InMemoryRateLimiter
|
||||
from typing import TypedDict, List, Optional
|
||||
|
||||
import yaml
|
||||
from langchain.chat_models import init_chat_model
|
||||
from langchain_core.rate_limiters import InMemoryRateLimiter
|
||||
from mkdocs.structure.files import File
|
||||
from mkdocs.structure.pages import Page
|
||||
from pydantic import BaseModel, Field
|
||||
from yaml import SafeLoader
|
||||
|
||||
from _scripts.notebook_hooks import _on_page_markdown_with_config
|
||||
@@ -211,7 +210,9 @@ async def process_nav_items(nav_items: list[NavItem]) -> list[NavItem]:
|
||||
# Remove any items that start with http:// or https:// looking only for
|
||||
# local file at this stages.
|
||||
nav_items = [
|
||||
item for item in nav_items if not item["url"].startswith(("http://", "https://"))
|
||||
item
|
||||
for item in nav_items
|
||||
if not item["url"].startswith(("http://", "https://"))
|
||||
]
|
||||
# Process items in parallel
|
||||
tasks = [process_single_item(item) for item in nav_items]
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
JS_LINK_MAP = {
|
||||
"langgraph.types.interrupt": "https://langchain-ai.github.io/langgraphjs/reference/functions/langgraph.interrupt-2.html",
|
||||
"create_react_agent": "https://langchain-ai.github.io/langgraphjs/reference/functions/langgraph_prebuilt.createReactAgent.html",
|
||||
"langgraph.types.Command": "https://langchain-ai.github.io/langgraphjs/reference/classes/langgraph.Command.html",
|
||||
}
|
||||
+165
-42
@@ -3,6 +3,7 @@
|
||||
Lifecycle events: https://www.mkdocs.org/dev-guide/plugins/#events
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import posixpath
|
||||
@@ -15,6 +16,7 @@ from mkdocs.structure.files import Files, File
|
||||
from mkdocs.structure.pages import Page
|
||||
|
||||
from _scripts.generate_api_reference_links import update_markdown_with_imports
|
||||
from _scripts.link_map import JS_LINK_MAP
|
||||
from _scripts.notebook_convert import convert_notebook
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -33,43 +35,49 @@ REDIRECT_MAP = {
|
||||
"how-tos/streaming-from-final-node.ipynb": "how-tos/streaming-specific-nodes.ipynb",
|
||||
"how-tos/streaming-events-from-within-tools-without-langchain.ipynb": "how-tos/streaming-events-from-within-tools.ipynb#example-without-langchain",
|
||||
# graph-api
|
||||
"how-tos/state-reducers.ipynb": "how-tos/graph-api#define-and-update-state",
|
||||
"how-tos/sequence.ipynb": "how-tos/graph-api#create-a-sequence-of-steps",
|
||||
"how-tos/branching.ipynb": "how-tos/graph-api#create-branches",
|
||||
"how-tos/recursion-limit.ipynb": "how-tos/graph-api#create-and-control-loops",
|
||||
"how-tos/visualization.ipynb": "how-tos/graph-api#visualize-your-graph",
|
||||
"how-tos/input_output_schema.ipynb": "how-tos/graph-api#define-input-and-output-schemas",
|
||||
"how-tos/pass_private_state.ipynb": "how-tos/graph-api#pass-private-state-between-nodes",
|
||||
"how-tos/state-model.ipynb": "how-tos/graph-api#use-pydantic-models-for-graph-state",
|
||||
"how-tos/map-reduce.ipynb": "how-tos/graph-api/#map-reduce-and-the-send-api",
|
||||
"how-tos/command.ipynb": "how-tos/graph-api/#combine-control-flow-and-state-updates-with-command",
|
||||
"how-tos/configuration.ipynb": "how-tos/graph-api/#add-runtime-configuration",
|
||||
"how-tos/node-retries.ipynb": "how-tos/graph-api/#add-retry-policies",
|
||||
"how-tos/return-when-recursion-limit-hits.ipynb": "how-tos/graph-api/#impose-a-recursion-limit",
|
||||
"how-tos/async.ipynb": "how-tos/graph-api/#async",
|
||||
"how-tos/state-reducers.ipynb": "how-tos/graph-api.md#define-and-update-state",
|
||||
"how-tos/sequence.ipynb": "how-tos/graph-api.md#create-a-sequence-of-steps",
|
||||
"how-tos/branching.ipynb": "how-tos/graph-api.md#create-branches",
|
||||
"how-tos/recursion-limit.ipynb": "how-tos/graph-api.md#create-and-control-loops",
|
||||
"how-tos/visualization.ipynb": "how-tos/graph-api.md#visualize-your-graph",
|
||||
"how-tos/input_output_schema.ipynb": "how-tos/graph-api.md#define-input-and-output-schemas",
|
||||
"how-tos/pass_private_state.ipynb": "how-tos/graph-api.md#pass-private-state-between-nodes",
|
||||
"how-tos/state-model.ipynb": "how-tos/graph-api.md#use-pydantic-models-for-graph-state",
|
||||
"how-tos/map-reduce.ipynb": "how-tos/graph-api.md#map-reduce-and-the-send-api",
|
||||
"how-tos/command.ipynb": "how-tos/graph-api.md#combine-control-flow-and-state-updates-with-command",
|
||||
"how-tos/configuration.ipynb": "how-tos/graph-api.md#add-runtime-configuration",
|
||||
"how-tos/node-retries.ipynb": "how-tos/graph-api.md#add-retry-policies",
|
||||
"how-tos/return-when-recursion-limit-hits.ipynb": "how-tos/graph-api.md#impose-a-recursion-limit",
|
||||
"how-tos/async.ipynb": "how-tos/graph-api.md#async",
|
||||
# memory how-tos
|
||||
"how-tos/memory/manage-conversation-history.ipynb": "how-tos/memory.ipynb",
|
||||
"how-tos/memory/delete-messages.ipynb": "how-tos/memory.ipynb#delete-messages",
|
||||
"how-tos/memory/add-summary-conversation-history.ipynb": "how-tos/memory.ipynb#summarize-messages",
|
||||
"how-tos/memory/manage-conversation-history.ipynb": "how-tos/memory/add-memory.md",
|
||||
"how-tos/memory/delete-messages.ipynb": "how-tos/memory/add-memory.md#delete-messages",
|
||||
"how-tos/memory/add-summary-conversation-history.ipynb": "how-tos/memory/add-memory.md#summarize-messages",
|
||||
"how-tos/memory.ipynb": "how-tos/memory/add-memory.md",
|
||||
"agents/memory.ipynb": "how-tos/memory/add-memory.md",
|
||||
# subgraph how-tos
|
||||
"how-tos/subgraph-transform-state.ipynb": "how-tos/subgraph.ipynb#different-state-schemas",
|
||||
"how-tos/subgraphs-manage-state.ipynb": "how-tos/subgraph.ipynb#add-persistence",
|
||||
"how-tos/subgraph-transform-state.ipynb": "how-tos/subgraph.md#different-state-schemas",
|
||||
"how-tos/subgraphs-manage-state.ipynb": "how-tos/subgraph.md#add-persistence",
|
||||
# persistence how-tos
|
||||
"how-tos/persistence_postgres.ipynb": "how-tos/persistence.ipynb#use-in-production",
|
||||
"how-tos/persistence_mongodb.ipynb": "how-tos/persistence.ipynb#use-in-production",
|
||||
"how-tos/persistence_redis.ipynb": "how-tos/persistence.ipynb#use-in-production",
|
||||
"how-tos/subgraph-persistence.ipynb": "how-tos/persistence.ipynb#use-with-subgraphs",
|
||||
"how-tos/cross-thread-persistence.ipynb": "how-tos/persistence.ipynb#add-long-term-memory",
|
||||
"how-tos/persistence_postgres.ipynb": "how-tos/memory/add-memory.md#use-in-production",
|
||||
"how-tos/persistence_mongodb.ipynb": "how-tos/memory/add-memory.md#use-in-production",
|
||||
"how-tos/persistence_redis.ipynb": "how-tos/memory/add-memory.md#use-in-production",
|
||||
"how-tos/subgraph-persistence.ipynb": "how-tos/memory/add-memory.md#use-with-subgraphs",
|
||||
"how-tos/cross-thread-persistence.ipynb": "how-tos/memory/add-memory.md#add-long-term-memory",
|
||||
"cloud/how-tos/copy_threads": "cloud/how-tos/use_threads",
|
||||
"cloud/how-tos/check-thread-status": "cloud/how-tos/use_threads",
|
||||
"cloud/concepts/threads.md": "concepts/persistence.md#threads",
|
||||
"how-tos/persistence.ipynb": "how-tos/memory/add-memory.md",
|
||||
# tool calling how-tos
|
||||
"how-tos/tool-calling-errors.ipynb": "how-tos/tool-calling.ipynb#handle-errors",
|
||||
"how-tos/pass-config-to-tools.ipynb": "how-tos/tool-calling.ipynb#access-config",
|
||||
"how-tos/pass-run-time-values-to-tools.ipynb": "how-tos/tool-calling.ipynb#read-state",
|
||||
"how-tos/update-state-from-tools.ipynb": "how-tos/tool-calling.ipynb#update-state",
|
||||
"agents/tools.md": "how-tos/tool-calling.md",
|
||||
# multi-agent how-tos
|
||||
"how-tos/agent-handoffs.ipynb": "how-tos/multi_agent.ipynb#handoffs",
|
||||
"how-tos/multi-agent-network.ipynb": "how-tos/multi_agent.ipynb#use-in-a-multi-agent-system",
|
||||
"how-tos/multi-agent-multi-turn-convo.ipynb": "how-tos/multi_agent.ipynb#multi-turn-conversation",
|
||||
"how-tos/agent-handoffs.ipynb": "how-tos/multi_agent.md#handoffs",
|
||||
"how-tos/multi-agent-network.ipynb": "how-tos/multi_agent.md#use-in-a-multi-agent-system",
|
||||
"how-tos/multi-agent-multi-turn-convo.ipynb": "how-tos/multi_agent.md#multi-turn-conversation",
|
||||
# cloud redirects
|
||||
"cloud/index.md": "index.md",
|
||||
"cloud/how-tos/index.md": "concepts/langgraph_platform",
|
||||
@@ -86,16 +94,13 @@ REDIRECT_MAP = {
|
||||
"cloud/how-tos/stream_events.md": "cloud/how-tos/streaming.md#stream-events",
|
||||
"cloud/how-tos/stream_debug.md": "cloud/how-tos/streaming.md#debug",
|
||||
"cloud/how-tos/stream_multiple.md": "cloud/how-tos/streaming.md#stream-multiple-modes",
|
||||
# prebuit redirects
|
||||
"cloud/concepts/streaming.md": "concepts/streaming.md",
|
||||
"agents/streaming.md": "how-tos/streaming.md",
|
||||
# prebuilt redirects
|
||||
"how-tos/create-react-agent.ipynb": "agents/agents.md#basic-configuration",
|
||||
"how-tos/create-react-agent-memory.ipynb": "agents/memory.md",
|
||||
"how-tos/create-react-agent-system-prompt.ipynb": "agents/context.md#prompts",
|
||||
"how-tos/create-react-agent-hitl.ipynb": "agents/human-in-the-loop.md",
|
||||
"how-tos/create-react-agent-structured-output.ipynb": "agents/agents.md#structured-output",
|
||||
# Time-travel
|
||||
"how-tos/human_in_the_loop/edit-graph-state.ipynb": "how-tos/human_in_the_loop/time-travel.ipynb",
|
||||
# breakpoints
|
||||
"how-tos/human_in_the_loop/dynamic_breakpoints.ipynb": "how-tos/human_in_the_loop/breakpoints.ipynb",
|
||||
# misc
|
||||
"prebuilt.md": "agents/prebuilt.md",
|
||||
"reference/prebuilt.md": "reference/agents.md",
|
||||
@@ -104,11 +109,24 @@ REDIRECT_MAP = {
|
||||
"concepts/v0-human-in-the-loop.md": "concepts/human-in-the-loop.md",
|
||||
"how-tos/index.md": "index.md",
|
||||
"tutorials/introduction.ipynb": "concepts/why-langgraph.md",
|
||||
"agents/deployment.md": "tutorials/langgraph-platform/local-server.md",
|
||||
# deployment redirects
|
||||
"how-tos/deploy-self-hosted.md": "cloud/deployment/self_hosted_data_plane.md",
|
||||
"concepts/self_hosted.md": "concepts/langgraph_self_hosted_data_plane.md",
|
||||
"tutorials/deployment.md": "concepts/deployment_options.md",
|
||||
# assistant redirects
|
||||
"cloud/how-tos/assistant_versioning.md": "cloud/how-tos/configuration_cloud.md",
|
||||
"cloud/concepts/runs.md": "concepts/assistants.md#execution",
|
||||
# hitl redirects
|
||||
"how-tos/wait-user-input-functional.ipynb": "how-tos/use-functional-api.md",
|
||||
"how-tos/review-tool-calls-functional.ipynb": "how-tos/use-functional-api.md",
|
||||
"how-tos/create-react-agent-hitl.ipynb": "how-tos/human_in_the_loop/add-human-in-the-loop.md",
|
||||
"agents/human-in-the-loop.md": "how-tos/human_in_the_loop/add-human-in-the-loop.md",
|
||||
"how-tos/human_in_the_loop/dynamic_breakpoints.ipynb": "how-tos/human_in_the_loop/breakpoints.md",
|
||||
"concepts/breakpoints.md": "concepts/human_in_the_loop.md",
|
||||
"how-tos/human_in_the_loop/breakpoints.md": "how-tos/human_in_the_loop/add-human-in-the-loop.md",
|
||||
"cloud/how-tos/human_in_the_loop_breakpoint.md": "cloud/how-tos/add-human-in-the-loop.md",
|
||||
"how-tos/human_in_the_loop/edit-graph-state.ipynb": "how-tos/human_in_the_loop/time-travel.md",
|
||||
}
|
||||
|
||||
|
||||
@@ -158,6 +176,62 @@ def _add_path_to_code_blocks(markdown: str, page: Page) -> str:
|
||||
return code_block_pattern.sub(replace_code_block_header, markdown)
|
||||
|
||||
|
||||
def _resolve_cross_references(md_text: str, link_map: dict[str, str]) -> str:
|
||||
"""Replace [title][identifier] with [title](url) using language-specific link_map.
|
||||
|
||||
Args:
|
||||
md_text: The markdown text to process.
|
||||
link_map: mapping of identifier to URL.
|
||||
|
||||
Returns:
|
||||
The processed markdown text with cross-references resolved.
|
||||
"""
|
||||
# Pattern to match [title][identifier]
|
||||
pattern = re.compile(r"\[([^\]]+)\]\[([^\]]+)\]")
|
||||
|
||||
def replace_reference(match: re.Match) -> str:
|
||||
"""Replace the matched reference with the corresponding URL."""
|
||||
title, identifier = match.group(1), match.group(2)
|
||||
url = link_map.get(identifier)
|
||||
|
||||
if url:
|
||||
return f"[{title}]({url})"
|
||||
else:
|
||||
# Leave it unchanged if not found
|
||||
return match.group(0)
|
||||
|
||||
return pattern.sub(replace_reference, md_text)
|
||||
|
||||
|
||||
def _apply_conditional_rendering(md_text: str, target_language: str) -> str:
|
||||
if target_language not in {"python", "js"}:
|
||||
raise ValueError("target_language must be 'python' or 'js'")
|
||||
|
||||
pattern = re.compile(
|
||||
r"(?P<indent>[ \t]*):::(?P<language>\w+)\s*\n"
|
||||
r"(?P<content>((?:.*\n)*?))" # Capture the content inside the block
|
||||
r"(?P=indent):::" # Match closing with the same indentation
|
||||
)
|
||||
|
||||
def replace_conditional_blocks(match: re.Match) -> str:
|
||||
"""Keep active conditionals."""
|
||||
language = match.group("language")
|
||||
content = match.group("content")
|
||||
|
||||
if language not in {"python", "js"}:
|
||||
# If the language is not supported, return the original block
|
||||
return match.group(0)
|
||||
|
||||
if language == target_language:
|
||||
return content
|
||||
|
||||
# If the language does not match, return an empty string
|
||||
return ""
|
||||
|
||||
processed = pattern.sub(replace_conditional_blocks, md_text)
|
||||
return processed
|
||||
|
||||
|
||||
def _highlight_code_blocks(markdown: str) -> str:
|
||||
"""Find code blocks with highlight comments and add hl_lines attribute.
|
||||
|
||||
@@ -257,6 +331,20 @@ def _on_page_markdown_with_config(
|
||||
# Apply highlight comments to code blocks
|
||||
markdown = _highlight_code_blocks(markdown)
|
||||
|
||||
# Apply conditional rendering for code blocks
|
||||
target_language = kwargs.get("target_language", "python")
|
||||
markdown = _apply_conditional_rendering(markdown, target_language)
|
||||
if target_language == "js":
|
||||
markdown = _resolve_cross_references(markdown, JS_LINK_MAP)
|
||||
elif target_language == "python":
|
||||
# Via a dedicated plugin
|
||||
pass
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Unsupported target language: {target_language}. "
|
||||
"Supported languages are 'python' and 'js'."
|
||||
)
|
||||
|
||||
# Add file path as an attribute to code blocks that are executable.
|
||||
# This file path is used to associate fixtures with the executable code
|
||||
# which can be used in CI to test the docs without making network requests.
|
||||
@@ -270,12 +358,16 @@ def _on_page_markdown_with_config(
|
||||
|
||||
|
||||
def on_page_markdown(markdown: str, page: Page, **kwargs: Dict[str, Any]):
|
||||
return _on_page_markdown_with_config(
|
||||
markdown,
|
||||
page,
|
||||
add_api_references=True,
|
||||
**kwargs,
|
||||
finalized_markdown = (
|
||||
_on_page_markdown_with_config(
|
||||
markdown,
|
||||
page,
|
||||
add_api_references=True,
|
||||
**kwargs,
|
||||
)
|
||||
)
|
||||
page.meta["original_markdown"] = finalized_markdown
|
||||
return finalized_markdown
|
||||
|
||||
|
||||
# redirects
|
||||
@@ -345,20 +437,51 @@ height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
|
||||
else:
|
||||
return html # fallback if no <body> found
|
||||
|
||||
def _inject_markdown_into_html(html: str, page: Page) -> str:
|
||||
"""Inject the original markdown content into the HTML page as JSON."""
|
||||
original_markdown = page.meta.get("original_markdown", "")
|
||||
if not original_markdown:
|
||||
return html
|
||||
markdown_data = {
|
||||
"markdown": original_markdown,
|
||||
"title": page.title or "Page Content",
|
||||
"url": page.url or "",
|
||||
}
|
||||
|
||||
def on_post_page(output: str, page: Page, config: MkDocsConfig) -> str:
|
||||
# Properly escape the JSON for HTML
|
||||
json_content = json.dumps(markdown_data, ensure_ascii=False)
|
||||
|
||||
json_content = (
|
||||
json_content.replace("</", "\\u003c/")
|
||||
.replace("<script", "\\u003cscript")
|
||||
.replace("</script", "\\u003c/script")
|
||||
)
|
||||
|
||||
script_content = (
|
||||
f'<script id="page-markdown-content" '
|
||||
f'type="application/json">{json_content}</script>'
|
||||
)
|
||||
|
||||
# Insert before </head> if it exists, otherwise before </body>
|
||||
if "</head>" not in html:
|
||||
raise ValueError(
|
||||
"HTML does not contain </head> tag. Cannot inject markdown content."
|
||||
)
|
||||
return html.replace("</head>", f"{script_content}</head>")
|
||||
|
||||
def on_post_page(html: str, page: Page, config: MkDocsConfig) -> str:
|
||||
"""Inject Google Tag Manager noscript tag immediately after <body>.
|
||||
|
||||
Args:
|
||||
output: The HTML output of the page.
|
||||
html: The HTML output of the page.
|
||||
page: The page instance.
|
||||
config: The MkDocs configuration object.
|
||||
|
||||
Returns:
|
||||
modified HTML output with GTM code injected.
|
||||
"""
|
||||
return _inject_gtm(output)
|
||||
|
||||
html = _inject_markdown_into_html(html, page)
|
||||
return _inject_gtm(html)
|
||||
|
||||
# Create HTML files for redirects after site dir has been built
|
||||
def on_post_build(config):
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# Additional resources
|
||||
|
||||
This section contains additional resources for LangGraph.
|
||||
|
||||
- [Community agents](../agents/prebuilt.md): A collection of prebuilt libraries that you can use in your LangGraph applications.
|
||||
- [LangGraph Academy](https://academy.langchain.com/courses/intro-to-langgraph): A collection of courses that teach you how to use LangGraph.
|
||||
- [Case studies](../adopters.md): A collection of case studies that show how LangGraph is used in production.
|
||||
- [FAQ](../concepts/faq.md): A collection of frequently asked questions about LangGraph.
|
||||
- [llms.txt](../llms-txt-overview.md): A list of documentation files in the `llms.txt` format that allow LLMs and agents to access our documentation.
|
||||
- [LangChain Forum](https://forum.langchain.com/): A place to ask questions and get help from other LangGraph users.
|
||||
- [Troubleshooting](../troubleshooting/errors/index.md): A collection of troubleshooting guides for common issues.
|
||||
+23
-6
@@ -8,24 +8,41 @@ This list of companies using LangGraph and their success stories is compiled fro
|
||||
| [AirTop](https://www.airtop.ai/) | Software & Technology (GenAI Native) | Browser automation for AI agents | [Case study, 2024](https://blog.langchain.dev/customers-airtop/) |
|
||||
| [AppFolio](https://www.appfolio.com/) | Real Estate | Copilot for domain-specific task | [Case study, 2024](https://blog.langchain.dev/customers-appfolio/) |
|
||||
| [Athena Intelligence](https://www.athenaintel.com/) | Software & Technology (GenAI Native) | Research & summarization | [Case study, 2024](https://blog.langchain.dev/customers-athena-intelligence/) |
|
||||
| [BlackRock](https://www.blackrock.com/) | Financial Services | Copilot for domain-specific task | [Interrupt talk, 2025](https://youtu.be/oyqeCHFM5U4?feature=shared) |
|
||||
| [Captide](https://www.captide.co/) | Software & Technology (GenAI Native) | Data extraction | [Case study, 2025](https://blog.langchain.dev/how-captide-is-redefining-equity-research-with-agentic-workflows-built-on-langgraph-and-langsmith/) |
|
||||
| [Cisco Outshift](https://outshift.cisco.com/) | Software & Technology | DevOps | [Blog post, 2025](https://outshift.cisco.com/blog/build-react-agent-application-for-devops-tasks-using-rest-apis) |
|
||||
| [Cisco CX](https://www.cisco.com/site/us/en/services/modern-data-center/index.html?CCID=cc005911&DTID=eivtotr001480&OID=srwsas032775) | Software & Technology | Customer support | [Interrupt Talk, 2025](https://youtu.be/gPhyPRtIMn0?feature=shared) |
|
||||
| [Cisco Outshift](https://outshift.cisco.com/) | Software & Technology | DevOps | [Video story, 2025](https://www.youtube.com/watch?v=htcb-vGR_x0); [Case study, 2025](https://blog.langchain.com/cisco-outshift/); [Blog post, 2025](https://outshift.cisco.com/blog/build-react-agent-application-for-devops-tasks-using-rest-apis) |
|
||||
| [Cisco TAC](https://www.cisco.com/c/en/us/support/index.html) | Software & Technology | Customer support | [Video story, 2025](https://youtu.be/EAj0HBDGqaE?feature=shared) |
|
||||
| [City of Hope](https://www.cityofhope.org/) | Non-profit | Copilot for domain-specific task | [Video story, 2025](https://youtu.be/9ABwtK2gIZU?feature=shared) |
|
||||
| [C.H. Robinson](https://www.chrobinson.com/en-us/) | Logistics | Automation | [Case study, 2025](https://blog.langchain.dev/customers-chrobinson/) |
|
||||
| [Definely](https://www.definely.com/) | Legal | Copilot for domain-specific task | [Case study, 2025](https://blog.langchain.com/customers-definely/) |
|
||||
| [Docent Pro](https://docentpro.com/) | Travel | GenAI embedded product experiences | [Case study, 2025](https://blog.langchain.com/customers-docentpro/) |
|
||||
| [Elastic](https://www.elastic.co/) | Software & Technology | Copilot for domain-specific task | [Blog post, 2025](https://www.elastic.co/blog/elastic-security-generative-ai-features) |
|
||||
| [Exa](https://exa.ai/) | Software & Technology (GenAI Native) | Search | [Case study, 2025](https://blog.langchain.com/exa/) |
|
||||
| [GitLab](https://about.gitlab.com/) | Software & Technology | Code generation | [Duo workflow docs](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/duo_workflow/) |
|
||||
| [Harmonic](https://harmonic.ai/) | Software & Technology | Search | [Case study, 2025](https://blog.langchain.com/customers-harmonic/) |
|
||||
| [Inconvo](https://inconvo.ai/?ref=blog.langchain.dev) | Software & Technology | Code generation | [Case study, 2025](https://blog.langchain.dev/customers-inconvo/) |
|
||||
| [Infor](https://infor.com/) | Software & Technology | GenAI embedded product experiences; customer support; copilot | [Case study, 2025](https://blog.langchain.dev/customers-infor/) |
|
||||
| [J.P. Morgan](https://www.jpmorganchase.com/) | Financial Services | Copilot for domain-specific task | [Interrupt talk, 2025](https://youtu.be/yMalr0jiOAc?feature=shared) |
|
||||
| [Klarna](https://www.klarna.com/) | Fintech | Copilot for domain-specific task | [Case study, 2025](https://blog.langchain.dev/customers-klarna/) |
|
||||
| [Komodo Health](https://www.komodohealth.com/) | Healthcare | Copilot for domain-specific task | [Blog post](https://www.komodohealth.com/perspectives/new-gen-ai-assistant-empowers-the-enterprise/) |
|
||||
| [LinkedIn](https://www.linkedin.com/) | Social Media | Code generation; Search & discovery | [Blog post, 2025](https://www.linkedin.com/blog/engineering/ai/practical-text-to-sql-for-data-analytics); [Blog post, 2024](https://www.linkedin.com/blog/engineering/generative-ai/behind-the-platform-the-journey-to-create-the-linkedin-genai-application-tech-stack) |
|
||||
| [LinkedIn](https://www.linkedin.com/) | Social Media | Code generation; Search & discovery | [Interrupt talk, 2025](https://youtu.be/NmblVxyBhi8?feature=shared); [Blog post, 2025](https://www.linkedin.com/blog/engineering/ai/practical-text-to-sql-for-data-analytics); [Blog post, 2024](https://www.linkedin.com/blog/engineering/generative-ai/behind-the-platform-the-journey-to-create-the-linkedin-genai-application-tech-stack) |
|
||||
| [Minimal](https://gominimal.ai/) | E-commerce | Customer support | [Case study, 2025](https://blog.langchain.dev/how-minimal-built-a-multi-agent-customer-support-system-with-langgraph-langsmith/) |
|
||||
| [Modern Treasury](https://www.moderntreasury.com/) | Fintech | GenAI embedded product experiences | [Video story, 2025](https://youtu.be/AwAiffXqaCU?feature=shared) |
|
||||
| [Monday](https://monday.com/) | Software & Technology | GenAI embedded product experiences | [Interrupt talk, 2025](https://blog.langchain.dev/how-minimal-built-a-multi-agent-customer-support-system-with-langgraph-langsmith/) |
|
||||
| [Morningstar](https://www.morningstar.com/) | Financial Services | Research & summarization | [Video story, 2025](https://youtu.be/6LidoFXCJPs?feature=shared) |
|
||||
| [OpenRecovery](https://www.openrecovery.com/) | Healthcare | Copilot for domain-specific task | [Case study, 2024](https://blog.langchain.dev/customers-openrecovery/) |
|
||||
| [Pigment](https://www.pigment.com/) | Fintech | GenAI embedded product experiences | [Video story, 2025](https://youtu.be/5JVSO2KYOmE?feature=shared) |
|
||||
| [Prosper](https://www.prosper.com/) | Fintech | Customer support | [Video story, 2025](https://youtu.be/9RFNOYtkwsc?feature=shared) |
|
||||
| [Qodo](https://www.qodo.ai/) | Software & Technology (GenAI Native) | Code generation | [Blog post, 2025](https://www.qodo.ai/blog/why-we-chose-langgraph-to-build-our-coding-agent/) |
|
||||
| [Rakuten](https://www.rakuten.com/) | E-commerce / Fintech | Copilot for domain-specific task | [Blog post, 2025](https://rakuten.today/blog/from-ai-hype-to-real-world-tools-rakuten-teams-up-with-langchain.html) |
|
||||
| [Rakuten](https://www.rakuten.com/) | E-commerce / Fintech | Copilot for domain-specific task | [Video story, 2025](https://youtu.be/gD1LIjCkuA8?feature=shared); [Blog post, 2025](https://rakuten.today/blog/from-ai-hype-to-real-world-tools-rakuten-teams-up-with-langchain.html) |
|
||||
| [Replit](https://replit.com/) | Software & Technology | Code generation | [Blog post, 2024](https://blog.langchain.dev/customers-replit/); [Breakout agent story, 2024](https://www.langchain.com/breakoutagents/replit); [Fireside chat video, 2024](https://www.youtube.com/watch?v=ViykMqljjxU) |
|
||||
| [Rexera](https://www.rexera.com/) | Real Estate (GenAI Native) | Copilot for domain-specific task | [Case study, 2024](https://blog.langchain.dev/customers-rexera/) |
|
||||
| [Abu Dhabi Government](https://www.tamm.abudhabi/) | Government | Search | [Case study, 2025](https://blog.langchain.com/customers-abu-dhabi-government/) |
|
||||
| [Tradestack](https://www.tradestack.uk/) | Software & Technology (GenAI Native) | Copilot for domain-specific task | [Case study, 2024](https://blog.langchain.dev/customers-tradestack/) |
|
||||
| [Uber](https://www.uber.com/) | Transportation | Developer productivity; Code generation | [Presentation, 2024](https://dpe.org/sessions/ty-smith-adam-huda/this-year-in-ubers-ai-driven-developer-productivity-revolution/); [Video, 2024](https://www.youtube.com/watch?v=8rkA5vWUE4Y) |
|
||||
| [Unify](https://www.unifygtm.com/) | Software & Technology (GenAI Native) | Copilot for domain-specific task | [Blog post, 2024](https://blog.langchain.dev/unify-launches-agents-for-account-qualification-using-langgraph-and-langsmith/) |
|
||||
| [Vizient](https://www.vizientinc.com/) | Healthcare | Copilot for domain-specific task | [Case study, 2025](https://blog.langchain.dev/p/3d2cd58c-13a5-4df9-bd84-7d54ed0ed82c/) |
|
||||
| [Uber](https://www.uber.com/) | Transportation | Developer productivity; Code generation | [Interrupt talk, 2025](https://youtu.be/Bugs0dVcNI8?feature=shared); [Presentation, 2024](https://dpe.org/sessions/ty-smith-adam-huda/this-year-in-ubers-ai-driven-developer-productivity-revolution/); [Video, 2024](https://www.youtube.com/watch?v=8rkA5vWUE4Y) |
|
||||
| [Unify](https://www.unifygtm.com/) | Software & Technology (GenAI Native) | Copilot for domain-specific task | [Interrupt talk, 2025](https://youtu.be/pKk-LfhujwI?feature=shared); [Blog post, 2024](https://blog.langchain.dev/unify-launches-agents-for-account-qualification-using-langgraph-and-langsmith/) |
|
||||
| [Vizient](https://www.vizientinc.com/) | Healthcare | Copilot for domain-specific task | [Video story, 2025](https://www.youtube.com/watch?v=vrjJ6NuyTWA); [Case study, 2025](https://blog.langchain.dev/p/3d2cd58c-13a5-4df9-bd84-7d54ed0ed82c/) |
|
||||
| [Vodafone](https://www.vodafone.com/) | Telecommunications | Code generation; internal search | [Case study, 2025](https://blog.langchain.dev/customers-vodafone/) |
|
||||
| [WebToon](https://www.webtoons.com/en/) | Media & Entertainment | Data extraction | [Case study, 2025](https://blog.langchain.com/customers-webtoon/) |
|
||||
| [11x](https://www.11x.ai/) | Software & Technology (GenAI Native) | Research & outreach | [Interrupt talk, 2025](https://youtu.be/fegwPmaAPQk?feature=shared) |
|
||||
|
||||
@@ -52,7 +52,7 @@ agent.invoke(
|
||||
)
|
||||
```
|
||||
|
||||
1. Define a tool for the agent to use. Tools can be defined as vanilla Python functions. For more advanced tool usage and customization, check the [tools](./tools.md) page.
|
||||
1. Define a tool for the agent to use. Tools can be defined as vanilla Python functions. For more advanced tool usage and customization, check the [tools](../how-tos/tool-calling.md) page.
|
||||
2. Provide a language model for the agent to use. To learn more about configuring language models for the agents, check the [models](./models.md) page.
|
||||
3. Provide a list of tools for the model to use.
|
||||
4. Provide a system prompt (instructions) to the language model used by the agent.
|
||||
@@ -180,14 +180,14 @@ ny_response = agent.invoke(
|
||||
)
|
||||
```
|
||||
|
||||
1. `checkpointer` allows the agent to store its state at every step in the tool calling loop. This enables [short-term memory](./memory.md#short-term-memory) and [human-in-the-loop](./human-in-the-loop.md) capabilities.
|
||||
1. `checkpointer` allows the agent to store its state at every step in the tool calling loop. This enables [short-term memory](../how-tos/memory/add-memory.md#add-short-term-memory) and [human-in-the-loop](../concepts/human_in_the_loop.md) capabilities.
|
||||
2. Pass configuration with `thread_id` to be able to resume the same conversation on future agent invocations.
|
||||
|
||||
When you enable the checkpointer, it stores agent state at every step in the provided checkpointer database (or in memory, if using `InMemorySaver`).
|
||||
|
||||
Note that in the above example, when the agent is invoked the second time with the same `thread_id`, the original message history from the first conversation is automatically included, together with the new user input.
|
||||
|
||||
For more information, see [Memory](./memory.md).
|
||||
For more information, see [Memory](../how-tos/memory/add-memory.md).
|
||||
|
||||
## 6. Configure structured output
|
||||
|
||||
|
||||
+107
-149
@@ -1,17 +1,8 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
tags:
|
||||
- agent
|
||||
hide:
|
||||
- tags
|
||||
---
|
||||
|
||||
# Context
|
||||
|
||||
Agents often require more than a list of messages to function effectively. They need **context**.
|
||||
**Context engineering** is the practice of building dynamic systems that provide the right information and tools, in the right format, so that a language model can plausibly accomplish a task.
|
||||
|
||||
Context includes *any* data outside the message list that can shape agent behavior or tool execution. This can be:
|
||||
Context includes *any* data outside the message list that can shape behavior. This can be:
|
||||
|
||||
- Information passed at runtime, like a `user_id` or API credentials.
|
||||
- Internal state updated during a multi-step reasoning process.
|
||||
@@ -21,111 +12,108 @@ LangGraph provides **three** primary ways to supply context:
|
||||
|
||||
| Type | Description | Mutable? | Lifetime |
|
||||
|------------------------------------------------------------------------------|-----------------------------------------------|----------|-------------------------|
|
||||
| [**Config**](#config-static-context) | data passed at the start of a run | ❌ | per run |
|
||||
| [**State**](#state-mutable-context) | dynamic data that can change during execution | ✅ | per run or conversation |
|
||||
| [**Long-term Memory (Store)**](#long-term-memory-cross-conversation-context) | data that can be shared between conversations | ✅ | across conversations |
|
||||
| [**Runtime Context**](#runtime-context) | data passed at the start of a run | ❌ | per run |
|
||||
| [**Short-term memory (State)**](#short-term-memory-mutable-context) | dynamic data that can change during execution | ✅ | per run or conversation |
|
||||
| [**Long-term memory (Store)**](#long-term-memory-cross-conversation-context) | data that can be shared between conversations | ✅ | across conversations |
|
||||
|
||||
You can use context to:
|
||||
### Runtime Context
|
||||
|
||||
- Adjust the system prompt the model sees
|
||||
- Feed tools with necessary inputs
|
||||
- Track facts during an ongoing conversation
|
||||
!!! note "`config['configurable']` -> `runtime.context`"
|
||||
|
||||
## Providing Runtime Context
|
||||
In LangGraph < v1.0, static runtime context was passed via the `config['configurable']` key, paired with a `config_schema` argument
|
||||
to `StateGraph` or `Pregel`. This is now deprecated and will be removed in v2.0.
|
||||
|
||||
Use this when you need to inject data into an agent at runtime.
|
||||
As of LangGraph v1.0, the Runtime object is recommended to access static context and runtime-specific information like the store and stream writer.
|
||||
|
||||
### Config (static context)
|
||||
Runtime context is for immutable data like user metadata or API keys. Use this when you have values that don't change mid-run.
|
||||
|
||||
Config is for immutable data like user metadata or API keys. Use
|
||||
when you have values that don't change mid-run.
|
||||
|
||||
Specify configuration using a key called **"configurable"** which is reserved
|
||||
for this purpose:
|
||||
Specify static context via the `context` argument to `invoke` / `stream`, which is reserved for this purpose:
|
||||
|
||||
```python
|
||||
agent.invoke(
|
||||
{"messages": [{"role": "user", "content": "hi!"}]},
|
||||
# highlight-next-line
|
||||
config={"configurable": {"user_id": "user_123"}}
|
||||
)
|
||||
```
|
||||
|
||||
### State (mutable context)
|
||||
|
||||
State acts as short-term memory during a run. It holds dynamic data that can evolve during execution, such as values derived from tools or LLM outputs.
|
||||
|
||||
```python
|
||||
class CustomState(AgentState):
|
||||
# highlight-next-line
|
||||
@dataclass
|
||||
class ContextSchema:
|
||||
user_name: str
|
||||
|
||||
agent = create_react_agent(
|
||||
# Other agent parameters...
|
||||
graph.invoke( # (1)!
|
||||
{"messages": [{"role": "user", "content": "hi!"}]}, # (2)!
|
||||
# highlight-next-line
|
||||
state_schema=CustomState,
|
||||
context={"user_name": "John Smith"} # (3)!
|
||||
)
|
||||
|
||||
agent.invoke({
|
||||
"messages": "hi!",
|
||||
"user_name": "Jane"
|
||||
})
|
||||
```
|
||||
|
||||
!!! tip "Turning on memory"
|
||||
1. This is the invocation of the agent or graph. The `invoke` method runs the underlying graph with the provided input.
|
||||
2. This example uses messages as an input, which is common, but your application may use different input structures.
|
||||
3. This is where you pass the runtime data. The `context` parameter allows you to provide additional dependencies that the agent can use during its execution.
|
||||
|
||||
Please see the [memory guide](./memory.md) for more details on how to enable memory. This is a powerful feature that allows you to persist the agent's state across multiple invocations.
|
||||
Otherwise, the state is scoped only to a single agent run.
|
||||
|
||||
|
||||
|
||||
### Long-Term Memory (cross-conversation context)
|
||||
|
||||
For context that spans *across* conversations or sessions, LangGraph allows access to **long-term memory** via a `store`. This can be used to read or update persistent facts (e.g., user profiles, preferences, prior interactions). For more, see the [Memory guide](./memory.md).
|
||||
|
||||
## Customizing Prompts with Context { #prompts }
|
||||
|
||||
Prompts define how the agent behaves. To incorporate runtime context, you can dynamically generate prompts based on the agent's state or config.
|
||||
|
||||
Common use cases:
|
||||
|
||||
- Personalization
|
||||
- Role or goal customization
|
||||
- Conditional behavior (e.g., user is admin)
|
||||
|
||||
=== "Using config"
|
||||
=== "Agent prompt"
|
||||
|
||||
```python
|
||||
from langchain_core.messages import AnyMessage
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
from langgraph.runtime import get_runtime
|
||||
from langgraph.prebuilt.chat_agent_executor import AgentState
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
|
||||
def prompt(
|
||||
state: AgentState,
|
||||
# highlight-next-line
|
||||
config: RunnableConfig,
|
||||
) -> list[AnyMessage]:
|
||||
# highlight-next-line
|
||||
user_name = config["configurable"].get("user_name")
|
||||
system_msg = f"You are a helpful assistant. User's name is {user_name}"
|
||||
# highlight-next-line
|
||||
def prompt(state: AgentState) -> list[AnyMessage]:
|
||||
runtime = get_runtime(ContextSchema)
|
||||
system_msg = f"You are a helpful assistant. Address the user as {runtime.context.user_name}."
|
||||
return [{"role": "system", "content": system_msg}] + state["messages"]
|
||||
|
||||
agent = create_react_agent(
|
||||
model="anthropic:claude-3-7-sonnet-latest",
|
||||
tools=[get_weather],
|
||||
# highlight-next-line
|
||||
prompt=prompt
|
||||
prompt=prompt,
|
||||
context_schema=ContextSchema
|
||||
)
|
||||
|
||||
agent.invoke(
|
||||
...,
|
||||
{"messages": [{"role": "user", "content": "what is the weather in sf"}]},
|
||||
# highlight-next-line
|
||||
config={"configurable": {"user_name": "John Smith"}}
|
||||
context={"user_name": "John Smith"}
|
||||
)
|
||||
```
|
||||
|
||||
=== "Using state"
|
||||
* See [Agents](../agents/agents.md) for details.
|
||||
|
||||
=== "Workflow node"
|
||||
|
||||
```python
|
||||
from langgraph.runtime import Runtime
|
||||
|
||||
# highlight-next-line
|
||||
def node(state: State, config: Runtime[ContextSchema]):
|
||||
user_name = runtime.context.user_name
|
||||
...
|
||||
```
|
||||
|
||||
* See [the Graph API](https://langchain-ai.github.io/langgraph/how-tos/graph-api/#add-runtime-configuration) for details.
|
||||
|
||||
=== "In a tool"
|
||||
|
||||
```python
|
||||
from langgraph.runtime import get_runtime
|
||||
|
||||
@tool
|
||||
# highlight-next-line
|
||||
def get_user_email() -> str:
|
||||
"""Retrieve user information based on user ID."""
|
||||
# simulate fetching user info from a database
|
||||
runtime = get_runtime(ContextSchema)
|
||||
email = get_user_email_from_db(runtime.context.user_name)
|
||||
return email
|
||||
```
|
||||
|
||||
See the [tool calling guide](../how-tos/tool-calling.md#configuration) for details.
|
||||
|
||||
### Short-term memory (mutable context)
|
||||
|
||||
State acts as [short-term memory](../concepts/memory.md) during a run. It holds dynamic data that can evolve during execution, such as values derived from tools or LLM outputs.
|
||||
|
||||
=== "In an agent"
|
||||
|
||||
Example shows how to incorporate state into an agent **prompt**.
|
||||
|
||||
State can also be accessed by the agent's **tools**, which can read or update the state as needed. See [tool calling guide](../how-tos/tool-calling.md#short-term-memory) for details.
|
||||
|
||||
```python
|
||||
from langchain_core.messages import AnyMessage
|
||||
@@ -133,15 +121,14 @@ Common use cases:
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
from langgraph.prebuilt.chat_agent_executor import AgentState
|
||||
|
||||
class CustomState(AgentState):
|
||||
# highlight-next-line
|
||||
# highlight-next-line
|
||||
class CustomState(AgentState): # (1)!
|
||||
user_name: str
|
||||
|
||||
def prompt(
|
||||
# highlight-next-line
|
||||
state: CustomState
|
||||
) -> list[AnyMessage]:
|
||||
# highlight-next-line
|
||||
user_name = state["user_name"]
|
||||
system_msg = f"You are a helpful assistant. User's name is {user_name}"
|
||||
return [{"role": "system", "content": system_msg}] + state["messages"]
|
||||
@@ -150,87 +137,58 @@ Common use cases:
|
||||
model="anthropic:claude-3-7-sonnet-latest",
|
||||
tools=[...],
|
||||
# highlight-next-line
|
||||
state_schema=CustomState,
|
||||
# highlight-next-line
|
||||
state_schema=CustomState, # (2)!
|
||||
prompt=prompt
|
||||
)
|
||||
|
||||
agent.invoke({
|
||||
"messages": "hi!",
|
||||
# highlight-next-line
|
||||
"user_name": "John Smith"
|
||||
})
|
||||
```
|
||||
|
||||
## Accessing Context in Tools { #tools }
|
||||
|
||||
Tools can access context through special parameter **annotations**.
|
||||
|
||||
* Use `RunnableConfig` for config access
|
||||
* Use `Annotated[StateSchema, InjectedState]` for agent state
|
||||
1. Define a custom state schema that extends `AgentState` or `MessagesState`.
|
||||
2. Pass the custom state schema to the agent. This allows the agent to access and modify the state during execution.
|
||||
|
||||
|
||||
!!! tip
|
||||
|
||||
These annotations prevent LLMs from attempting to fill in the values. These parameters will be **hidden** from the LLM.
|
||||
|
||||
=== "Using config"
|
||||
=== "In a workflow"
|
||||
|
||||
```python
|
||||
def get_user_info(
|
||||
# highlight-next-line
|
||||
config: RunnableConfig,
|
||||
) -> str:
|
||||
"""Look up user info."""
|
||||
# highlight-next-line
|
||||
user_id = config["configurable"].get("user_id")
|
||||
return "User is John Smith" if user_id == "user_123" else "Unknown user"
|
||||
from typing_extensions import TypedDict
|
||||
from langchain_core.messages import AnyMessage
|
||||
from langgraph.graph import StateGraph
|
||||
|
||||
agent = create_react_agent(
|
||||
model="anthropic:claude-3-7-sonnet-latest",
|
||||
tools=[get_user_info],
|
||||
)
|
||||
# highlight-next-line
|
||||
class CustomState(TypedDict): # (1)!
|
||||
messages: list[AnyMessage]
|
||||
extra_field: int
|
||||
|
||||
agent.invoke(
|
||||
{"messages": [{"role": "user", "content": "look up user information"}]},
|
||||
# highlight-next-line
|
||||
config={"configurable": {"user_id": "user_123"}}
|
||||
)
|
||||
# highlight-next-line
|
||||
def node(state: CustomState): # (2)!
|
||||
messages = state["messages"]
|
||||
...
|
||||
return { # (3)!
|
||||
# highlight-next-line
|
||||
"extra_field": state["extra_field"] + 1
|
||||
}
|
||||
|
||||
builder = StateGraph(State)
|
||||
builder.add_node(node)
|
||||
builder.set_entry_point("node")
|
||||
graph = builder.compile()
|
||||
```
|
||||
|
||||
1. Define a custom state
|
||||
2. Access the state in any node or tool
|
||||
3. The Graph API is designed to work as easily as possible with state. The return value of a node represents a requested update to the state.
|
||||
|
||||
=== "Using State"
|
||||
|
||||
```python
|
||||
from typing import Annotated
|
||||
from langgraph.prebuilt import InjectedState
|
||||
!!! tip "Turning on memory"
|
||||
|
||||
class CustomState(AgentState):
|
||||
# highlight-next-line
|
||||
user_id: str
|
||||
Please see the [memory guide](../how-tos/memory/add-memory.md) for more details on how to enable memory. This is a powerful feature that allows you to persist the agent's state across multiple invocations. Otherwise, the state is scoped only to a single run.
|
||||
|
||||
def get_user_info(
|
||||
# highlight-next-line
|
||||
state: Annotated[CustomState, InjectedState]
|
||||
) -> str:
|
||||
"""Look up user info."""
|
||||
# highlight-next-line
|
||||
user_id = state["user_id"]
|
||||
return "User is John Smith" if user_id == "user_123" else "Unknown user"
|
||||
### Long-term memory (cross-conversation context)
|
||||
|
||||
agent = create_react_agent(
|
||||
model="anthropic:claude-3-7-sonnet-latest",
|
||||
tools=[get_user_info],
|
||||
# highlight-next-line
|
||||
state_schema=CustomState,
|
||||
)
|
||||
For context that spans *across* conversations or sessions, LangGraph allows access to **long-term memory** via a `store`. This can be used to read or update persistent facts (e.g., user profiles, preferences, prior interactions).
|
||||
|
||||
agent.invoke({
|
||||
"messages": "look up user information",
|
||||
# highlight-next-line
|
||||
"user_id": "user_123"
|
||||
})
|
||||
```
|
||||
|
||||
### Update Context from Tools
|
||||
|
||||
Tools can update agent's context (state and long-term memory) during execution. This is useful for persisting intermediate results or making information accessible to subsequent tools or prompts. See [Memory](./memory.md#read-short-term) guide for more information.
|
||||
For more information, see the [Memory guide](../how-tos/memory/add-memory.md).
|
||||
@@ -1,92 +0,0 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
tags:
|
||||
- agent
|
||||
hide:
|
||||
- tags
|
||||
---
|
||||
|
||||
# Deployment
|
||||
|
||||
To deploy your LangGraph agent, create and configure a LangGraph app. This setup supports both local development and production deployments.
|
||||
|
||||
Features:
|
||||
|
||||
* 🖥️ Local server for development
|
||||
* 🧩 Studio Web UI for visual debugging
|
||||
* ☁️ Cloud and 🔧 self-hosted deployment options
|
||||
* 📊 LangSmith integration for tracing and observability
|
||||
|
||||
!!! info "Requirements"
|
||||
|
||||
- ✅ You **must** have a [LangSmith account](https://www.langchain.com/langsmith). You can sign up for **free** and get started with the free tier.
|
||||
|
||||
## Create a LangGraph app
|
||||
|
||||
```bash
|
||||
pip install -U "langgraph-cli[inmem]"
|
||||
langgraph new path/to/your/app --template new-langgraph-project-python
|
||||
```
|
||||
|
||||
This will create an empty LangGraph project. You can modify it by replacing the code in `src/agent/graph.py` with your agent code. For example:
|
||||
|
||||
```python
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
|
||||
def get_weather(city: str) -> str:
|
||||
"""Get weather for a given city."""
|
||||
return f"It's always sunny in {city}!"
|
||||
|
||||
graph = create_react_agent(
|
||||
model="anthropic:claude-3-7-sonnet-latest",
|
||||
tools=[get_weather],
|
||||
prompt="You are a helpful assistant"
|
||||
)
|
||||
```
|
||||
|
||||
### Install dependencies
|
||||
|
||||
In the root of your new LangGraph app, install the dependencies in `edit` mode so your local changes are used by the server:
|
||||
|
||||
```shell
|
||||
pip install -e .
|
||||
```
|
||||
|
||||
### Create an `.env` file
|
||||
|
||||
You will find a `.env.example` in the root of your new LangGraph app. Create
|
||||
a `.env` file in the root of your new LangGraph app and copy the contents of the `.env.example` file into it, filling in the necessary API keys:
|
||||
|
||||
```bash
|
||||
LANGSMITH_API_KEY=lsv2...
|
||||
ANTHROPIC_API_KEY=sk-
|
||||
```
|
||||
|
||||
## Launch LangGraph server locally
|
||||
|
||||
```shell
|
||||
langgraph dev
|
||||
```
|
||||
|
||||
This will start up the LangGraph API server locally. If this runs successfully, you should see something like:
|
||||
|
||||
> Ready!
|
||||
>
|
||||
> - API: [http://localhost:2024](http://localhost:2024/)
|
||||
>
|
||||
> - Docs: http://localhost:2024/docs
|
||||
>
|
||||
> - LangGraph Studio Web UI: https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024
|
||||
|
||||
See this [tutorial](https://langchain-ai.github.io/langgraph/tutorials/langgraph-platform/local-server/) to learn more about running LangGraph app locally.
|
||||
|
||||
## LangGraph Studio Web UI
|
||||
|
||||
LangGraph Studio Web is a specialized UI that you can connect to LangGraph API server to enable visualization, interaction, and debugging of your application locally. Test your graph in the LangGraph Studio Web UI by visiting the URL provided in the output of the `langgraph dev` command.
|
||||
|
||||
> - LangGraph Studio Web UI: https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024
|
||||
|
||||
## Deployment
|
||||
|
||||
Once your LangGraph app is running locally, you can deploy it using LangGraph Platform. Refer to the [deployment options guide](../tutorials/deployment.md) for detailed instructions on all supported deployment models.
|
||||
@@ -15,7 +15,7 @@ To evaluate your agent's performance you can use `LangSmith` [evaluations](https
|
||||
def evaluator(*, outputs: dict, reference_outputs: dict):
|
||||
# compare agent outputs against reference outputs
|
||||
output_messages = outputs["messages"]
|
||||
reference_messages = reference["messages"]
|
||||
reference_messages = reference_outputs["messages"]
|
||||
score = compare_messages(output_messages, reference_messages)
|
||||
return {"key": "evaluator_score", "score": score}
|
||||
```
|
||||
|
||||
@@ -1,238 +0,0 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
tags:
|
||||
- human-in-the-loop
|
||||
- hil
|
||||
- agent
|
||||
hide:
|
||||
- tags
|
||||
---
|
||||
|
||||
# Human-in-the-loop
|
||||
|
||||
To review, edit and approve tool calls in an agent you can use LangGraph's built-in [Human-In-the-Loop (HIL)](../concepts/human_in_the_loop.md) features, specifically the [`interrupt()`][langgraph.types.interrupt] primitive.
|
||||
|
||||
LangGraph allows you to pause execution **indefinitely** — for minutes, hours, or even days—until human input is received.
|
||||
|
||||
This is possible because the agent state is **checkpointed into a database**, which allows the system to persist execution context and later resume the workflow, continuing from where it left off.
|
||||
|
||||
For a deeper dive into the **human-in-the-loop** concept, see the [concept guide](../concepts/human_in_the_loop.md).
|
||||
|
||||
<figure markdown="1">
|
||||
{: style="max-height:400px"}
|
||||
<figcaption>
|
||||
A human can review and edit the output from the agent before proceeding. This is particularly critical in applications where the tool calls requested may be sensitive or require human oversight.
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
|
||||
## Review tool calls
|
||||
|
||||
To add a human approval step to a tool:
|
||||
|
||||
1. Use `interrupt()` in the tool to pause execution.
|
||||
2. Resume with a `Command(resume=...)` to continue based on human input.
|
||||
|
||||
```python
|
||||
from langgraph.checkpoint.memory import InMemorySaver
|
||||
from langgraph.types import interrupt
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
|
||||
# An example of a sensitive tool that requires human review / approval
|
||||
def book_hotel(hotel_name: str):
|
||||
"""Book a hotel"""
|
||||
# highlight-next-line
|
||||
response = interrupt( # (1)!
|
||||
f"Trying to call `book_hotel` with args {{'hotel_name': {hotel_name}}}. "
|
||||
"Please approve or suggest edits."
|
||||
)
|
||||
if response["type"] == "accept":
|
||||
pass
|
||||
elif response["type"] == "edit":
|
||||
hotel_name = response["args"]["hotel_name"]
|
||||
else:
|
||||
raise ValueError(f"Unknown response type: {response['type']}")
|
||||
return f"Successfully booked a stay at {hotel_name}."
|
||||
|
||||
# highlight-next-line
|
||||
checkpointer = InMemorySaver() # (2)!
|
||||
|
||||
agent = create_react_agent(
|
||||
model="anthropic:claude-3-5-sonnet-latest",
|
||||
tools=[book_hotel],
|
||||
# highlight-next-line
|
||||
checkpointer=checkpointer, # (3)!
|
||||
)
|
||||
```
|
||||
|
||||
1. The [`interrupt` function][langgraph.types.interrupt] pauses the agent graph at a specific node. In this case, we call `interrupt()` at the beginning of the tool function, which pauses the graph at the node that executes the tool. The information inside `interrupt()` (e.g., tool calls) can be presented to a human, and the graph can be resumed with the user input (tool call approval, edit or feedback).
|
||||
2. The `InMemorySaver` is used to store the agent state at every step in the tool calling loop. This enables [short-term memory](./memory.md#short-term-memory) and [human-in-the-loop](./human-in-the-loop.md) capabilities. In this example, we use `InMemorySaver` to store the agent state in memory. In a production application, the agent state will be stored in a database.
|
||||
3. Initialize the agent with the `checkpointer`.
|
||||
|
||||
Run the agent with the `stream()` method, passing the `config` object to specify the thread ID. This allows the agent to resume the same conversation on future invocations.
|
||||
|
||||
```python
|
||||
config = {
|
||||
"configurable": {
|
||||
# highlight-next-line
|
||||
"thread_id": "1"
|
||||
}
|
||||
}
|
||||
|
||||
for chunk in agent.stream(
|
||||
{"messages": [{"role": "user", "content": "book a stay at McKittrick hotel"}]},
|
||||
# highlight-next-line
|
||||
config
|
||||
):
|
||||
print(chunk)
|
||||
print("\n")
|
||||
```
|
||||
|
||||
> You should see that the agent runs until it reaches the `interrupt()` call, at which point it pauses and waits for human input.
|
||||
|
||||
Resume the agent with a `Command(resume=...)` to continue based on human input.
|
||||
|
||||
```python
|
||||
from langgraph.types import Command
|
||||
|
||||
for chunk in agent.stream(
|
||||
# highlight-next-line
|
||||
Command(resume={"type": "accept"}), # (1)!
|
||||
# Command(resume={"type": "edit", "args": {"hotel_name": "McKittrick Hotel"}}),
|
||||
config
|
||||
):
|
||||
print(chunk)
|
||||
print("\n")
|
||||
```
|
||||
|
||||
1. The [`interrupt` function][langgraph.types.interrupt] is used in conjunction with the [`Command`][langgraph.types.Command] object to resume the graph with a value provided by the human.
|
||||
|
||||
## Using with Agent Inbox
|
||||
|
||||
You can create a wrapper to add interrupts to *any* tool.
|
||||
|
||||
The example below provides a reference implementation compatible with [Agent Inbox UI](https://github.com/langchain-ai/agent-inbox) and [Agent Chat UI](https://github.com/langchain-ai/agent-chat-ui).
|
||||
|
||||
```python title="Wrapper that adds human-in-the-loop to any tool"
|
||||
from typing import Callable
|
||||
from langchain_core.tools import BaseTool, tool as create_tool
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
from langgraph.types import interrupt
|
||||
from langgraph.prebuilt.interrupt import HumanInterruptConfig, HumanInterrupt
|
||||
|
||||
def add_human_in_the_loop(
|
||||
tool: Callable | BaseTool,
|
||||
*,
|
||||
interrupt_config: HumanInterruptConfig = None,
|
||||
) -> BaseTool:
|
||||
"""Wrap a tool to support human-in-the-loop review."""
|
||||
if not isinstance(tool, BaseTool):
|
||||
tool = create_tool(tool)
|
||||
|
||||
if interrupt_config is None:
|
||||
interrupt_config = {
|
||||
"allow_accept": True,
|
||||
"allow_edit": True,
|
||||
"allow_respond": True,
|
||||
}
|
||||
|
||||
@create_tool( # (1)!
|
||||
tool.name,
|
||||
description=tool.description,
|
||||
args_schema=tool.args_schema
|
||||
)
|
||||
def call_tool_with_interrupt(config: RunnableConfig, **tool_input):
|
||||
request: HumanInterrupt = {
|
||||
"action_request": {
|
||||
"action": tool.name,
|
||||
"args": tool_input
|
||||
},
|
||||
"config": interrupt_config,
|
||||
"description": "Please review the tool call"
|
||||
}
|
||||
# highlight-next-line
|
||||
response = interrupt([request])[0] # (2)!
|
||||
# approve the tool call
|
||||
if response["type"] == "accept":
|
||||
tool_response = tool.invoke(tool_input, config)
|
||||
# update tool call args
|
||||
elif response["type"] == "edit":
|
||||
tool_input = response["args"]["args"]
|
||||
tool_response = tool.invoke(tool_input, config)
|
||||
# respond to the LLM with user feedback
|
||||
elif response["type"] == "response":
|
||||
user_feedback = response["args"]
|
||||
tool_response = user_feedback
|
||||
else:
|
||||
raise ValueError(f"Unsupported interrupt response type: {response['type']}")
|
||||
|
||||
return tool_response
|
||||
|
||||
return call_tool_with_interrupt
|
||||
```
|
||||
|
||||
1. This wrapper creates a new tool that calls `interrupt()` **before** executing the wrapped tool.
|
||||
2. `interrupt()` is using special input and output format that's expected by [Agent Inbox UI](https://github.com/langchain-ai/agent-inbox):
|
||||
- a list of [`HumanInterrupt`][langgraph.prebuilt.interrupt.HumanInterrupt] objects is sent to `AgentInbox` render interrupt information to the end user
|
||||
- resume value is provided by `AgentInbox` as a list (i.e., `Command(resume=[...])`)
|
||||
|
||||
You can use the `add_human_in_the_loop` wrapper to add `interrupt()` to any tool without having to add it *inside* the tool:
|
||||
|
||||
```python
|
||||
from langgraph.checkpoint.memory import InMemorySaver
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
|
||||
# highlight-next-line
|
||||
checkpointer = InMemorySaver()
|
||||
|
||||
def book_hotel(hotel_name: str):
|
||||
"""Book a hotel"""
|
||||
return f"Successfully booked a stay at {hotel_name}."
|
||||
|
||||
|
||||
agent = create_react_agent(
|
||||
model="anthropic:claude-3-5-sonnet-latest",
|
||||
tools=[
|
||||
# highlight-next-line
|
||||
add_human_in_the_loop(book_hotel), # (1)!
|
||||
],
|
||||
# highlight-next-line
|
||||
checkpointer=checkpointer,
|
||||
)
|
||||
|
||||
config = {"configurable": {"thread_id": "1"}}
|
||||
|
||||
# Run the agent
|
||||
for chunk in agent.stream(
|
||||
{"messages": [{"role": "user", "content": "book a stay at McKittrick hotel"}]},
|
||||
# highlight-next-line
|
||||
config
|
||||
):
|
||||
print(chunk)
|
||||
print("\n")
|
||||
```
|
||||
|
||||
1. The `add_human_in_the_loop` wrapper is used to add `interrupt()` to the tool. This allows the agent to pause execution and wait for human input before proceeding with the tool call.
|
||||
|
||||
> You should see that the agent runs until it reaches the `interrupt()` call,
|
||||
> at which point it pauses and waits for human input.
|
||||
|
||||
Resume the agent with a `Command(resume=...)` to continue based on human input.
|
||||
|
||||
```python
|
||||
from langgraph.types import Command
|
||||
|
||||
for chunk in agent.stream(
|
||||
# highlight-next-line
|
||||
Command(resume=[{"type": "accept"}]),
|
||||
# Command(resume=[{"type": "edit", "args": {"args": {"hotel_name": "McKittrick Hotel"}}}]),
|
||||
config
|
||||
):
|
||||
print(chunk)
|
||||
print("\n")
|
||||
```
|
||||
|
||||
## Additional resources
|
||||
|
||||
* [Human-in-the-loop in LangGraph](../concepts/human_in_the_loop.md)
|
||||
+113
-44
@@ -7,57 +7,125 @@ hide:
|
||||
- tags
|
||||
---
|
||||
|
||||
# MCP Integration
|
||||
# Use MCP
|
||||
|
||||
[Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) is an open protocol that standardizes how applications provide tools and context to language models. LangGraph agents can use tools defined on MCP servers through the `langchain-mcp-adapters` library.
|
||||
|
||||

|
||||
|
||||
Install the `langchain-mcp-adapters` library to use MCP tools in LangGraph:
|
||||
|
||||
```bash
|
||||
pip install langchain-mcp-adapters
|
||||
```
|
||||
The Model Context Protocol (MCP) is an open protocol that standardizes how applications provide tools and context to language models. LangGraph agents can use tools defined on MCP servers through the `langchain-mcp-adapters` library.
|
||||
|
||||
## Use MCP tools
|
||||
|
||||
The `langchain-mcp-adapters` package enables agents to use tools defined across one or more MCP servers.
|
||||
|
||||
```python title="Agent using tools defined on MCP servers"
|
||||
# highlight-next-line
|
||||
from langchain_mcp_adapters.client import MultiServerMCPClient
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
=== "In an agent"
|
||||
|
||||
# highlight-next-line
|
||||
client = MultiServerMCPClient(
|
||||
{
|
||||
"math": {
|
||||
"command": "python",
|
||||
# Replace with absolute path to your math_server.py file
|
||||
"args": ["/path/to/math_server.py"],
|
||||
"transport": "stdio",
|
||||
},
|
||||
"weather": {
|
||||
# Ensure you start your weather server on port 8000
|
||||
"url": "http://localhost:8000/mcp",
|
||||
"transport": "streamable_http",
|
||||
}
|
||||
}
|
||||
)
|
||||
# highlight-next-line
|
||||
tools = await client.get_tools()
|
||||
agent = create_react_agent(
|
||||
"anthropic:claude-3-7-sonnet-latest",
|
||||
```python title="Agent using tools defined on MCP servers"
|
||||
# highlight-next-line
|
||||
tools
|
||||
)
|
||||
math_response = await agent.ainvoke(
|
||||
{"messages": [{"role": "user", "content": "what's (3 + 5) x 12?"}]}
|
||||
)
|
||||
weather_response = await agent.ainvoke(
|
||||
{"messages": [{"role": "user", "content": "what is the weather in nyc?"}]}
|
||||
)
|
||||
```
|
||||
from langchain_mcp_adapters.client import MultiServerMCPClient
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
|
||||
# highlight-next-line
|
||||
client = MultiServerMCPClient(
|
||||
{
|
||||
"math": {
|
||||
"command": "python",
|
||||
# Replace with absolute path to your math_server.py file
|
||||
"args": ["/path/to/math_server.py"],
|
||||
"transport": "stdio",
|
||||
},
|
||||
"weather": {
|
||||
# Ensure you start your weather server on port 8000
|
||||
"url": "http://localhost:8000/mcp",
|
||||
"transport": "streamable_http",
|
||||
}
|
||||
}
|
||||
)
|
||||
# highlight-next-line
|
||||
tools = await client.get_tools()
|
||||
agent = create_react_agent(
|
||||
"anthropic:claude-3-7-sonnet-latest",
|
||||
# highlight-next-line
|
||||
tools
|
||||
)
|
||||
math_response = await agent.ainvoke(
|
||||
{"messages": [{"role": "user", "content": "what's (3 + 5) x 12?"}]}
|
||||
)
|
||||
weather_response = await agent.ainvoke(
|
||||
{"messages": [{"role": "user", "content": "what is the weather in nyc?"}]}
|
||||
)
|
||||
```
|
||||
|
||||
=== "In a workflow"
|
||||
|
||||
```python title="Workflow using MCP tools with ToolNode"
|
||||
from langchain_mcp_adapters.client import MultiServerMCPClient
|
||||
from langchain.chat_models import init_chat_model
|
||||
from langgraph.graph import StateGraph, MessagesState, START, END
|
||||
from langgraph.prebuilt import ToolNode
|
||||
|
||||
# Initialize the model
|
||||
model = init_chat_model("anthropic:claude-3-5-sonnet-latest")
|
||||
|
||||
# Set up MCP client
|
||||
client = MultiServerMCPClient(
|
||||
{
|
||||
"math": {
|
||||
"command": "python",
|
||||
# Make sure to update to the full absolute path to your math_server.py file
|
||||
"args": ["./examples/math_server.py"],
|
||||
"transport": "stdio",
|
||||
},
|
||||
"weather": {
|
||||
# make sure you start your weather server on port 8000
|
||||
"url": "http://localhost:8000/mcp/",
|
||||
"transport": "streamable_http",
|
||||
}
|
||||
}
|
||||
)
|
||||
tools = await client.get_tools()
|
||||
|
||||
# Bind tools to model
|
||||
model_with_tools = model.bind_tools(tools)
|
||||
|
||||
# Create ToolNode
|
||||
tool_node = ToolNode(tools)
|
||||
|
||||
def should_continue(state: MessagesState):
|
||||
messages = state["messages"]
|
||||
last_message = messages[-1]
|
||||
if last_message.tool_calls:
|
||||
return "tools"
|
||||
return END
|
||||
|
||||
# Define call_model function
|
||||
async def call_model(state: MessagesState):
|
||||
messages = state["messages"]
|
||||
response = await model_with_tools.ainvoke(messages)
|
||||
return {"messages": [response]}
|
||||
|
||||
# Build the graph
|
||||
builder = StateGraph(MessagesState)
|
||||
builder.add_node("call_model", call_model)
|
||||
builder.add_node("tools", tool_node)
|
||||
|
||||
builder.add_edge(START, "call_model")
|
||||
builder.add_conditional_edges(
|
||||
"call_model",
|
||||
should_continue,
|
||||
)
|
||||
builder.add_edge("tools", "call_model")
|
||||
|
||||
# Compile the graph
|
||||
graph = builder.compile()
|
||||
|
||||
# Test the graph
|
||||
math_response = await graph.ainvoke(
|
||||
{"messages": [{"role": "user", "content": "what's (3 + 5) x 12?"}]}
|
||||
)
|
||||
weather_response = await graph.ainvoke(
|
||||
{"messages": [{"role": "user", "content": "what is the weather in nyc?"}]}
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Custom MCP servers
|
||||
|
||||
@@ -106,4 +174,5 @@ if __name__ == "__main__":
|
||||
## Additional resources
|
||||
|
||||
- [MCP documentation](https://modelcontextprotocol.io/introduction)
|
||||
- [MCP Transport documentation](https://modelcontextprotocol.io/docs/concepts/transports)
|
||||
- [MCP Transport documentation](https://modelcontextprotocol.io/docs/concepts/transports)
|
||||
- [langchain_mcp_adapters](https://github.com/langchain-ai/langchain-mcp-adapters)
|
||||
|
||||
@@ -1,423 +0,0 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
tags:
|
||||
- agent
|
||||
hide:
|
||||
- tags
|
||||
---
|
||||
|
||||
# Memory
|
||||
|
||||
LangGraph supports two types of memory essential for building conversational agents:
|
||||
|
||||
- **[Short-term memory](#short-term-memory)**: Tracks the ongoing conversation by maintaining message history within a session.
|
||||
- **[Long-term memory](#long-term-memory)**: Stores user-specific or application-level data across sessions.
|
||||
|
||||
This guide demonstrates how to use both memory types with agents in LangGraph. For a deeper
|
||||
understanding of memory concepts, refer to the [LangGraph memory documentation](../concepts/memory.md).
|
||||
|
||||
<figure markdown="1">
|
||||
{: style="max-height:400px"}
|
||||
<figcaption>Both <strong>short-term</strong> and <strong>long-term</strong> memory require persistent storage to maintain continuity across LLM interactions. In production environments, this data is typically stored in a database.</figcaption>
|
||||
</figure>
|
||||
|
||||
!!! note "Terminology"
|
||||
|
||||
In LangGraph:
|
||||
|
||||
- *Short-term memory* is also referred to as **thread-level memory**.
|
||||
- *Long-term memory* is also called **cross-thread memory**.
|
||||
|
||||
A [thread](../concepts/persistence.md#threads) represents a sequence of related runs
|
||||
grouped by the same `thread_id`.
|
||||
|
||||
## Short-term memory
|
||||
|
||||
Short-term memory enables agents to track multi-turn conversations. To use it, you must:
|
||||
|
||||
1. Provide a `checkpointer` when creating the agent. The `checkpointer` enables [persistence](../concepts/persistence.md) of the agent's state.
|
||||
2. Supply a `thread_id` in the config when running the agent. The `thread_id` is a unique identifier for the conversation session.
|
||||
|
||||
```python
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
from langgraph.checkpoint.memory import InMemorySaver
|
||||
|
||||
# highlight-next-line
|
||||
checkpointer = InMemorySaver() # (1)!
|
||||
|
||||
|
||||
def get_weather(city: str) -> str:
|
||||
"""Get weather for a given city."""
|
||||
return f"It's always sunny in {city}!"
|
||||
|
||||
|
||||
agent = create_react_agent(
|
||||
model="anthropic:claude-3-7-sonnet-latest",
|
||||
tools=[get_weather],
|
||||
# highlight-next-line
|
||||
checkpointer=checkpointer # (2)!
|
||||
)
|
||||
|
||||
# Run the agent
|
||||
config = {
|
||||
"configurable": {
|
||||
# highlight-next-line
|
||||
"thread_id": "1" # (3)!
|
||||
}
|
||||
}
|
||||
|
||||
sf_response = agent.invoke(
|
||||
{"messages": [{"role": "user", "content": "what is the weather in sf"}]},
|
||||
# highlight-next-line
|
||||
config
|
||||
)
|
||||
|
||||
# Continue the conversation using the same thread_id
|
||||
ny_response = agent.invoke(
|
||||
{"messages": [{"role": "user", "content": "what about new york?"}]},
|
||||
# highlight-next-line
|
||||
config # (4)!
|
||||
)
|
||||
```
|
||||
|
||||
1. The `InMemorySaver` is a checkpointer that stores the agent's state in memory. In a production setting, you would typically use a database or other persistent storage. Please review the [checkpointer documentation](../reference/checkpoints.md) for more options. If you're deploying with **LangGraph Platform**, the platform will provide a production-ready checkpointer for you.
|
||||
2. The `checkpointer` is passed to the agent. This enables the agent to persist its state across invocations.
|
||||
3. A unique `thread_id` is provided in the config. This ID is used to identify the conversation session. The value is controlled by the user and can be any string.
|
||||
4. The agent will continue the conversation using the same `thread_id`. This will allow the agent to infer that the user is asking specifically about the **weather** in New York.
|
||||
|
||||
When the agent is invoked the second time with the same `thread_id`, the original message history from the first conversation is automatically included, allowing the agent to infer that the user is asking specifically about the **weather** in New York.
|
||||
|
||||
!!! Note "LangGraph Platform provides a production-ready checkpointer"
|
||||
|
||||
If you're using [LangGraph Platform](./deployment.md), during deployment your checkpointer will be automatically configured to use a production-ready database.
|
||||
|
||||
### Manage message history
|
||||
|
||||
Long conversations can exceed the LLM's context window. Common solutions are:
|
||||
|
||||
* [Summarization](#summarize-message-history): Maintain a running summary of the conversation
|
||||
* [Trimming](#trim-message-history): Remove first or last N messages in the history
|
||||
|
||||
This allows the agent to keep track of the conversation without exceeding the LLM's context window.
|
||||
|
||||
To manage message history, specify `pre_model_hook` — a function ([node](../concepts/low_level.md#nodes)) that will always run before calling the language model.
|
||||
|
||||
#### Summarize message history
|
||||
|
||||
<figure markdown="1">
|
||||
{: style="max-height:400px"}
|
||||
<figcaption>Long conversations can exceed the LLM's context window. A common solution is to maintain a running summary of the conversation. This allows the agent to keep track of the conversation without exceeding the LLM's context window.
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
To summarize message history, you can use [`pre_model_hook`][langgraph.prebuilt.chat_agent_executor.create_react_agent] with a prebuilt [`SummarizationNode`](https://langchain-ai.github.io/langmem/reference/short_term/#langmem.short_term.SummarizationNode):
|
||||
|
||||
```python
|
||||
from langchain_anthropic import ChatAnthropic
|
||||
from langmem.short_term import SummarizationNode
|
||||
from langchain_core.messages.utils import count_tokens_approximately
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
from langgraph.prebuilt.chat_agent_executor import AgentState
|
||||
from langgraph.checkpoint.memory import InMemorySaver
|
||||
from typing import Any
|
||||
|
||||
model = ChatAnthropic(model="claude-3-7-sonnet-latest")
|
||||
|
||||
summarization_node = SummarizationNode( # (1)!
|
||||
token_counter=count_tokens_approximately,
|
||||
model=model,
|
||||
max_tokens=384,
|
||||
max_summary_tokens=128,
|
||||
output_messages_key="llm_input_messages",
|
||||
)
|
||||
|
||||
class State(AgentState):
|
||||
# NOTE: we're adding this key to keep track of previous summary information
|
||||
# to make sure we're not summarizing on every LLM call
|
||||
# highlight-next-line
|
||||
context: dict[str, Any] # (2)!
|
||||
|
||||
|
||||
checkpointer = InMemorySaver() # (3)!
|
||||
|
||||
agent = create_react_agent(
|
||||
model=model,
|
||||
tools=tools,
|
||||
# highlight-next-line
|
||||
pre_model_hook=summarization_node, # (4)!
|
||||
# highlight-next-line
|
||||
state_schema=State, # (5)!
|
||||
checkpointer=checkpointer,
|
||||
)
|
||||
```
|
||||
|
||||
1. The `InMemorySaver` is a checkpointer that stores the agent's state in memory. In a production setting, you would typically use a database or other persistent storage. Please review the [checkpointer documentation](../reference/checkpoints.md) for more options. If you're deploying with **LangGraph Platform**, the platform will provide a production-ready checkpointer for you.
|
||||
2. The `context` key is added to the agent's state. The key contains book-keeping information for the summarization node. It is used to keep track of the last summary information and ensure that the agent doesn't summarize on every LLM call, which can be inefficient.
|
||||
3. The `checkpointer` is passed to the agent. This enables the agent to persist its state across invocations.
|
||||
4. The `pre_model_hook` is set to the `SummarizationNode`. This node will summarize the message history before sending it to the LLM. The summarization node will automatically handle the summarization process and update the agent's state with the new summary. You can replace this with a custom implementation if you prefer. Please see the [create_react_agent][langgraph.prebuilt.chat_agent_executor.create_react_agent] API reference for more details.
|
||||
5. The `state_schema` is set to the `State` class, which is the custom state that contains an extra `context` key.
|
||||
|
||||
#### Trim message history
|
||||
|
||||
To trim message history, you can use [`pre_model_hook`][langgraph.prebuilt.chat_agent_executor.create_react_agent] with [`trim_messages`](https://python.langchain.com/api_reference/core/messages/langchain_core.messages.utils.trim_messages.html) function:
|
||||
|
||||
```python
|
||||
# highlight-next-line
|
||||
from langchain_core.messages.utils import (
|
||||
# highlight-next-line
|
||||
trim_messages,
|
||||
# highlight-next-line
|
||||
count_tokens_approximately
|
||||
# highlight-next-line
|
||||
)
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
|
||||
# This function will be called every time before the node that calls LLM
|
||||
def pre_model_hook(state):
|
||||
trimmed_messages = trim_messages(
|
||||
state["messages"],
|
||||
strategy="last",
|
||||
token_counter=count_tokens_approximately,
|
||||
max_tokens=384,
|
||||
start_on="human",
|
||||
end_on=("human", "tool"),
|
||||
)
|
||||
# highlight-next-line
|
||||
return {"llm_input_messages": trimmed_messages}
|
||||
|
||||
checkpointer = InMemorySaver()
|
||||
agent = create_react_agent(
|
||||
model,
|
||||
tools,
|
||||
# highlight-next-line
|
||||
pre_model_hook=pre_model_hook,
|
||||
checkpointer=checkpointer,
|
||||
)
|
||||
```
|
||||
|
||||
To learn more about using `pre_model_hook` for managing message history, see this [how-to guide](../how-tos/create-react-agent-manage-message-history.ipynb)
|
||||
|
||||
### Read in tools { #read-short-term }
|
||||
|
||||
LangGraph allows agent to access its short-term memory (state) inside the tools.
|
||||
|
||||
```python
|
||||
from typing import Annotated
|
||||
from langgraph.prebuilt import InjectedState, create_react_agent
|
||||
|
||||
class CustomState(AgentState):
|
||||
# highlight-next-line
|
||||
user_id: str
|
||||
|
||||
def get_user_info(
|
||||
# highlight-next-line
|
||||
state: Annotated[CustomState, InjectedState]
|
||||
) -> str:
|
||||
"""Look up user info."""
|
||||
# highlight-next-line
|
||||
user_id = state["user_id"]
|
||||
return "User is John Smith" if user_id == "user_123" else "Unknown user"
|
||||
|
||||
agent = create_react_agent(
|
||||
model="anthropic:claude-3-7-sonnet-latest",
|
||||
tools=[get_user_info],
|
||||
# highlight-next-line
|
||||
state_schema=CustomState,
|
||||
)
|
||||
|
||||
agent.invoke({
|
||||
"messages": "look up user information",
|
||||
# highlight-next-line
|
||||
"user_id": "user_123"
|
||||
})
|
||||
```
|
||||
|
||||
See the [Context](./context.md#__tabbed_2_2) guide for more information.
|
||||
|
||||
### Write from tools { #write-short-term }
|
||||
|
||||
To modify the agent's short-term memory (state) during execution, you can return state updates directly from the tools. This is useful for persisting intermediate results or making information accessible to subsequent tools or prompts.
|
||||
|
||||
```python
|
||||
from typing import Annotated
|
||||
from langchain_core.tools import InjectedToolCallId
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
from langchain_core.messages import ToolMessage
|
||||
from langgraph.prebuilt import InjectedState, create_react_agent
|
||||
from langgraph.prebuilt.chat_agent_executor import AgentState
|
||||
from langgraph.types import Command
|
||||
|
||||
class CustomState(AgentState):
|
||||
# highlight-next-line
|
||||
user_name: str
|
||||
|
||||
def update_user_info(
|
||||
tool_call_id: Annotated[str, InjectedToolCallId],
|
||||
config: RunnableConfig
|
||||
) -> Command:
|
||||
"""Look up and update user info."""
|
||||
user_id = config["configurable"].get("user_id")
|
||||
name = "John Smith" if user_id == "user_123" else "Unknown user"
|
||||
# highlight-next-line
|
||||
return Command(update={
|
||||
# highlight-next-line
|
||||
"user_name": name,
|
||||
# update the message history
|
||||
"messages": [
|
||||
ToolMessage(
|
||||
"Successfully looked up user information",
|
||||
tool_call_id=tool_call_id
|
||||
)
|
||||
]
|
||||
})
|
||||
|
||||
def greet(
|
||||
# highlight-next-line
|
||||
state: Annotated[CustomState, InjectedState]
|
||||
) -> str:
|
||||
"""Use this to greet the user once you found their info."""
|
||||
user_name = state["user_name"]
|
||||
return f"Hello {user_name}!"
|
||||
|
||||
agent = create_react_agent(
|
||||
model="anthropic:claude-3-7-sonnet-latest",
|
||||
tools=[update_user_info, greet],
|
||||
# highlight-next-line
|
||||
state_schema=CustomState
|
||||
)
|
||||
|
||||
agent.invoke(
|
||||
{"messages": [{"role": "user", "content": "greet the user"}]},
|
||||
# highlight-next-line
|
||||
config={"configurable": {"user_id": "user_123"}}
|
||||
)
|
||||
```
|
||||
|
||||
For more details, see [how to update state from tools](../how-tos/tool-calling.ipynb#update).
|
||||
|
||||
## Long-term memory
|
||||
|
||||
Use long-term memory to store user-specific or application-specific data across conversations. This is useful for applications like chatbots, where you want to remember user preferences or other information.
|
||||
|
||||
To use long-term memory, you need to:
|
||||
|
||||
1. [Configure a store](../how-tos/persistence.ipynb#add-long-term-memory) to persist data across invocations.
|
||||
2. Use the [`get_store`][langgraph.config.get_store] function to access the store from within tools or prompts.
|
||||
|
||||
### Read { #read-long-term }
|
||||
|
||||
```python title="A tool the agent can use to look up user information"
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
from langgraph.config import get_store
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
from langgraph.store.memory import InMemoryStore
|
||||
|
||||
# highlight-next-line
|
||||
store = InMemoryStore() # (1)!
|
||||
|
||||
# highlight-next-line
|
||||
store.put( # (2)!
|
||||
("users",), # (3)!
|
||||
"user_123", # (4)!
|
||||
{
|
||||
"name": "John Smith",
|
||||
"language": "English",
|
||||
} # (5)!
|
||||
)
|
||||
|
||||
def get_user_info(config: RunnableConfig) -> str:
|
||||
"""Look up user info."""
|
||||
# Same as that provided to `create_react_agent`
|
||||
# highlight-next-line
|
||||
store = get_store() # (6)!
|
||||
user_id = config["configurable"].get("user_id")
|
||||
# highlight-next-line
|
||||
user_info = store.get(("users",), user_id) # (7)!
|
||||
return str(user_info.value) if user_info else "Unknown user"
|
||||
|
||||
agent = create_react_agent(
|
||||
model="anthropic:claude-3-7-sonnet-latest",
|
||||
tools=[get_user_info],
|
||||
# highlight-next-line
|
||||
store=store # (8)!
|
||||
)
|
||||
|
||||
# Run the agent
|
||||
agent.invoke(
|
||||
{"messages": [{"role": "user", "content": "look up user information"}]},
|
||||
# highlight-next-line
|
||||
config={"configurable": {"user_id": "user_123"}}
|
||||
)
|
||||
```
|
||||
|
||||
1. The `InMemoryStore` is a store that stores data in memory. In a production setting, you would typically use a database or other persistent storage. Please review the [store documentation](../reference/store.md) for more options. If you're deploying with **LangGraph Platform**, the platform will provide a production-ready store for you.
|
||||
2. For this example, we write some sample data to the store using the `put` method. Please see the [BaseStore.put][langgraph.store.base.BaseStore.put] API reference for more details.
|
||||
3. The first argument is the namespace. This is used to group related data together. In this case, we are using the `users` namespace to group user data.
|
||||
4. A key within the namespace. This example uses a user ID for the key.
|
||||
5. The data that we want to store for the given user.
|
||||
6. The `get_store` function is used to access the store. You can call it from anywhere in your code, including tools and prompts. This function returns the store that was passed to the agent when it was created.
|
||||
7. The `get` method is used to retrieve data from the store. The first argument is the namespace, and the second argument is the key. This will return a `StoreValue` object, which contains the value and metadata about the value.
|
||||
8. The `store` is passed to the agent. This enables the agent to access the store when running tools. You can also use the `get_store` function to access the store from anywhere in your code.
|
||||
|
||||
### Write { #write-long-term }
|
||||
|
||||
```python title="Example of a tool that updates user information"
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
from langgraph.config import get_store
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
from langgraph.store.memory import InMemoryStore
|
||||
|
||||
store = InMemoryStore() # (1)!
|
||||
|
||||
class UserInfo(TypedDict): # (2)!
|
||||
name: str
|
||||
|
||||
def save_user_info(user_info: UserInfo, config: RunnableConfig) -> str: # (3)!
|
||||
"""Save user info."""
|
||||
# Same as that provided to `create_react_agent`
|
||||
# highlight-next-line
|
||||
store = get_store() # (4)!
|
||||
user_id = config["configurable"].get("user_id")
|
||||
# highlight-next-line
|
||||
store.put(("users",), user_id, user_info) # (5)!
|
||||
return "Successfully saved user info."
|
||||
|
||||
agent = create_react_agent(
|
||||
model="anthropic:claude-3-7-sonnet-latest",
|
||||
tools=[save_user_info],
|
||||
# highlight-next-line
|
||||
store=store
|
||||
)
|
||||
|
||||
# Run the agent
|
||||
agent.invoke(
|
||||
{"messages": [{"role": "user", "content": "My name is John Smith"}]},
|
||||
# highlight-next-line
|
||||
config={"configurable": {"user_id": "user_123"}} # (6)!
|
||||
)
|
||||
|
||||
# You can access the store directly to get the value
|
||||
store.get(("users",), "user_123").value
|
||||
```
|
||||
|
||||
1. The `InMemoryStore` is a store that stores data in memory. In a production setting, you would typically use a database or other persistent storage. Please review the [store documentation](../reference/store.md) for more options. If you're deploying with **LangGraph Platform**, the platform will provide a production-ready store for you.
|
||||
2. The `UserInfo` class is a `TypedDict` that defines the structure of the user information. The LLM will use this to format the response according to the schema.
|
||||
3. The `save_user_info` function is a tool that allows an agent to update user information. This could be useful for a chat application where the user wants to update their profile information.
|
||||
4. The `get_store` function is used to access the store. You can call it from anywhere in your code, including tools and prompts. This function returns the store that was passed to the agent when it was created.
|
||||
5. The `put` method is used to store data in the store. The first argument is the namespace, and the second argument is the key. This will store the user information in the store.
|
||||
6. The `user_id` is passed in the config. This is used to identify the user whose information is being updated.
|
||||
|
||||
### Semantic search
|
||||
|
||||
LangGraph also allows you to [search](https://langchain-ai.github.io/langgraph/how-tos/memory/semantic-search/#using-in-create-react-agent) for items in long-term memory by semantic similarity.
|
||||
|
||||
### Prebuilt memory tools
|
||||
|
||||
**LangMem** is a LangChain-maintained library that offers tools for managing long-term memories in your agent. See the [LangMem documentation](https://langchain-ai.github.io/langmem/) for usage examples.
|
||||
|
||||
|
||||
## Additional resources
|
||||
|
||||
* [Memory in LangGraph](../concepts/memory.md)
|
||||
+192
-213
@@ -1,233 +1,176 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
tags:
|
||||
- anthropic
|
||||
- openai
|
||||
- agent
|
||||
hide:
|
||||
- tags
|
||||
---
|
||||
|
||||
# Models
|
||||
|
||||
This page describes how to configure the chat model used by an agent.
|
||||
|
||||
## Tool calling support
|
||||
|
||||
To enable tool-calling agents, the underlying LLM must support [tool calling](https://python.langchain.com/docs/concepts/tool_calling/).
|
||||
|
||||
Compatible models can be found in the [LangChain integrations directory](https://python.langchain.com/docs/integrations/chat/).
|
||||
|
||||
## Specifying a model by name
|
||||
|
||||
You can configure an agent with a model name string:
|
||||
|
||||
=== "OpenAI"
|
||||
|
||||
```python
|
||||
import os
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
|
||||
os.environ["OPENAI_API_KEY"] = "sk-..."
|
||||
|
||||
agent = create_react_agent(
|
||||
# highlight-next-line
|
||||
model="openai:gpt-4.1",
|
||||
# other parameters
|
||||
)
|
||||
```
|
||||
|
||||
=== "Anthropic"
|
||||
|
||||
```python
|
||||
import os
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
|
||||
os.environ["ANTHROPIC_API_KEY"] = "sk-..."
|
||||
|
||||
agent = create_react_agent(
|
||||
# highlight-next-line
|
||||
model="anthropic:claude-3-7-sonnet-latest",
|
||||
# other parameters
|
||||
)
|
||||
```
|
||||
|
||||
=== "Azure"
|
||||
|
||||
```python
|
||||
import os
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
|
||||
os.environ["AZURE_OPENAI_API_KEY"] = "..."
|
||||
os.environ["AZURE_OPENAI_ENDPOINT"] = "..."
|
||||
os.environ["OPENAI_API_VERSION"] = "2025-03-01-preview"
|
||||
|
||||
agent = create_react_agent(
|
||||
# highlight-next-line
|
||||
model="azure_openai:gpt-4.1",
|
||||
# other parameters
|
||||
)
|
||||
```
|
||||
|
||||
=== "Google Gemini"
|
||||
|
||||
```python
|
||||
import os
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
|
||||
os.environ["GOOGLE_API_KEY"] = "..."
|
||||
|
||||
agent = create_react_agent(
|
||||
# highlight-next-line
|
||||
model="google_genai:gemini-2.0-flash",
|
||||
# other parameters
|
||||
)
|
||||
```
|
||||
|
||||
=== "AWS Bedrock"
|
||||
|
||||
```python
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
|
||||
# Follow the steps here to configure your credentials:
|
||||
# https://docs.aws.amazon.com/bedrock/latest/userguide/getting-started.html
|
||||
|
||||
agent = create_react_agent(
|
||||
# highlight-next-line
|
||||
model="bedrock_converse:anthropic.claude-3-5-sonnet-20240620-v1:0",
|
||||
# other parameters
|
||||
)
|
||||
```
|
||||
LangGraph provides built-in support for [LLMs (language models)](https://python.langchain.com/docs/concepts/chat_models/) via the LangChain library. This makes it easy to integrate various LLMs into your agents and workflows.
|
||||
|
||||
|
||||
## Using `init_chat_model`
|
||||
## Initialize a model
|
||||
|
||||
The [`init_chat_model`](https://python.langchain.com/docs/how_to/chat_models_universal_init/) utility simplifies model initialization with configurable parameters:
|
||||
Use [`init_chat_model`](https://python.langchain.com/docs/how_to/chat_models_universal_init/) to initialize models:
|
||||
|
||||
=== "OpenAI"
|
||||
{% include-markdown "../../snippets/chat_model_tabs.md" %}
|
||||
|
||||
```
|
||||
pip install -U "langchain[openai]"
|
||||
```
|
||||
```python
|
||||
import os
|
||||
from langchain.chat_models import init_chat_model
|
||||
|
||||
os.environ["OPENAI_API_KEY"] = "sk-..."
|
||||
|
||||
model = init_chat_model(
|
||||
"openai:gpt-4.1",
|
||||
temperature=0,
|
||||
# other parameters
|
||||
)
|
||||
```
|
||||
|
||||
=== "Anthropic"
|
||||
|
||||
```
|
||||
pip install -U "langchain[anthropic]"
|
||||
```
|
||||
```python
|
||||
import os
|
||||
from langchain.chat_models import init_chat_model
|
||||
|
||||
os.environ["ANTHROPIC_API_KEY"] = "sk-..."
|
||||
|
||||
model = init_chat_model(
|
||||
"anthropic:claude-3-5-sonnet-latest",
|
||||
temperature=0,
|
||||
# other parameters
|
||||
)
|
||||
```
|
||||
|
||||
=== "Azure"
|
||||
|
||||
```
|
||||
pip install -U "langchain[openai]"
|
||||
```
|
||||
```python
|
||||
import os
|
||||
from langchain.chat_models import init_chat_model
|
||||
|
||||
os.environ["AZURE_OPENAI_API_KEY"] = "..."
|
||||
os.environ["AZURE_OPENAI_ENDPOINT"] = "..."
|
||||
os.environ["OPENAI_API_VERSION"] = "2025-03-01-preview"
|
||||
|
||||
model = init_chat_model(
|
||||
"azure_openai:gpt-4.1",
|
||||
azure_deployment=os.environ["AZURE_OPENAI_DEPLOYMENT_NAME"],
|
||||
temperature=0,
|
||||
# other parameters
|
||||
)
|
||||
```
|
||||
|
||||
=== "Google Gemini"
|
||||
|
||||
```
|
||||
pip install -U "langchain[google-genai]"
|
||||
```
|
||||
```python
|
||||
import os
|
||||
from langchain.chat_models import init_chat_model
|
||||
|
||||
os.environ["GOOGLE_API_KEY"] = "..."
|
||||
|
||||
model = init_chat_model(
|
||||
"google_genai:gemini-2.0-flash",
|
||||
temperature=0,
|
||||
# other parameters
|
||||
)
|
||||
```
|
||||
|
||||
=== "AWS Bedrock"
|
||||
|
||||
```
|
||||
pip install -U "langchain[aws]"
|
||||
```
|
||||
```python
|
||||
from langchain.chat_models import init_chat_model
|
||||
|
||||
# Follow the steps here to configure your credentials:
|
||||
# https://docs.aws.amazon.com/bedrock/latest/userguide/getting-started.html
|
||||
|
||||
model = init_chat_model(
|
||||
"anthropic.claude-3-5-sonnet-20240620-v1:0",
|
||||
model_provider="bedrock_converse",
|
||||
temperature=0,
|
||||
# other parameters
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
Refer to the [API reference](https://python.langchain.com/api_reference/langchain/chat_models/langchain.chat_models.base.init_chat_model.html) for advanced options.
|
||||
|
||||
## Using provider-specific LLMs
|
||||
### Instantiate a model directly
|
||||
|
||||
If a model provider is not available via `init_chat_model`, you can instantiate the provider's model class directly. The model must implement the [BaseChatModel interface](https://python.langchain.com/api_reference/core/language_models/langchain_core.language_models.chat_models.BaseChatModel.html) and support tool calling:
|
||||
|
||||
|
||||
```python
|
||||
# Anthropic is already supported by `init_chat_model`,
|
||||
# but you can also instantiate it directly.
|
||||
from langchain_anthropic import ChatAnthropic
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
|
||||
model = ChatAnthropic(
|
||||
model="claude-3-7-sonnet-latest",
|
||||
temperature=0,
|
||||
max_tokens=2048
|
||||
)
|
||||
|
||||
agent = create_react_agent(
|
||||
# highlight-next-line
|
||||
model=model,
|
||||
# other parameters
|
||||
model="claude-3-7-sonnet-latest",
|
||||
temperature=0,
|
||||
max_tokens=2048
|
||||
)
|
||||
```
|
||||
|
||||
!!! note "Illustrative example"
|
||||
!!! important "Tool calling support"
|
||||
|
||||
The example above uses `ChatAnthropic`, which is already supported by `init_chat_model`. This pattern is shown to illustrate how to manually instantiate a model not available through init_chat_model.
|
||||
If you are building an agent or workflow that requires the model to call external tools, ensure that the underlying
|
||||
language model supports [tool calling](../concepts/tools.md). Compatible models can be found in the [LangChain integrations directory](https://python.langchain.com/docs/integrations/chat/).
|
||||
|
||||
## Disable streaming
|
||||
|
||||
## Use in an agent
|
||||
|
||||
When using `create_react_agent` you can specify the model by its name string, which is a shorthand for initializing the model using `init_chat_model`. This allows you to use the model without needing to import or instantiate it directly.
|
||||
|
||||
=== "model name"
|
||||
|
||||
|
||||
```python
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
|
||||
create_react_agent(
|
||||
# highlight-next-line
|
||||
model="anthropic:claude-3-7-sonnet-latest",
|
||||
# other parameters
|
||||
)
|
||||
```
|
||||
|
||||
=== "model instance"
|
||||
|
||||
```python
|
||||
from langchain_anthropic import ChatAnthropic
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
|
||||
model = ChatAnthropic(
|
||||
model="claude-3-7-sonnet-latest",
|
||||
temperature=0,
|
||||
max_tokens=2048
|
||||
)
|
||||
# Alternatively
|
||||
# model = init_chat_model("anthropic:claude-3-7-sonnet-latest")
|
||||
|
||||
agent = create_react_agent(
|
||||
# highlight-next-line
|
||||
model=model,
|
||||
# other parameters
|
||||
)
|
||||
```
|
||||
|
||||
### Dynamic model selection
|
||||
|
||||
Pass a callable function to `create_react_agent` to dynamically select the model at runtime. This is useful for scenarios where you want to choose a model based on user input, configuration settings, or other runtime conditions.
|
||||
|
||||
The selector function must return an instance of a `BaseChatModel`. If you're using tools, you must bind the tools to the model within the selector function.
|
||||
|
||||
```python
|
||||
openai_model = init_chat_model("openai:gpt-4o")
|
||||
anthropic_model = init_chat_model("anthropic:claude-sonnet-4-20250514")
|
||||
|
||||
# highlight-next-line
|
||||
def select_model(state, runtime: Runtime[CustomContext]) -> BaseChatModel:
|
||||
if runtime.context.provider == "anthropic":
|
||||
model = anthropic_model
|
||||
elif runtime.context.provider == "openai":
|
||||
model = openai_model
|
||||
else:
|
||||
raise ValueError(f"Unsupported provider: {runtime.context.provider}")
|
||||
# With dynamic model selection, you must bind tools explicitly
|
||||
# highlight-next-line
|
||||
return model.bind_tools(tools_to_use)
|
||||
|
||||
agent = create_react_agent(
|
||||
# highlight-next-line
|
||||
select_model,
|
||||
tools=all_known_tools
|
||||
)
|
||||
```
|
||||
|
||||
!!! version-added "New in LangGraph v0.6"
|
||||
|
||||
|
||||
??? example "Extended example: dynamically select model and tools"
|
||||
|
||||
```python
|
||||
from dataclasses import dataclass
|
||||
from typing import Literal
|
||||
from langchain.chat_models import init_chat_model
|
||||
from langchain_core.language_models import BaseChatModel
|
||||
from langchain_core.tools import tool
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
from langgraph.prebuilt.chat_agent_executor import AgentState
|
||||
from langgraph.runtime import Runtime
|
||||
|
||||
# Define the runtime context
|
||||
@dataclass
|
||||
class CustomContext:
|
||||
provider: Literal["anthropic", "openai"]
|
||||
|
||||
@tool
|
||||
def weather() -> str:
|
||||
"""Returns the current weather conditions."""
|
||||
return "It's nice and sunny."
|
||||
|
||||
# Initialize models
|
||||
openai_model = init_chat_model("openai:gpt-4o")
|
||||
anthropic_model = init_chat_model("anthropic:claude-sonnet-4-20250514")
|
||||
|
||||
@dataclass
|
||||
class CustomContext:
|
||||
provider: Literal["anthropic", "openai"]
|
||||
|
||||
# Initialize models
|
||||
openai_model = init_chat_model("openai:gpt-4o")
|
||||
anthropic_model = init_chat_model("anthropic:claude-sonnet-4-20250514")
|
||||
|
||||
# Selector function for model choice
|
||||
def select_model(state: AgentState, runtime: Runtime[CustomContext]) -> BaseChatModel:
|
||||
if runtime.context.provider == "anthropic":
|
||||
model = anthropic_model
|
||||
elif runtime.context.provider == "openai":
|
||||
model = openai_model
|
||||
else:
|
||||
raise ValueError(f"Unsupported provider: {runtime.context.provider}")
|
||||
|
||||
# With dynamic model selection, you must bind tools explicitly
|
||||
return model.bind_tools([weather])
|
||||
|
||||
# Create agent with dynamic model selection
|
||||
agent = create_react_agent(select_model, tools=[weather])
|
||||
|
||||
# Invoke with context to select model
|
||||
output = agent.invoke(
|
||||
{
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Which model is handling this?",
|
||||
}
|
||||
]
|
||||
},
|
||||
context=CustomContext(provider="openai"),
|
||||
)
|
||||
|
||||
print(output["messages"][-1].text())
|
||||
```
|
||||
|
||||
|
||||
## Advanced model configuration
|
||||
|
||||
### Disable streaming
|
||||
|
||||
To disable streaming of the individual LLM tokens, set `disable_streaming=True` when initializing the model:
|
||||
|
||||
@@ -257,7 +200,7 @@ To disable streaming of the individual LLM tokens, set `disable_streaming=True`
|
||||
|
||||
Refer to the [API reference](https://python.langchain.com/api_reference/core/language_models/langchain_core.language_models.chat_models.BaseChatModel.html#langchain_core.language_models.chat_models.BaseChatModel.disable_streaming) for more information on `disable_streaming`
|
||||
|
||||
## Adding model fallbacks
|
||||
### Add model fallbacks
|
||||
|
||||
You can add a fallback to a different model or a different LLM provider using `model.with_fallbacks([...])`:
|
||||
|
||||
@@ -292,7 +235,43 @@ You can add a fallback to a different model or a different LLM provider using `m
|
||||
|
||||
See this [guide](https://python.langchain.com/docs/how_to/fallbacks/#fallback-to-better-model) for more information on model fallbacks.
|
||||
|
||||
### Use the built-in rate limiter
|
||||
|
||||
Langchain includes a built-in in-memory rate limiter. This rate limiter is thread safe and can be shared by multiple threads in the same process.
|
||||
|
||||
```python
|
||||
from langchain_core.rate_limiters import InMemoryRateLimiter
|
||||
from langchain_anthropic import ChatAnthropic
|
||||
|
||||
rate_limiter = InMemoryRateLimiter(
|
||||
requests_per_second=0.1, # <-- Super slow! We can only make a request once every 10 seconds!!
|
||||
check_every_n_seconds=0.1, # Wake up every 100 ms to check whether allowed to make a request,
|
||||
max_bucket_size=10, # Controls the maximum burst size.
|
||||
)
|
||||
|
||||
model = ChatAnthropic(
|
||||
model_name="claude-3-opus-20240229",
|
||||
rate_limiter=rate_limiter
|
||||
)
|
||||
```
|
||||
|
||||
See the LangChain docs for more information on how to [handle rate limiting](https://python.langchain.com/docs/how_to/chat_model_rate_limiting/).
|
||||
|
||||
## Bring your own model
|
||||
|
||||
If your desired LLM isn't officially supported by LangChain, consider these options:
|
||||
|
||||
1. **Implement a custom LangChain chat model**: Create a model conforming to the [LangChain chat model interface](https://python.langchain.com/docs/how_to/custom_chat_model/). This enables full compatibility with LangGraph's agents and workflows but requires understanding of the LangChain framework.
|
||||
|
||||
2. **Direct invocation with custom streaming**: Use your model directly by [adding custom streaming logic](../how-tos/streaming.md#use-with-any-llm) with `StreamWriter`.
|
||||
Refer to the [custom streaming documentation](../how-tos/streaming.md#use-with-any-llm) for guidance. This approach suits custom workflows where prebuilt agent integration is not necessary.
|
||||
|
||||
|
||||
## Additional resources
|
||||
|
||||
- [Multimodal inputs](https://python.langchain.com/docs/how_to/multimodal_inputs/)
|
||||
- [Structured outputs](https://python.langchain.com/docs/how_to/structured_output/)
|
||||
- [Model integration directory](https://python.langchain.com/docs/integrations/chat/)
|
||||
- [Universal initialization with `init_chat_model`](https://python.langchain.com/docs/how_to/chat_models_universal_init/)
|
||||
- [Force model to call a specific tool](https://python.langchain.com/docs/how_to/tool_choice/)
|
||||
- [All chat model how-to guides](https://python.langchain.com/docs/how_to/#chat-models)
|
||||
- [Chat model integrations](https://python.langchain.com/docs/integrations/chat/)
|
||||
|
||||
@@ -8,9 +8,9 @@ hide:
|
||||
- tags
|
||||
---
|
||||
|
||||
# Agent development with LangGraph
|
||||
# Agent development using prebuilt components
|
||||
|
||||
**LangGraph** provides both low-level primitives and high-level prebuilt components for building agent-based applications. This section focuses on the **prebuilt**, **reusable** components designed to help you construct agentic systems quickly and reliably—without the need to implement orchestration, memory, or human feedback handling from scratch.
|
||||
LangGraph provides both low-level primitives and high-level prebuilt components for building agent-based applications. This section focuses on the prebuilt, ready-to-use components designed to help you construct agentic systems quickly and reliably—without the need to implement orchestration, memory, or human feedback handling from scratch.
|
||||
|
||||
## What is an agent?
|
||||
|
||||
@@ -27,12 +27,12 @@ The LLM operates in a loop. In each iteration, it selects a tool to invoke, prov
|
||||
|
||||
LangGraph includes several capabilities essential for building robust, production-ready agentic systems:
|
||||
|
||||
- [**Memory integration**](./memory.md): Native support for *short-term* (session-based) and *long-term* (persistent across sessions) memory, enabling stateful behaviors in chatbots and assistants.
|
||||
- [**Human-in-the-loop control**](./human-in-the-loop.md): Execution can pause *indefinitely* to await human feedback—unlike websocket-based solutions limited to real-time interaction. This enables asynchronous approval, correction, or intervention at any point in the workflow.
|
||||
- [**Streaming support**](./streaming.md): Real-time streaming of agent state, model tokens, tool outputs, or combined streams.
|
||||
- [**Deployment tooling**](./deployment.md): Includes infrastructure-free deployment tools. [**LangGraph Platform**](https://langchain-ai.github.io/langgraph/concepts/langgraph_platform/) supports testing, debugging, and deployment.
|
||||
- [**Memory integration**](../how-tos/memory/add-memory.md): Native support for *short-term* (session-based) and *long-term* (persistent across sessions) memory, enabling stateful behaviors in chatbots and assistants.
|
||||
- [**Human-in-the-loop control**](../concepts/human_in_the_loop.md): Execution can pause *indefinitely* to await human feedback—unlike websocket-based solutions limited to real-time interaction. This enables asynchronous approval, correction, or intervention at any point in the workflow.
|
||||
- [**Streaming support**](../how-tos/streaming.md): Real-time streaming of agent state, model tokens, tool outputs, or combined streams.
|
||||
- [**Deployment tooling**](../tutorials/langgraph-platform/local-server.md): Includes infrastructure-free deployment tools. [**LangGraph Platform**](https://langchain-ai.github.io/langgraph/concepts/langgraph_platform/) supports testing, debugging, and deployment.
|
||||
- **[Studio](https://langchain-ai.github.io/langgraph/concepts/langgraph_studio/)**: A visual IDE for inspecting and debugging workflows.
|
||||
- Supports multiple [**deployment options**](https://langchain-ai.github.io/langgraph/tutorials/deployment/) for production.
|
||||
- Supports multiple [**deployment options**](https://langchain-ai.github.io/langgraph/concepts/deployment_options.md) for production.
|
||||
|
||||
## High-level building blocks
|
||||
|
||||
@@ -50,7 +50,7 @@ The high-level components are organized into several packages, each with a speci
|
||||
| `langgraph-supervisor` | Tools for building [**supervisor**](./multi-agent.md#supervisor) agents | `pip install -U langgraph-supervisor` |
|
||||
| `langgraph-swarm` | Tools for building a [**swarm**](./multi-agent.md#swarm) multi-agent system | `pip install -U langgraph-swarm` |
|
||||
| `langchain-mcp-adapters` | Interfaces to [**MCP servers**](./mcp.md) for tool and resource integration | `pip install -U langchain-mcp-adapters` |
|
||||
| `langmem` | Agent memory management: [**short-term and long-term**](./memory.md) | `pip install -U langmem` |
|
||||
| `langmem` | Agent memory management: [**short-term and long-term**](../how-tos/memory/add-memory.md) | `pip install -U langmem` |
|
||||
| `agentevals` | Utilities to [**evaluate agent performance**](./evals.md) | `pip install -U agentevals` |
|
||||
|
||||
## Visualize an agent graph
|
||||
@@ -60,7 +60,7 @@ Use the following tool to visualize the graph generated by
|
||||
and to view an outline of the corresponding code.
|
||||
It allows you to explore the infrastructure of the agent as defined by the presence of:
|
||||
|
||||
* [`tools`](../agents/tools.md): A list of tools (functions, APIs, or other callable objects) that the agent can use to perform tasks.
|
||||
* [`tools`](../how-tos/tool-calling.md): A list of tools (functions, APIs, or other callable objects) that the agent can use to perform tasks.
|
||||
* [`pre_model_hook`](../how-tos/create-react-agent-manage-message-history.ipynb): A function that is called before the model is invoked. It can be used to condense messages or perform other preprocessing tasks.
|
||||
* `post_model_hook`: A function that is called after the model is invoked. It can be used to implement guardrails, human-in-the-loop flows, or other postprocessing tasks.
|
||||
* [`response_format`](../agents/agents.md#6-configure-structured-output): A data structure used to constrain the type of the final output, e.g., a `pydantic` `BaseModel`.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
[//]: # (This file is automatically generated using a script in docs/_scripts. Do not edit this file directly!)
|
||||
# Community agents
|
||||
# Community Agents
|
||||
|
||||
If you’re looking for other prebuilt libraries, explore the community-built options
|
||||
below. These libraries can extend LangGraph's functionality in various ways.
|
||||
|
||||
## 📚 Available libraries
|
||||
## 📚 Available Libraries
|
||||
|
||||
[//]: # (This file is automatically generated using a script in docs/_scripts. Do not edit this file directly!)
|
||||
| Name | GitHub URL | Description | Weekly Downloads | Stars |
|
||||
@@ -23,7 +23,7 @@ below. These libraries can extend LangGraph's functionality in various ways.
|
||||
| **langgraph-reflection** | [langchain-ai/langgraph-reflection](https://github.com/langchain-ai/langgraph-reflection) | LangGraph agent that runs a reflection step. | -12345 | 
|
||||
| **langgraph-codeact** | [langchain-ai/langgraph-codeact](https://github.com/langchain-ai/langgraph-codeact) | LangGraph implementation of CodeAct agent that generates and executes code instead of tool calling. | -12345 | 
|
||||
|
||||
## ✨ Contributing your library
|
||||
## ✨ Contributing Your Library
|
||||
|
||||
Have you built an awesome open-source library using LangGraph? We'd love to feature
|
||||
your project on the official LangGraph documentation pages! 🏆
|
||||
|
||||
@@ -10,7 +10,7 @@ hide:
|
||||
# Running agents
|
||||
|
||||
|
||||
Agents support both synchronous and asynchronous execution using either `.invoke()` / `await .ainvoke()` for full responses, or `.stream()` / `.astream()` for **incremental** [streaming](streaming.md) output. This section explains how to provide input, interpret output, enable streaming, and control execution limits.
|
||||
Agents support both synchronous and asynchronous execution using either `.invoke()` / `await .ainvoke()` for full responses, or `.stream()` / `.astream()` for **incremental** [streaming](../how-tos/streaming.md) output. This section explains how to provide input, interpret output, enable streaming, and control execution limits.
|
||||
|
||||
|
||||
## Basic usage
|
||||
@@ -109,7 +109,7 @@ Streaming is available in both sync and async modes:
|
||||
|
||||
!!! tip
|
||||
|
||||
For full details, see the [streaming guide](./streaming.md).
|
||||
For full details, see the [streaming guide](../how-tos/streaming.md).
|
||||
|
||||
## Max iterations
|
||||
|
||||
|
||||
@@ -1,223 +0,0 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
tags:
|
||||
- agent
|
||||
hide:
|
||||
- tags
|
||||
---
|
||||
|
||||
# Streaming
|
||||
|
||||
Streaming is key to building responsive applications. There are a few types of data you’ll want to stream:
|
||||
|
||||
1. [**Agent progress**](#agent-progress) — get updates after each node in the agent graph is executed.
|
||||
2. [**LLM tokens**](#llm-tokens) — stream tokens as they are generated by the language model.
|
||||
3. [**Custom updates**](#tool-updates) — emit custom data from tools during execution (e.g., "Fetched 10/100 records")
|
||||
|
||||
You can stream [more than one type of data](#stream-multiple-modes) at a time.
|
||||
|
||||
|
||||
<figure markdown="1">
|
||||
{: style="max-height:300px"}
|
||||
<figcaption>
|
||||
Waiting is for pigeons.
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
## Agent progress
|
||||
|
||||
To stream agent progress, use the [`stream()`][langgraph.graph.state.CompiledStateGraph.stream] or [`astream()`][langgraph.graph.state.CompiledStateGraph.astream] methods with [`stream_mode="updates"`](https://langchain-ai.github.io/langgraph/how-tos/streaming/#updates). This emits an event after every agent step.
|
||||
|
||||
For example, if you have an agent that calls a tool once, you should see the following updates:
|
||||
|
||||
* **LLM node**: AI message with tool call requests
|
||||
* **Tool node**: Tool message with execution result
|
||||
* **LLM node**: Final AI response
|
||||
|
||||
=== "Sync"
|
||||
|
||||
```python
|
||||
agent = create_react_agent(
|
||||
model="anthropic:claude-3-7-sonnet-latest",
|
||||
tools=[get_weather],
|
||||
)
|
||||
# highlight-next-line
|
||||
for chunk in agent.stream(
|
||||
{"messages": [{"role": "user", "content": "what is the weather in sf"}]},
|
||||
# highlight-next-line
|
||||
stream_mode="updates"
|
||||
):
|
||||
print(chunk)
|
||||
print("\n")
|
||||
```
|
||||
|
||||
=== "Async"
|
||||
|
||||
```python
|
||||
agent = create_react_agent(
|
||||
model="anthropic:claude-3-7-sonnet-latest",
|
||||
tools=[get_weather],
|
||||
)
|
||||
# highlight-next-line
|
||||
async for chunk in agent.astream(
|
||||
{"messages": [{"role": "user", "content": "what is the weather in sf"}]},
|
||||
# highlight-next-line
|
||||
stream_mode="updates"
|
||||
):
|
||||
print(chunk)
|
||||
print("\n")
|
||||
```
|
||||
|
||||
## LLM tokens
|
||||
|
||||
To stream tokens as they are produced by the LLM, use `stream_mode="messages"`:
|
||||
|
||||
=== "Sync"
|
||||
|
||||
```python
|
||||
agent = create_react_agent(
|
||||
model="anthropic:claude-3-7-sonnet-latest",
|
||||
tools=[get_weather],
|
||||
)
|
||||
# highlight-next-line
|
||||
for token, metadata in agent.stream(
|
||||
{"messages": [{"role": "user", "content": "what is the weather in sf"}]},
|
||||
# highlight-next-line
|
||||
stream_mode="messages"
|
||||
):
|
||||
print("Token", token)
|
||||
print("Metadata", metadata)
|
||||
print("\n")
|
||||
```
|
||||
|
||||
=== "Async"
|
||||
|
||||
```python
|
||||
agent = create_react_agent(
|
||||
model="anthropic:claude-3-7-sonnet-latest",
|
||||
tools=[get_weather],
|
||||
)
|
||||
# highlight-next-line
|
||||
async for token, metadata in agent.astream(
|
||||
{"messages": [{"role": "user", "content": "what is the weather in sf"}]},
|
||||
# highlight-next-line
|
||||
stream_mode="messages"
|
||||
):
|
||||
print("Token", token)
|
||||
print("Metadata", metadata)
|
||||
print("\n")
|
||||
```
|
||||
|
||||
## Tool updates
|
||||
|
||||
To stream updates from tools as they are executed, you can use [get_stream_writer][langgraph.config.get_stream_writer].
|
||||
|
||||
=== "Sync"
|
||||
|
||||
```python
|
||||
# highlight-next-line
|
||||
from langgraph.config import get_stream_writer
|
||||
|
||||
def get_weather(city: str) -> str:
|
||||
"""Get weather for a given city."""
|
||||
# highlight-next-line
|
||||
writer = get_stream_writer()
|
||||
# stream any arbitrary data
|
||||
# highlight-next-line
|
||||
writer(f"Looking up data for city: {city}")
|
||||
return f"It's always sunny in {city}!"
|
||||
|
||||
agent = create_react_agent(
|
||||
model="anthropic:claude-3-7-sonnet-latest",
|
||||
tools=[get_weather],
|
||||
)
|
||||
|
||||
for chunk in agent.stream(
|
||||
{"messages": [{"role": "user", "content": "what is the weather in sf"}]},
|
||||
# highlight-next-line
|
||||
stream_mode="custom"
|
||||
):
|
||||
print(chunk)
|
||||
print("\n")
|
||||
```
|
||||
|
||||
=== "Async"
|
||||
|
||||
```python
|
||||
# highlight-next-line
|
||||
from langgraph.config import get_stream_writer
|
||||
|
||||
def get_weather(city: str) -> str:
|
||||
"""Get weather for a given city."""
|
||||
# highlight-next-line
|
||||
writer = get_stream_writer()
|
||||
# stream any arbitrary data
|
||||
# highlight-next-line
|
||||
writer(f"Looking up data for city: {city}")
|
||||
return f"It's always sunny in {city}!"
|
||||
|
||||
agent = create_react_agent(
|
||||
model="anthropic:claude-3-7-sonnet-latest",
|
||||
tools=[get_weather],
|
||||
)
|
||||
|
||||
async for chunk in agent.astream(
|
||||
{"messages": [{"role": "user", "content": "what is the weather in sf"}]},
|
||||
# highlight-next-line
|
||||
stream_mode="custom"
|
||||
):
|
||||
print(chunk)
|
||||
print("\n")
|
||||
```
|
||||
|
||||
!!! Note
|
||||
If you add `get_stream_writer` inside your tool, you won't be able to invoke the tool outside of a LangGraph execution context.
|
||||
|
||||
## Stream multiple modes
|
||||
|
||||
You can specify multiple streaming modes by passing stream mode as a list: `stream_mode=["updates", "messages", "custom"]`:
|
||||
|
||||
=== "Sync"
|
||||
|
||||
```python
|
||||
agent = create_react_agent(
|
||||
model="anthropic:claude-3-7-sonnet-latest",
|
||||
tools=[get_weather],
|
||||
)
|
||||
|
||||
for stream_mode, chunk in agent.stream(
|
||||
{"messages": [{"role": "user", "content": "what is the weather in sf"}]},
|
||||
# highlight-next-line
|
||||
stream_mode=["updates", "messages", "custom"]
|
||||
):
|
||||
print(chunk)
|
||||
print("\n")
|
||||
```
|
||||
|
||||
=== "Async"
|
||||
|
||||
```python
|
||||
agent = create_react_agent(
|
||||
model="anthropic:claude-3-7-sonnet-latest",
|
||||
tools=[get_weather],
|
||||
)
|
||||
|
||||
async for stream_mode, chunk in agent.astream(
|
||||
{"messages": [{"role": "user", "content": "what is the weather in sf"}]},
|
||||
# highlight-next-line
|
||||
stream_mode=["updates", "messages", "custom"]
|
||||
):
|
||||
print(chunk)
|
||||
print("\n")
|
||||
```
|
||||
|
||||
## Disable streaming
|
||||
|
||||
In some applications you might need to disable streaming of individual tokens for a given model. This is useful in [multi-agent](./multi-agent.md) systems to control which agents stream their output.
|
||||
|
||||
See the [Models](./models.md#disable-streaming) guide to learn how to disable streaming.
|
||||
|
||||
## Additional resources
|
||||
|
||||
* [Streaming in LangGraph](https://langchain-ai.github.io/langgraph/how-tos/streaming)
|
||||
@@ -1,310 +0,0 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
tags:
|
||||
- agent
|
||||
hide:
|
||||
- tags
|
||||
---
|
||||
|
||||
# Tools
|
||||
|
||||
[Tools](https://python.langchain.com/docs/concepts/tools/) are a way to encapsulate a function and its input schema in a way that can be passed to a chat model that supports tool calling. This allows the model to request the execution of this function with specific inputs.
|
||||
|
||||
You can either [define your own tools](#define-simple-tools) or use [prebuilt integrations](#prebuilt-tools) that LangChain provides.
|
||||
|
||||
## Define simple tools
|
||||
|
||||
You can pass a vanilla function to `create_react_agent` to use as a tool:
|
||||
|
||||
```python
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
|
||||
def multiply(a: int, b: int) -> int:
|
||||
"""Multiply two numbers."""
|
||||
return a * b
|
||||
|
||||
create_react_agent(
|
||||
model="anthropic:claude-3-7-sonnet",
|
||||
tools=[multiply]
|
||||
)
|
||||
```
|
||||
|
||||
`create_react_agent` automatically converts vanilla functions to [LangChain tools](https://python.langchain.com/docs/concepts/tools/#tool-interface).
|
||||
|
||||
## Customize tools
|
||||
|
||||
For more control over tool behavior, use the `@tool` decorator:
|
||||
|
||||
```python
|
||||
# highlight-next-line
|
||||
from langchain_core.tools import tool
|
||||
|
||||
# highlight-next-line
|
||||
@tool("multiply_tool", parse_docstring=True)
|
||||
def multiply(a: int, b: int) -> int:
|
||||
"""Multiply two numbers.
|
||||
|
||||
Args:
|
||||
a: First operand
|
||||
b: Second operand
|
||||
"""
|
||||
return a * b
|
||||
```
|
||||
|
||||
You can also define a custom input schema using Pydantic:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
class MultiplyInputSchema(BaseModel):
|
||||
"""Multiply two numbers"""
|
||||
a: int = Field(description="First operand")
|
||||
b: int = Field(description="Second operand")
|
||||
|
||||
# highlight-next-line
|
||||
@tool("multiply_tool", args_schema=MultiplyInputSchema)
|
||||
def multiply(a: int, b: int) -> int:
|
||||
return a * b
|
||||
```
|
||||
|
||||
For additional customization, refer to the [custom tools guide](https://python.langchain.com/docs/how_to/custom_tools/).
|
||||
|
||||
## Hide arguments from the model
|
||||
|
||||
Some tools require runtime-only arguments (e.g., user ID or session context) that should not be controllable by the model.
|
||||
|
||||
You can put these arguments in the `state` or `config` of the agent, and access
|
||||
this information inside the tool:
|
||||
|
||||
```python
|
||||
from langgraph.prebuilt import InjectedState
|
||||
from langgraph.prebuilt.chat_agent_executor import AgentState
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
|
||||
def my_tool(
|
||||
# This will be populated by an LLM
|
||||
tool_arg: str,
|
||||
# access information that's dynamically updated inside the agent
|
||||
# highlight-next-line
|
||||
state: Annotated[AgentState, InjectedState],
|
||||
# access static data that is passed at agent invocation
|
||||
# highlight-next-line
|
||||
config: RunnableConfig,
|
||||
) -> str:
|
||||
"""My tool."""
|
||||
do_something_with_state(state["messages"])
|
||||
do_something_with_config(config)
|
||||
...
|
||||
```
|
||||
|
||||
## Disable parallel tool calling
|
||||
|
||||
Some model providers support executing multiple tools in parallel, but
|
||||
allow users to disable this feature.
|
||||
|
||||
For supported providers, you can disable parallel tool calling by setting `parallel_tool_calls=False` via the `model.bind_tools()` method:
|
||||
|
||||
```python
|
||||
from langchain.chat_models import init_chat_model
|
||||
|
||||
def add(a: int, b: int) -> int:
|
||||
"""Add two numbers"""
|
||||
return a + b
|
||||
|
||||
def multiply(a: int, b: int) -> int:
|
||||
"""Multiply two numbers."""
|
||||
return a * b
|
||||
|
||||
model = init_chat_model("anthropic:claude-3-5-sonnet-latest", temperature=0)
|
||||
tools = [add, multiply]
|
||||
agent = create_react_agent(
|
||||
# disable parallel tool calls
|
||||
# highlight-next-line
|
||||
model=model.bind_tools(tools, parallel_tool_calls=False),
|
||||
tools=tools
|
||||
)
|
||||
|
||||
agent.invoke(
|
||||
{"messages": [{"role": "user", "content": "what's 3 + 5 and 4 * 7?"}]}
|
||||
)
|
||||
```
|
||||
|
||||
## Return tool results directly
|
||||
|
||||
Use `return_direct=True` to return tool results immediately and stop the agent loop:
|
||||
|
||||
```python
|
||||
from langchain_core.tools import tool
|
||||
|
||||
# highlight-next-line
|
||||
@tool(return_direct=True)
|
||||
def add(a: int, b: int) -> int:
|
||||
"""Add two numbers"""
|
||||
return a + b
|
||||
|
||||
agent = create_react_agent(
|
||||
model="anthropic:claude-3-7-sonnet-latest",
|
||||
tools=[add]
|
||||
)
|
||||
|
||||
agent.invoke(
|
||||
{"messages": [{"role": "user", "content": "what's 3 + 5?"}]}
|
||||
)
|
||||
```
|
||||
|
||||
## Force tool use
|
||||
|
||||
To force the agent to use specific tools, you can set the `tool_choice` option in `model.bind_tools()`:
|
||||
|
||||
```python
|
||||
from langchain_core.tools import tool
|
||||
|
||||
# highlight-next-line
|
||||
@tool(return_direct=True)
|
||||
def greet(user_name: str) -> int:
|
||||
"""Greet user."""
|
||||
return f"Hello {user_name}!"
|
||||
|
||||
tools = [greet]
|
||||
|
||||
agent = create_react_agent(
|
||||
# highlight-next-line
|
||||
model=model.bind_tools(tools, tool_choice={"type": "tool", "name": "greet"}),
|
||||
tools=tools
|
||||
)
|
||||
|
||||
agent.invoke(
|
||||
{"messages": [{"role": "user", "content": "Hi, I am Bob"}]}
|
||||
)
|
||||
```
|
||||
|
||||
!!! Warning "Avoid infinite loops"
|
||||
|
||||
Forcing tool usage without stopping conditions can create infinite loops. Use one of the following safeguards:
|
||||
|
||||
- Mark the tool with [`return_direct=True`](#return-tool-results-directly) to end the loop after execution.
|
||||
- Set [`recursion_limit`](../concepts/low_level.md#recursion-limit) to restrict the number of execution steps.
|
||||
|
||||
## Handle tool errors
|
||||
|
||||
By default, the agent will catch all exceptions raised during tool calls and will pass those as tool messages to the LLM. To control how the errors are handled, you can use the prebuilt [`ToolNode`][langgraph.prebuilt.tool_node.ToolNode] — the node that executes tools inside `create_react_agent` — via its `handle_tool_errors` parameter:
|
||||
|
||||
=== "Enable error handling (default)"
|
||||
|
||||
```python
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
|
||||
def multiply(a: int, b: int) -> int:
|
||||
"""Multiply two numbers."""
|
||||
if a == 42:
|
||||
raise ValueError("The ultimate error")
|
||||
return a * b
|
||||
|
||||
# Run with error handling (default)
|
||||
agent = create_react_agent(
|
||||
model="anthropic:claude-3-7-sonnet-latest",
|
||||
tools=[multiply]
|
||||
)
|
||||
agent.invoke(
|
||||
{"messages": [{"role": "user", "content": "what's 42 x 7?"}]}
|
||||
)
|
||||
```
|
||||
|
||||
=== "Disable error handling"
|
||||
|
||||
```python
|
||||
from langgraph.prebuilt import create_react_agent, ToolNode
|
||||
|
||||
def multiply(a: int, b: int) -> int:
|
||||
"""Multiply two numbers."""
|
||||
if a == 42:
|
||||
raise ValueError("The ultimate error")
|
||||
return a * b
|
||||
|
||||
# highlight-next-line
|
||||
tool_node = ToolNode(
|
||||
[multiply],
|
||||
# highlight-next-line
|
||||
handle_tool_errors=False # (1)!
|
||||
)
|
||||
agent_no_error_handling = create_react_agent(
|
||||
model="anthropic:claude-3-7-sonnet-latest",
|
||||
tools=tool_node
|
||||
)
|
||||
agent_no_error_handling.invoke(
|
||||
{"messages": [{"role": "user", "content": "what's 42 x 7?"}]}
|
||||
)
|
||||
```
|
||||
|
||||
1. This disables error handling (enabled by default). See all available strategies in the [API reference][langgraph.prebuilt.tool_node.ToolNode].
|
||||
|
||||
=== "Custom error handling"
|
||||
|
||||
```python
|
||||
from langgraph.prebuilt import create_react_agent, ToolNode
|
||||
|
||||
def multiply(a: int, b: int) -> int:
|
||||
"""Multiply two numbers."""
|
||||
if a == 42:
|
||||
raise ValueError("The ultimate error")
|
||||
return a * b
|
||||
|
||||
# highlight-next-line
|
||||
tool_node = ToolNode(
|
||||
[multiply],
|
||||
# highlight-next-line
|
||||
handle_tool_errors=(
|
||||
"Can't use 42 as a first operand, you must switch operands!" # (1)!
|
||||
)
|
||||
)
|
||||
agent_custom_error_handling = create_react_agent(
|
||||
model="anthropic:claude-3-7-sonnet-latest",
|
||||
tools=tool_node
|
||||
)
|
||||
agent_custom_error_handling.invoke(
|
||||
{"messages": [{"role": "user", "content": "what's 42 x 7?"}]}
|
||||
)
|
||||
```
|
||||
|
||||
1. This provides a custom message to send to the LLM in case of an exception. See all available strategies in the [API reference][langgraph.prebuilt.tool_node.ToolNode].
|
||||
|
||||
See [API reference][langgraph.prebuilt.tool_node.ToolNode] for more information on different tool error handling options.
|
||||
|
||||
## Working with memory
|
||||
|
||||
LangGraph allows access to short-term and long-term memory from tools. See [Memory](./memory.md) guide for more information on:
|
||||
|
||||
* how to [read](./memory.md#read-short-term) from and [write](./memory.md#write-short-term) to **short-term** memory
|
||||
* how to [read](./memory.md#read-long-term) from and [write](./memory.md#write-long-term) to **long-term** memory
|
||||
|
||||
## Prebuilt tools
|
||||
|
||||
You can use prebuilt tools from model providers by passing a dictionary with tool specs to the `tools` parameter of `create_react_agent`. For example, to use the `web_search_preview` tool from OpenAI:
|
||||
|
||||
```python
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
|
||||
agent = create_react_agent(
|
||||
model="openai:gpt-4o-mini",
|
||||
tools=[{"type": "web_search_preview"}]
|
||||
)
|
||||
response = agent.invoke(
|
||||
{"messages": ["What was a positive news story from today?"]}
|
||||
)
|
||||
```
|
||||
|
||||
Additionally, LangChain supports a wide range of prebuilt tool integrations for interacting with APIs, databases, file systems, web data, and more. These tools extend the functionality of agents and enable rapid development.
|
||||
|
||||
You can browse the full list of available integrations in the [LangChain integrations directory](https://python.langchain.com/docs/integrations/tools/).
|
||||
|
||||
Some commonly used tool categories include:
|
||||
|
||||
- **Search**: Bing, SerpAPI, Tavily
|
||||
- **Code interpreters**: Python REPL, Node.js REPL
|
||||
- **Databases**: SQL, MongoDB, Redis
|
||||
- **Web data**: Web scraping and browsing
|
||||
- **APIs**: OpenWeatherMap, NewsAPI, and others
|
||||
|
||||
These integrations can be configured and added to your agents using the same `tools` parameter shown in the examples above.
|
||||
|
||||
@@ -13,7 +13,7 @@ You can use a prebuilt chat UI for interacting with any LangGraph agent through
|
||||
|
||||
## Run agent in UI
|
||||
|
||||
First, set up LangGraph API server [locally](./deployment.md#launch-langgraph-server-locally) or deploy your agent on [LangGraph Platform](https://langchain-ai.github.io/langgraph/cloud/quick_start/).
|
||||
First, set up LangGraph API server [locally](../tutorials/langgraph-platform/local-server.md) or deploy your agent on [LangGraph Platform](https://langchain-ai.github.io/langgraph/cloud/quick_start/).
|
||||
|
||||
Then, navigate to [Agent Chat UI](https://agentchat.vercel.app), or clone the repository and [run the dev server locally](https://github.com/langchain-ai/agent-chat-ui?tab=readme-ov-file#setup):
|
||||
|
||||
@@ -25,7 +25,7 @@ Then, navigate to [Agent Chat UI](https://agentchat.vercel.app), or clone the re
|
||||
|
||||
## Add human-in-the-loop
|
||||
|
||||
Agent Chat UI has full support for [human-in-the-loop](../concepts/human_in_the_loop.md) workflows. To try it out, replace the agent code in `src/agent/graph.py` (from the [deployment](./deployment.md) guide) with this [agent implementation](./human-in-the-loop.md#using-with-agent-inbox):
|
||||
Agent Chat UI has full support for [human-in-the-loop](../concepts/human_in_the_loop.md) workflows. To try it out, replace the agent code in `src/agent/graph.py` (from the [deployment](../tutorials/langgraph-platform/local-server.md) guide) with this [agent implementation](../how-tos/human_in_the_loop/add-human-in-the-loop.md#add-interrupts-to-any-tool):
|
||||
|
||||
<video controls src="../assets/interrupt-chat-ui.mp4" type="video/mp4"></video>
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# Data Storage and Privacy
|
||||
|
||||
This document describes how data is processed in the LangGraph CLI and the LangGraph Server for both the in-memory server (`langgraph dev`) and the local Docker server (`langgraph up`). It also describes what data is tracked when interacting with the hosted LangGraph Studio frontend.
|
||||
|
||||
## CLI
|
||||
|
||||
LangGraph **CLI** is the command-line interface for building and running LangGraph applications; see the [CLI guide](../../concepts/langgraph_cli.md) to learn more.
|
||||
|
||||
By default, calls to most CLI commands log a single analytics event upon invocation. This helps us better prioritize improvements to the CLI experience. Each telemetry event contains the calling process's OS, OS version, Python version, the CLI version, the command name (`dev`, `up`, `run`, etc.), and booleans representing whether a flag was passed to the command. You can see the full analytics logic [here](https://github.com/langchain-ai/langgraph/blob/main/libs/cli/langgraph_cli/analytics.py).
|
||||
|
||||
You can disable all CLI telemetry by setting `LANGGRAPH_CLI_NO_ANALYTICS=1`.
|
||||
|
||||
## LangGraph Server (in-memory & docker)
|
||||
|
||||
The [LangGraph Server](../../concepts/langgraph_server.md) provides a durable execution runtime that relies on persisting checkpoints of your application state, long-term memories, thread metadata, assistants, and similar resources to the local file system or a database. Unless you have deliberately customized the storage location, this information is either written to local disk (for `langgraph dev`) or a PostgreSQL database (for `langgraph up` and in all deployments).
|
||||
|
||||
### LangSmith Tracing
|
||||
|
||||
When running the LangGraph server (either in-memory or in Docker), LangSmith tracing may be enabled to facilitate faster debugging and offer observability of graph state and LLM prompts in production. You can always disable tracing by setting `LANGSMITH_TRACING=false` in your server's runtime environment.
|
||||
|
||||
### In-memory development server (`langgraph dev`)
|
||||
|
||||
`langgraph dev` runs an [in-memory development server](../../tutorials/langgraph-platform/local-server.md) as a single Python process, designed for quick development and testing. It saves all checkpointing and memory data to disk within a `.langgraph_api` directory in the current working directory. Apart from the telemetry data described in the [CLI](#cli) section, no data leaves the machine unless you have enabled tracing or your graph code explicitly contacts an external service.
|
||||
|
||||
### Standalone Container (`langgraph up`)
|
||||
|
||||
`langgraph up` builds your local package into a Docker image and runs the server as a [standalone container](../../concepts/deployment_options.md#standalone-container) consisting of three containers: the API server, a PostgreSQL container, and a Redis container. All persistent data (checkpoints, assistants, etc.) are stored in the PostgreSQL database. Redis is used as a pubsub connection for real-time streaming of events. You can encrypt all checkpoints before saving to the database by setting a valid `LANGGRAPH_AES_KEY` environment variable. You can also specify [TTLs](../../how-tos/ttl/configure_ttl.md) for checkpoints and cross-thread memories in `langgraph.json` to control how long data is stored. All persisted threads, memories, and other data can be deleted via the relevant API endpoints.
|
||||
|
||||
Additional API calls are made to confirm that the server has a valid license and to track the number of executed runs and tasks. Periodically, the API server validates the provided license key (or API key).
|
||||
|
||||
If you've disabled [tracing](#langsmith-tracing), no user data is persisted externally unless your graph code explicitly contacts an external service.
|
||||
|
||||
## Studio
|
||||
|
||||
[LangGraph Studio](../../concepts/langgraph_studio.md) is a graphical interface for interacting with your LangGraph server. It does not persist any private data (the data you send to your server is not sent to LangSmith). Though the studio interface is served at [smith.langchain.com](https://smith.langchain.com), it is run in your browser and connects directly to your local LangGraph server so that no data needs to be sent to LangSmith.
|
||||
|
||||
If you are logged in, LangSmith does collect some usage analytics to help improve studio's user experience. This includes:
|
||||
|
||||
- Page visits and navigation patterns
|
||||
- User actions (button clicks)
|
||||
- Browser type and version
|
||||
- Screen resolution and viewport size
|
||||
|
||||
Importantly, no application data or code (or other sensitive configuration details) are collected. All of that is stored in the persistence layer of your LangGraph server. When using Studio anonymously, no account creation is required and usage analytics are not collected.
|
||||
|
||||
## Quick reference
|
||||
|
||||
In summary, you can opt-out of server-side telemetry by turning off CLI analytics and disabling tracing.
|
||||
|
||||
| Variable | Purpose | Default |
|
||||
| ------------------------------ | ------------------------- | -------------------------------- |
|
||||
| `LANGGRAPH_CLI_NO_ANALYTICS=1` | Disable CLI analytics | Analytics enabled |
|
||||
| `LANGSMITH_API_KEY` | Enable LangSmith tracing | Tracing disabled |
|
||||
| `LANGSMITH_TRACING=false` | Disable LangSmith tracing | Depends on environment |
|
||||
@@ -1,5 +0,0 @@
|
||||
# Runs
|
||||
|
||||
A run is an invocation of an [assistant](../../concepts/assistants.md). Each run may have its own input, configuration, and metadata, which may affect execution and output of the underlying graph. A run can optionally be executed on a [thread](./threads.md).
|
||||
|
||||
The LangGraph Platform API provides several endpoints for creating and managing runs. See the [API reference](../../cloud/reference/api/api_ref.html#tag/thread-runs/) for more details.
|
||||
@@ -1,138 +0,0 @@
|
||||
# Streaming
|
||||
|
||||
Streaming is critical for making LLM applications feel responsive to end users.
|
||||
When creating a streaming run, the **streaming mode** determines what kinds of data are streamed back to the API client.
|
||||
|
||||
## Supported streaming modes
|
||||
|
||||
LangGraph Platform supports the following streaming modes:
|
||||
|
||||
| Mode | Description | LangGraph Library Method |
|
||||
|----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------|
|
||||
| **`values`** | Stream the full graph state after each [super-step](https://langchain-ai.github.io/langgraph/concepts/low_level/#graphs). [Guide](../how-tos/streaming.md#stream-graph-state) | `.stream()` / `.astream()` with `stream_mode="values"` |
|
||||
| **`updates`** | Stream only the updates to the graph state after each node. [Guide](../how-tos/streaming.md#stream-graph-state) | `.stream()` / `.astream()` with `stream_mode="updates"` |
|
||||
| **`messages-tuple`** | Stream LLM tokens for any messages generated inside the graph (useful for chat apps). [Guide](../how-tos/streaming.md#messages) | `.stream()` / `.astream()` with `stream_mode="messages"` |
|
||||
| **`debug`** | Stream debug information throughout graph execution. [Guide](../how-tos/streaming.md#debug) | `.stream()` / `.astream()` with `stream_mode="debug"` |
|
||||
| **`custom`** | Stream custom data. [Guide](../../how-tos/streaming.md#stream-custom-data) | `.stream()` / `.astream()` with `stream_mode="custom"` |
|
||||
| **`events`** | Stream all events (including the state of the graph); mainly useful when migrating large LCEL apps. [Guide](../how-tos/streaming.md#stream-events) | `.astream_events()` |
|
||||
|
||||
✅ You can also **combine multiple modes** at the same time. See the [how-to guide](../how-tos/streaming.md#stream-multiple-modes) for configuration details.
|
||||
|
||||
## Stateless runs
|
||||
|
||||
If you don't want to **persist the outputs** of a streaming run in the [checkpointer](../../concepts/persistence.md) DB, you can create a stateless run without creating a thread:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
client = get_client(url=<DEPLOYMENT_URL>, api_key=<API_KEY>)
|
||||
|
||||
async for chunk in client.runs.stream(
|
||||
# highlight-next-line
|
||||
None, # (1)!
|
||||
assistant_id,
|
||||
input=inputs,
|
||||
stream_mode="updates"
|
||||
):
|
||||
print(chunk.data)
|
||||
```
|
||||
|
||||
1. We are passing `None` instead of a `thread_id` UUID.
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL>, apiKey: <API_KEY> });
|
||||
|
||||
// create a streaming run
|
||||
// highlight-next-line
|
||||
const streamResponse = client.runs.stream(
|
||||
// highlight-next-line
|
||||
null, // (1)!
|
||||
assistantID,
|
||||
{
|
||||
input,
|
||||
streamMode: "updates"
|
||||
}
|
||||
);
|
||||
for await (const chunk of streamResponse) {
|
||||
console.log(chunk.data);
|
||||
}
|
||||
```
|
||||
|
||||
1. We are passing `None` instead of a `thread_id` UUID.
|
||||
|
||||
=== "cURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'x-api-key: <API_KEY>'
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": <inputs>,
|
||||
\"stream_mode\": \"updates\"
|
||||
}"
|
||||
```
|
||||
|
||||
## Join and stream
|
||||
|
||||
LangGraph Platform allows you to join an active [background run](../how-tos/background_run.md) and stream outputs from it. To do so, you can use [LangGraph SDK's](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/) `client.runs.join_stream` method:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
client = get_client(url=<DEPLOYMENT_URL>, api_key=<API_KEY>)
|
||||
|
||||
# highlight-next-line
|
||||
async for chunk in client.runs.join_stream(
|
||||
thread_id,
|
||||
# highlight-next-line
|
||||
run_id, # (1)!
|
||||
):
|
||||
print(chunk)
|
||||
```
|
||||
|
||||
1. This is the `run_id` of an existing run you want to join.
|
||||
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL>, apiKey: <API_KEY> });
|
||||
|
||||
// highlight-next-line
|
||||
const streamResponse = client.runs.joinStream(
|
||||
threadID,
|
||||
// highlight-next-line
|
||||
runId // (1)!
|
||||
);
|
||||
for await (const chunk of streamResponse) {
|
||||
console.log(chunk);
|
||||
}
|
||||
```
|
||||
|
||||
1. This is the `run_id` of an existing run you want to join.
|
||||
|
||||
=== "cURL"
|
||||
|
||||
```bash
|
||||
curl --request GET \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/<RUN_ID>/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'x-api-key: <API_KEY>'
|
||||
```
|
||||
|
||||
!!! warning "Outputs not buffered"
|
||||
|
||||
When you use `.join_stream`, output is not buffered, so any output produced before joining will not be received.
|
||||
|
||||
## API Reference
|
||||
|
||||
For API usage and implementation, refer to the [API reference](../reference/api/api_ref.html#tag/thread-runs/POST/threads/{thread_id}/runs/stream).
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
# Threads
|
||||
|
||||
A thread contains the accumulated state of a sequence of [runs](./runs.md). When a run is executed, the [state](../../concepts/low_level.md#state) of the underlying graph of the assistant will be persisted to the thread.
|
||||
|
||||
A thread's current and historical state can be retrieved. To persist state, a thread must be created prior to executing a run.
|
||||
|
||||
The state of a thread at a particular point in time is called a [checkpoint](../../concepts/persistence.md#checkpoints). Checkpoints are persisted and can be used to restore the state of a thread at a later time.
|
||||
|
||||
## Learn more
|
||||
|
||||
* For more on threads and checkpoints, see this section of the [LangGraph conceptual guide](../../concepts/persistence.md).
|
||||
* The LangGraph Platform API provides several endpoints for creating and managing threads and thread state. See the [API reference](../../cloud/reference/api/api_ref.html#tag/threads) for more details.
|
||||
@@ -62,6 +62,15 @@ Starting from the `LangGraph Platform` view...
|
||||
1. In the panel, select the `Server` tab to view server logs for the revision. Server logs are only available after a revision has been deployed.
|
||||
1. Within the `Server` tab, adjust the date/time range picker as needed. By default, the date/time range picker is set to the `Last 7 days`.
|
||||
|
||||
## View Deployment Metrics
|
||||
|
||||
Starting from the <a href="https://smith.langchain.com/" target="_blank">LangSmith UI</a>...
|
||||
|
||||
1. In the left-hand navigation panel, select `LangGraph Platform`. The `LangGraph Platform` view contains a list of existing LangGraph Platform deployments.
|
||||
1. Select an existing deployment to monitor.
|
||||
1. Select the `Monitoring` tab to view the deployment metrics. See a list of [all available metrics](../../concepts/langgraph_control_plane.md#monitoring).
|
||||
1. Within the `Monitoring` tab, use the date/time range picker as needed. By default, the date/time range picker is set to the `Last 15 minutes`.
|
||||
|
||||
## Interrupt Revision
|
||||
|
||||
Interrupting a revision will stop deployment of the revision.
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
# Egress for Subscription Metrics and Operational Metadata
|
||||
|
||||
> **Important: Self Hosted Only**
|
||||
> This section only applies to customers who are not running in offline mode and assumes you are using a self-hosted LangGraph Platform instance.
|
||||
> This does not apply to SaaS or Hybrid deployments.
|
||||
|
||||
Self-Hosted LangGraph Platform instances store all information locally and will never send sensitive information outside of your network. We currently only track platform usage for billing purposes according to the entitlements in your order. In order to better remotely support our customers, we do require egress to `https://beacon.langchain.com`.
|
||||
|
||||
In the future, we will be introducing support diagnostics to help us ensure that the LangGraph Platform is running at an optimal level within your environment.
|
||||
|
||||
> **Warning**
|
||||
> **This will require egress to `https://beacon.langchain.com` from your network.**
|
||||
> **If using an API key, you will also need to allow egress to `https://api.smith.langchain.com` or `https://eu.api.smith.langchain.com` for API key verification.**
|
||||
|
||||
Generally, data that we send to Beacon can be categorized as follows:
|
||||
|
||||
- **Subscription Metrics**
|
||||
- Subscription metrics are used to determine level of access and utilization of LangSmith. This includes, but are not limited to:
|
||||
- Nodes Executed
|
||||
- Runs Executed
|
||||
- License Key Verification
|
||||
- **Operational Metadata**
|
||||
- This metadata will contain and collect the above subscription metrics to assist with remote support, allowing the LangChain team to diagnose and troubleshoot performance issues more effectively and proactively.
|
||||
|
||||
## Example Payloads
|
||||
|
||||
In an effort to maximize transparency, we provide sample payloads here:
|
||||
|
||||
### License Verification (If using an Enterprise License)
|
||||
|
||||
**Endpoint:**
|
||||
|
||||
`POST beacon.langchain.com/v1/beacon/verify`
|
||||
|
||||
**Request:**
|
||||
|
||||
```json
|
||||
{
|
||||
"license": "<YOUR_LICENSE_KEY>"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"token": "Valid JWT" // Short-lived JWT token to avoid repeated license checks
|
||||
}
|
||||
```
|
||||
|
||||
### Api Key Verification (If using a LangSmith API Key)
|
||||
|
||||
**Endpoint:**
|
||||
`POST api.smith.langchain.com/auth`
|
||||
|
||||
**Request:**
|
||||
|
||||
```json
|
||||
"Headers": {
|
||||
X-Api-Key: <YOUR_API_KEY>
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"org_config": {
|
||||
"org_id": "3a1c2b6f-4430-4b92-8a5b-79b8b567bbc1",
|
||||
... // Additional organization details
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Usage Reporting
|
||||
|
||||
**Endpoint:**
|
||||
|
||||
`POST beacon.langchain.com/v1/metadata/submit`
|
||||
|
||||
**Request:**
|
||||
|
||||
```json
|
||||
{
|
||||
"license": "<YOUR_LICENSE_KEY>",
|
||||
"from_timestamp": "2025-01-06T09:00:00Z",
|
||||
"to_timestamp": "2025-01-06T10:00:00Z",
|
||||
"tags": {
|
||||
"langgraph.python.version": "0.1.0",
|
||||
"langgraph_api.version": "0.2.0",
|
||||
"langgraph.platform.revision": "abc123",
|
||||
"langgraph.platform.variant": "standard",
|
||||
"langgraph.platform.host": "host-1",
|
||||
"langgraph.platform.tenant_id": "3a1c2b6f-4430-4b92-8a5b-79b8b567bbc1",
|
||||
"langgraph.platform.project_id": "c5b5f53a-4716-4326-8967-d4f7f7799735",
|
||||
"langgraph.platform.plan": "enterprise",
|
||||
"user_app.uses_indexing": "true",
|
||||
"user_app.uses_custom_app": "false",
|
||||
"user_app.uses_custom_auth": "true",
|
||||
"user_app.uses_thread_ttl": "true",
|
||||
"user_app.uses_store_ttl": "false"
|
||||
},
|
||||
"measures": {
|
||||
"langgraph.platform.runs": 150,
|
||||
"langgraph.platform.nodes": 450
|
||||
},
|
||||
"logs": []
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
"204 No Content"
|
||||
```
|
||||
|
||||
## Our Commitment
|
||||
|
||||
LangChain will not store any sensitive information in the Subscription Metrics or Operational Metadata. Any data collected will not be shared with a third party. If you have any concerns about the data being sent, please reach out to your account team.
|
||||
@@ -20,7 +20,7 @@ my-app/
|
||||
|-- openai_agent.py # code for your graph
|
||||
```
|
||||
|
||||
where the graph is defined in `openai_agent.py`.
|
||||
where the graph is defined in `openai_agent.py`.
|
||||
|
||||
### No rebuild
|
||||
|
||||
@@ -28,11 +28,11 @@ In the standard LangGraph API configuration, the server uses the compiled graph
|
||||
|
||||
```python
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langgraph.graph import END, START, StateGraph, MessagesState
|
||||
from langgraph.graph import END, START, MessageGraph
|
||||
|
||||
model = ChatOpenAI(temperature=0)
|
||||
|
||||
graph_workflow = StateGraph(MessagesState)
|
||||
graph_workflow = MessageGraph()
|
||||
|
||||
graph_workflow.add_node("agent", model)
|
||||
graph_workflow.add_edge("agent", END)
|
||||
@@ -61,7 +61,7 @@ To make your graph rebuild on each new run with custom configuration, you need t
|
||||
from typing import Annotated
|
||||
from typing_extensions import TypedDict
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langgraph.graph import END, START
|
||||
from langgraph.graph import END, START, MessageGraph
|
||||
from langgraph.graph.state import StateGraph
|
||||
from langgraph.graph.message import add_messages
|
||||
from langgraph.prebuilt import ToolNode
|
||||
@@ -144,4 +144,4 @@ Finally, you need to specify the path to your graph-making function (`make_graph
|
||||
}
|
||||
```
|
||||
|
||||
See more info on LangGraph API configuration file [here](../reference/cli.md#configuration-file)
|
||||
See more info on LangGraph API configuration file [here](../reference/cli.md#configuration-file)
|
||||
@@ -3,7 +3,7 @@
|
||||
Before deploying, review the [conceptual guide for the Self-Hosted Control Plane](../../concepts/langgraph_self_hosted_control_plane.md) deployment option.
|
||||
|
||||
!!! info "Important"
|
||||
The Self-Hosted Control Plane deployment option is currently in beta stage and requires an [Enterprise](../../concepts/plans.md) plan.
|
||||
The Self-Hosted Control Plane deployment option requires an [Enterprise](../../concepts/plans.md) plan.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
@@ -23,6 +23,8 @@ Before deploying, review the [conceptual guide for the Self-Hosted Control Plane
|
||||
|
||||
kubectl get storageclass
|
||||
|
||||
1. Egress to `https://beacon.langchain.com` from your network. This is required for license verification and usage reporting if not running in air-gapped mode. See the [Egress documentation](../../cloud/deployment/egress.md) for more details.
|
||||
|
||||
## Setup
|
||||
|
||||
1. As part of configuring your Self-Hosted LangSmith instance, you enable the `langgraphPlatform` option. This will provision a few key resources.
|
||||
@@ -30,18 +32,16 @@ Before deploying, review the [conceptual guide for the Self-Hosted Control Plane
|
||||
1. `LangGraphPlatform CRD`: A CRD for LangGraph Platform deployments. This contains the spec for managing an instance of a LangGraph platform deployment.
|
||||
1. `operator`: This operator handles changes to your LangGraph Platform CRDs.
|
||||
1. `host-backend`: This is the [control plane](../../concepts/langgraph_control_plane.md).
|
||||
1. Two additional images will be used by the chart.
|
||||
1. Two additional images will be used by the chart. Use the images that are specified in the latest release.
|
||||
|
||||
hostBackendImage:
|
||||
repository: "docker.io/langchain/hosted-langserve-backend"
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "0.9.80"
|
||||
operatorImage:
|
||||
repository: "docker.io/langchain/langgraph-operator"
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "aa9dff4"
|
||||
|
||||
1. In your `langsmith_config.yaml` file, enable the `langgraphPlatform` option. Note that you must also have a valid ingress setup:
|
||||
1. In your config file for langsmith (usually `langsmith_config.yaml`, enable the `langgraphPlatform` option. Note that you must also have a valid ingress setup:
|
||||
|
||||
config:
|
||||
langgraphPlatform:
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Before deploying, review the [conceptual guide for the Self-Hosted Data Plane](../../concepts/langgraph_self_hosted_data_plane.md) deployment option.
|
||||
|
||||
!!! info "Important"
|
||||
The Self-Hosted Data Plane deployment option is currently in beta stage and requires an [Enterprise](../../concepts/plans.md) plan.
|
||||
The Self-Hosted Data Plane deployment option requires an [Enterprise](../../concepts/plans.md) plan.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
@@ -15,11 +15,15 @@ Before deploying, review the [conceptual guide for the Self-Hosted Data Plane](.
|
||||
### Prerequisites
|
||||
1. `KEDA` is installed on your cluster.
|
||||
|
||||
helm repo add kedacore https://kedacore.github.io/charts
|
||||
helm repo add kedacore https://kedacore.github.io/charts
|
||||
helm install keda kedacore/keda --namespace keda --create-namespace
|
||||
|
||||
1. A valid `Ingress` controller is installed on your cluster.
|
||||
1. You have slack space in your cluster for multiple deployments. `Cluster-Autoscaler` is recommended to automatically provision new nodes.
|
||||
1. You will need to enable egress to two control plane URLs. The listener polls these endpoints for deployments:
|
||||
|
||||
https://api.host.langchain.com
|
||||
https://api.smith.langchain.com
|
||||
|
||||
### Setup
|
||||
|
||||
@@ -31,7 +35,6 @@ Before deploying, review the [conceptual guide for the Self-Hosted Data Plane](.
|
||||
1. Configure your `langgraph-dataplane-values.yaml` file.
|
||||
|
||||
config:
|
||||
langgraphPlatformLicenseKey: "" # Your LangGraph Platform license key
|
||||
langsmithApiKey: "" # API Key of your Workspace
|
||||
langsmithWorkspaceId: "" # Workspace ID
|
||||
hostBackendUrl: "https://api.host.langchain.com" # Only override this if on EU
|
||||
|
||||
@@ -108,11 +108,11 @@ from langgraph.graph import StateGraph, END, START
|
||||
from my_agent.utils.nodes import call_model, should_continue, tool_node # import nodes
|
||||
from my_agent.utils.state import AgentState # import state
|
||||
|
||||
# Define the config
|
||||
class GraphConfig(TypedDict):
|
||||
# Define the runtime context
|
||||
class GraphContext(TypedDict):
|
||||
model_name: Literal["anthropic", "openai"]
|
||||
|
||||
workflow = StateGraph(AgentState, config_schema=GraphConfig)
|
||||
workflow = StateGraph(AgentState, context_schema=GraphContext)
|
||||
workflow.add_node("agent", call_model)
|
||||
workflow.add_node("action", tool_node)
|
||||
workflow.add_edge(START, "agent")
|
||||
|
||||
@@ -121,11 +121,11 @@ from langgraph.graph import StateGraph, END, START
|
||||
from my_agent.utils.nodes import call_model, should_continue, tool_node # import nodes
|
||||
from my_agent.utils.state import AgentState # import state
|
||||
|
||||
# Define the config
|
||||
class GraphConfig(TypedDict):
|
||||
# Define the runtime context
|
||||
class GraphContext(TypedDict):
|
||||
model_name: Literal["anthropic", "openai"]
|
||||
|
||||
workflow = StateGraph(AgentState, config_schema=GraphConfig)
|
||||
workflow = StateGraph(AgentState, context_schema=GraphContext)
|
||||
workflow.add_node("agent", call_model)
|
||||
workflow.add_node("action", tool_node)
|
||||
workflow.add_edge(START, "agent")
|
||||
|
||||
@@ -24,6 +24,7 @@ Before deploying, review the [conceptual guide for the Standalone Container](../
|
||||
1. `LANGSMITH_API_KEY`: (if using [Lite](../../concepts/langgraph_server.md#server-versions)) LangSmith API key. This will be used to authenticate ONCE at server start up.
|
||||
1. `LANGGRAPH_CLOUD_LICENSE_KEY`: (if using [Enterprise](../../concepts/langgraph_data_plane.md#licensing)) LangGraph Platform license key. This will be used to authenticate ONCE at server start up.
|
||||
1. `LANGSMITH_ENDPOINT`: To send traces to a [self-hosted LangSmith](https://docs.smith.langchain.com/self_hosting) instance, set `LANGSMITH_ENDPOINT` to the hostname of the self-hosted LangSmith instance.
|
||||
1. Egress to `https://beacon.langchain.com` from your network. This is required for license verification and usage reporting if not running in air-gapped mode. See the [Egress documentation](../../cloud/deployment/egress.md) for more details.
|
||||
|
||||
## Kubernetes (Helm)
|
||||
|
||||
|
||||
@@ -1,38 +1,8 @@
|
||||
# Human-in-the-loop
|
||||
# Human-in-the-loop using Server API
|
||||
|
||||
LangGraph supports robust **human-in-the-loop (HIL)** workflows, enabling human intervention at any point in an automated process. This is especially useful in large language model (LLM)-driven applications where model output may require validation, correction, or additional context.
|
||||
To review, edit, and approve tool calls in an agent or workflow, use LangGraph's [human-in-the-loop](../../concepts/human_in_the_loop.md) features.
|
||||
|
||||
Please see [the overview of LangGraph human-in-the-loop](../../concepts/human_in_the_loop.md) features for more information.
|
||||
|
||||
## `interrupt`
|
||||
|
||||
The [`interrupt` function][langgraph.types.interrupt] in LangGraph enables human-in-the-loop workflows by pausing the graph at a specific node, presenting information to a human, and resuming the graph with their input. It's useful for tasks like approvals, edits, or gathering additional context.
|
||||
|
||||
The graph is resumed using a [`Command`][langgraph.types.Command] object that provides the human's response.
|
||||
|
||||
**Graph node with `interrupt`:**
|
||||
|
||||
```python
|
||||
# highlight-next-line
|
||||
from langgraph.types import interrupt, Command
|
||||
|
||||
def human_node(state: State):
|
||||
# highlight-next-line
|
||||
value = interrupt( # (1)!
|
||||
{
|
||||
"text_to_revise": state["some_text"] # (2)!
|
||||
}
|
||||
)
|
||||
return {
|
||||
"some_text": value # (3)!
|
||||
}
|
||||
```
|
||||
|
||||
1. `interrupt(...)` pauses execution at `human_node`, surfacing the given payload to a human.
|
||||
2. Any JSON serializable value can be passed to the `interrupt` function. Here, a dict containing the text to revise.
|
||||
3. Once resumed, the return value of `interrupt(...)` is the human-provided input, which is used to update the state.
|
||||
|
||||
**LangGraph API invoke & resume:**
|
||||
## Dynamic interrupts
|
||||
|
||||
=== "Python"
|
||||
|
||||
@@ -60,9 +30,7 @@ def human_node(state: State):
|
||||
# > [
|
||||
# > {
|
||||
# > 'value': {'text_to_revise': 'original text'},
|
||||
# > 'resumable': True,
|
||||
# > 'ns': ['human_node:fc722478-2f21-0578-c572-d9fc4dd07c3b'],
|
||||
# > 'when': 'during'
|
||||
# > 'id': '...',
|
||||
# > }
|
||||
# > ]
|
||||
|
||||
@@ -233,9 +201,7 @@ def human_node(state: State):
|
||||
# > [
|
||||
# > {
|
||||
# > 'value': {'text_to_revise': 'original text'},
|
||||
# > 'resumable': True,
|
||||
# > 'ns': ['human_node:fc722478-2f21-0578-c572-d9fc4dd07c3b'],
|
||||
# > 'when': 'during'
|
||||
# > 'id': '...',
|
||||
# > }
|
||||
# > ]
|
||||
|
||||
@@ -335,8 +301,186 @@ def human_node(state: State):
|
||||
}"
|
||||
```
|
||||
|
||||
## Static interrupts
|
||||
|
||||
Static interrupts (also known as static breakpoints) are triggered either before or after a node executes.
|
||||
|
||||
!!! warning
|
||||
|
||||
Static interrupts are **not** recommended for human-in-the-loop workflows. They are best used for debugging and testing.
|
||||
|
||||
You can set static interrupts by specifying `interrupt_before` and `interrupt_after` at compile time:
|
||||
|
||||
```python
|
||||
# highlight-next-line
|
||||
graph = graph_builder.compile( # (1)!
|
||||
# highlight-next-line
|
||||
interrupt_before=["node_a"], # (2)!
|
||||
# highlight-next-line
|
||||
interrupt_after=["node_b", "node_c"], # (3)!
|
||||
)
|
||||
```
|
||||
|
||||
1. The breakpoints are set during `compile` time.
|
||||
2. `interrupt_before` specifies the nodes where execution should pause before the node is executed.
|
||||
3. `interrupt_after` specifies the nodes where execution should pause after the node is executed.
|
||||
|
||||
Alternatively, you can set static interrupts at run time:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
# highlight-next-line
|
||||
await client.runs.wait( # (1)!
|
||||
thread_id,
|
||||
assistant_id,
|
||||
inputs=inputs,
|
||||
# highlight-next-line
|
||||
interrupt_before=["node_a"], # (2)!
|
||||
# highlight-next-line
|
||||
interrupt_after=["node_b", "node_c"] # (3)!
|
||||
)
|
||||
```
|
||||
|
||||
1. `client.runs.wait` is called with the `interrupt_before` and `interrupt_after` parameters. This is a run-time configuration and can be changed for every invocation.
|
||||
2. `interrupt_before` specifies the nodes where execution should pause before the node is executed.
|
||||
3. `interrupt_after` specifies the nodes where execution should pause after the node is executed.
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
// highlight-next-line
|
||||
await client.runs.wait( // (1)!
|
||||
threadID,
|
||||
assistantID,
|
||||
{
|
||||
input: input,
|
||||
// highlight-next-line
|
||||
interruptBefore: ["node_a"], // (2)!
|
||||
// highlight-next-line
|
||||
interruptAfter: ["node_b", "node_c"] // (3)!
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
1. `client.runs.wait` is called with the `interruptBefore` and `interruptAfter` parameters. This is a run-time configuration and can be changed for every invocation.
|
||||
2. `interruptBefore` specifies the nodes where execution should pause before the node is executed.
|
||||
3. `interruptAfter` specifies the nodes where execution should pause after the node is executed.
|
||||
|
||||
=== "cURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/wait \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"interrupt_before\": [\"node_a\"],
|
||||
\"interrupt_after\": [\"node_b\", \"node_c\"],
|
||||
\"input\": <INPUT>
|
||||
}"
|
||||
```
|
||||
|
||||
The following example shows how to add static interrupts:
|
||||
|
||||
=== "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 a thread
|
||||
thread = await client.threads.create()
|
||||
thread_id = thread["thread_id"]
|
||||
|
||||
# Run the graph until the breakpoint
|
||||
result = await client.runs.wait(
|
||||
thread_id,
|
||||
assistant_id,
|
||||
input=inputs # (1)!
|
||||
)
|
||||
|
||||
# Resume the graph
|
||||
await client.runs.wait(
|
||||
thread_id,
|
||||
assistant_id,
|
||||
input=None # (2)!
|
||||
)
|
||||
```
|
||||
|
||||
1. The graph is run until the first breakpoint is hit.
|
||||
2. The graph is resumed by passing in `None` for the input. This will run the graph until the next breakpoint is hit.
|
||||
|
||||
=== "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 a thread
|
||||
const thread = await client.threads.create();
|
||||
const threadID = thread["thread_id"];
|
||||
|
||||
// Run the graph until the breakpoint
|
||||
const result = await client.runs.wait(
|
||||
threadID,
|
||||
assistantID,
|
||||
{ input: input } // (1)!
|
||||
);
|
||||
|
||||
// Resume the graph
|
||||
await client.runs.wait(
|
||||
threadID,
|
||||
assistantID,
|
||||
{ input: null } // (2)!
|
||||
);
|
||||
```
|
||||
|
||||
1. The graph is run until the first breakpoint is hit.
|
||||
2. The graph is resumed by passing in `null` for the input. This will run the graph until the next breakpoint is hit.
|
||||
|
||||
=== "cURL"
|
||||
|
||||
Create a thread:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
Run the graph until the breakpoint:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/wait \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": <INPUT>
|
||||
}"
|
||||
```
|
||||
|
||||
Resume the graph:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/wait \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\"
|
||||
}"
|
||||
```
|
||||
|
||||
|
||||
## Learn more
|
||||
|
||||
- [**LangGraph human-in-the-loop overview**](../../concepts/human_in_the_loop.md): learn more about LangGraph human-in-the-loop features.
|
||||
- [**Design patterns**](../../how-tos/human_in_the_loop/add-human-in-the-loop.md#design-patterns): learn how to implement patterns like approving/rejecting actions, requesting user input, and more.
|
||||
- [**How to review tool calls**](./human_in_the_loop_review_tool_calls.md): detailed examples of how to review and approve/edit tool calls or provide feedback to the tool-calling LLM.
|
||||
- [Human-in-the-loop conceptual guide](../../concepts/human_in_the_loop.md): learn more about LangGraph human-in-the-loop features.
|
||||
- [Common patterns](../../how-tos/human_in_the_loop/add-human-in-the-loop.md#common-patterns): learn how to implement patterns like approving/rejecting actions, requesting user input, tool call review, and validating human input.
|
||||
@@ -2,21 +2,20 @@
|
||||
|
||||
In this guide we will show how to create, configure, and manage an [assistant](../../concepts/assistants.md).
|
||||
|
||||
First, as a brief refresher on the concept of configurations, consider the following simple `call_model` node and configuration schema. Observe that this node tries to read and use the `model_name` as defined by the `config` object's `configurable`.
|
||||
First, as a brief refresher on the concept of runtime context, consider the following simple `call_model` node and context schema. Observe that this node tries to read and use the `model_provider` as defined by the `Runtime` object's `context` property.
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class ContextSchema:
|
||||
llm_provider: str = "anthropic"
|
||||
|
||||
class ConfigSchema(TypedDict):
|
||||
model_name: str
|
||||
builder = StateGraph(AgentState, context_schema=ContextSchema)
|
||||
|
||||
builder = StateGraph(AgentState, config_schema=ConfigSchema)
|
||||
|
||||
def call_model(state, config):
|
||||
def call_model(state, runtime: Runtime[ContextSchema]):
|
||||
messages = state["messages"]
|
||||
model_name = config.get('configurable', {}).get("model_name", "anthropic")
|
||||
model = _get_model(model_name)
|
||||
model = _get_model(runtime.context.llm_provider)
|
||||
response = model.invoke(messages)
|
||||
# We return a list, because this will get added to the existing list
|
||||
return {"messages": [response]}
|
||||
@@ -44,7 +43,7 @@ First, as a brief refresher on the concept of configurations, consider the follo
|
||||
}
|
||||
```
|
||||
|
||||
For more information on configurations, [see here](../../concepts/low_level.md#configuration).
|
||||
For more information on runtime context, [see here](../../concepts/low_level.md#runtime-context).
|
||||
|
||||
## Create an assistant
|
||||
|
||||
@@ -212,6 +211,7 @@ We have now created an assistant called "Open AI Assistant" that has `model_name
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
Receiving event of type: metadata
|
||||
{'run_id': '1ef6746e-5893-67b1-978a-0f1cd4060e16'}
|
||||
|
||||
@@ -219,6 +219,7 @@ Output:
|
||||
|
||||
Receiving event of type: updates
|
||||
{'agent': {'messages': [{'content': 'I was created by OpenAI, a research organization focused on developing and advancing artificial intelligence technology.', 'additional_kwargs': {}, 'response_metadata': {'finish_reason': 'stop', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_157b3831f5'}, 'type': 'ai', 'name': None, 'id': 'run-e1a6b25c-8416-41f2-9981-f9cfe043f414', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': None}]}}
|
||||
```
|
||||
|
||||
### LangGraph Platform UI
|
||||
|
||||
@@ -231,9 +232,11 @@ Inside your deployment, select the "Assistants" tab. For the assistant you would
|
||||
To edit the assistant, use the `update` method. This will create a new version of the assistant with the provided edits. See the [Python](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/#langgraph_sdk.client.AssistantsClient.update) and [JS](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/js_ts_sdk_ref/#update) SDK reference docs for more information.
|
||||
|
||||
!!! note "Note"
|
||||
You must pass in the ENTIRE config (and metadata if you are using it). The update endpoint creates new versions completely from scratch and does not rely on previous versions.
|
||||
|
||||
You must pass in the ENTIRE config (and metadata if you are using it). The update endpoint creates new versions completely from scratch and does not rely on previous versions.
|
||||
|
||||
For example, to update your assistant's system prompt:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
|
||||
@@ -30,17 +30,33 @@ export default {
|
||||
|
||||
Next, define your UI components in your `langgraph.json` configuration:
|
||||
|
||||
```json
|
||||
{
|
||||
"node_version": "20",
|
||||
"graphs": {
|
||||
"agent": "./src/agent/index.ts:graph"
|
||||
},
|
||||
"ui": {
|
||||
"agent": "./src/agent/ui.tsx"
|
||||
}
|
||||
}
|
||||
```
|
||||
=== "Python agent"
|
||||
|
||||
```json title="langgraph.json"
|
||||
{
|
||||
"node_version": "20",
|
||||
"graphs": {
|
||||
"agent": "./src/agent.py:graph"
|
||||
},
|
||||
"ui": {
|
||||
"agent": "./src/agent/ui.tsx"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== "JS agent"
|
||||
|
||||
```json title="langgraph.json"
|
||||
{
|
||||
"node_version": "20",
|
||||
"graphs": {
|
||||
"agent": "./src/agent/index.ts:graph"
|
||||
},
|
||||
"ui": {
|
||||
"agent": "./src/agent/ui.tsx"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `ui` section points to the UI components that will be used by graphs. By default, we recommend using the same key as the graph name, but you can split out the components however you like, see [Customise the namespace of UI components](#customise-the-namespace-of-ui-components) for more details.
|
||||
|
||||
|
||||
@@ -1,184 +0,0 @@
|
||||
# Breakpoints
|
||||
|
||||
[Breakpoints](../../concepts/breakpoints.md) pause graph execution at defined points and let you step through each stage. They use LangGraph's [**persistence layer**](../../concepts/persistence.md), which saves the graph state after each step.
|
||||
|
||||
With breakpoints, you can inspect the graph's state and node inputs at any point. Execution pauses **indefinitely** until you resume, as the checkpointer preserves the state.
|
||||
|
||||
## Set breakpoints
|
||||
|
||||
=== "Compile time"
|
||||
|
||||
```python
|
||||
# highlight-next-line
|
||||
graph = graph_builder.compile( # (1)!
|
||||
# highlight-next-line
|
||||
interrupt_before=["node_a"], # (2)!
|
||||
# highlight-next-line
|
||||
interrupt_after=["node_b", "node_c"], # (3)!
|
||||
)
|
||||
```
|
||||
|
||||
1. The breakpoints are set during `compile` time.
|
||||
2. `interrupt_before` specifies the nodes where execution should pause before the node is executed.
|
||||
3. `interrupt_after` specifies the nodes where execution should pause after the node is executed.
|
||||
|
||||
=== "Run time"
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
# highlight-next-line
|
||||
await client.runs.wait( # (1)!
|
||||
thread_id,
|
||||
assistant_id,
|
||||
inputs=inputs,
|
||||
# highlight-next-line
|
||||
interrupt_before=["node_a"], # (2)!
|
||||
# highlight-next-line
|
||||
interrupt_after=["node_b", "node_c"] # (3)!
|
||||
)
|
||||
```
|
||||
|
||||
1. `client.runs.wait` is called with the `interrupt_before` and `interrupt_after` parameters. This is a run-time configuration and can be changed for every invocation.
|
||||
2. `interrupt_before` specifies the nodes where execution should pause before the node is executed.
|
||||
3. `interrupt_after` specifies the nodes where execution should pause after the node is executed.
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
// highlight-next-line
|
||||
await client.runs.wait( // (1)!
|
||||
threadID,
|
||||
assistantID,
|
||||
{
|
||||
input: input,
|
||||
// highlight-next-line
|
||||
interruptBefore: ["node_a"], // (2)!
|
||||
// highlight-next-line
|
||||
interruptAfter: ["node_b", "node_c"] // (3)!
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
1. `client.runs.wait` is called with the `interruptBefore` and `interruptAfter` parameters. This is a run-time configuration and can be changed for every invocation.
|
||||
2. `interruptBefore` specifies the nodes where execution should pause before the node is executed.
|
||||
3. `interruptAfter` specifies the nodes where execution should pause after the node is executed.
|
||||
|
||||
=== "cURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/wait \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"interrupt_before\": [\"node_a\"],
|
||||
\"interrupt_after\": [\"node_b\", \"node_c\"],
|
||||
\"input\": <INPUT>
|
||||
}"
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
||||
This example shows how to add **static** breakpoints. See [this guide](../../how-tos/human_in_the_loop/breakpoints.ipynb) for more options for how to add breakpoints.
|
||||
|
||||
|
||||
=== "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 a thread
|
||||
thread = await client.threads.create()
|
||||
thread_id = thread["thread_id"]
|
||||
|
||||
# Run the graph until the breakpoint
|
||||
result = await client.runs.wait(
|
||||
thread_id,
|
||||
assistant_id,
|
||||
input=inputs # (1)!
|
||||
)
|
||||
|
||||
# Resume the graph
|
||||
await client.runs.wait(
|
||||
thread_id,
|
||||
assistant_id,
|
||||
input=None # (2)!
|
||||
)
|
||||
```
|
||||
|
||||
1. The graph is run until the first breakpoint is hit.
|
||||
2. The graph is resumed by passing in `None` for the input. This will run the graph until the next breakpoint is hit.
|
||||
|
||||
=== "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 a thread
|
||||
const thread = await client.threads.create();
|
||||
const threadID = thread["thread_id"];
|
||||
|
||||
// Run the graph until the breakpoint
|
||||
const result = await client.runs.wait(
|
||||
threadID,
|
||||
assistantID,
|
||||
{ input: input } // (1)!
|
||||
);
|
||||
|
||||
// Resume the graph
|
||||
await client.runs.wait(
|
||||
threadID,
|
||||
assistantID,
|
||||
{ input: null } // (2)!
|
||||
);
|
||||
```
|
||||
|
||||
1. The graph is run until the first breakpoint is hit.
|
||||
2. The graph is resumed by passing in `null` for the input. This will run the graph until the next breakpoint is hit.
|
||||
|
||||
=== "cURL"
|
||||
|
||||
Create a thread:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
Run the graph until the breakpoint:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/wait \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": <INPUT>
|
||||
}"
|
||||
```
|
||||
|
||||
Resume the graph:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/wait \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\"
|
||||
}"
|
||||
```
|
||||
|
||||
## Learn more
|
||||
|
||||
- [**LangGraph breakpoints guide**](../../how-tos/human_in_the_loop/breakpoints.ipynb): learn more about adding breakpoints in LangGraph.
|
||||
@@ -1,549 +0,0 @@
|
||||
# How to review tool calls
|
||||
|
||||
!!! tip "Prerequisites"
|
||||
|
||||
This guide assumes familiarity with the following concepts:
|
||||
|
||||
* [Tool calling](https://python.langchain.com/docs/concepts/tool_calling/)
|
||||
* [Human-in-the-loop](../../concepts/human_in_the_loop.md)
|
||||
* [LangGraph Glossary](../../concepts/low_level.md)
|
||||
|
||||
Human-in-the-loop (HIL) interactions are crucial for [agentic systems](../../concepts/agentic_concepts.md). A common pattern is to add some human in the loop step after certain tool calls. These tool calls often lead to either a function call or saving of some information. Examples include:
|
||||
|
||||
- A tool call to execute SQL, which will then be run by the tool
|
||||
- A tool call to generate a summary, which will then be saved to the State of the graph
|
||||
|
||||
Note that using tool calls is common **whether actually calling tools or not**.
|
||||
|
||||
There are typically a few different interactions you may want to do here:
|
||||
|
||||
1. Approve the tool call and continue
|
||||
2. Modify the tool call manually and then continue
|
||||
3. Give natural language feedback, and then pass that back to the agent
|
||||
|
||||
|
||||
We can implement these in LangGraph using the [`interrupt()`][langgraph.types.interrupt] function. `interrupt` allows us to stop graph execution to collect input from a user and continue execution with collected input:
|
||||
|
||||
|
||||
```python
|
||||
def human_review_node(state) -> Command[Literal["call_llm", "run_tool"]]:
|
||||
# this is the value we'll be providing via Command(resume=<human_review>)
|
||||
human_review = interrupt(
|
||||
{
|
||||
"question": "Is this correct?",
|
||||
# Surface tool calls for review
|
||||
"tool_call": tool_call
|
||||
}
|
||||
)
|
||||
|
||||
review_action, review_data = human_review
|
||||
|
||||
# Approve the tool call and continue
|
||||
if review_action == "continue":
|
||||
return Command(goto="run_tool")
|
||||
|
||||
# Modify the tool call manually and then continue
|
||||
elif review_action == "update":
|
||||
...
|
||||
updated_msg = get_updated_msg(review_data)
|
||||
return Command(goto="run_tool", update={"messages": [updated_message]})
|
||||
|
||||
# Give natural language feedback, and then pass that back to the agent
|
||||
elif review_action == "feedback":
|
||||
...
|
||||
feedback_msg = get_feedback_msg(review_data)
|
||||
return Command(goto="call_llm", update={"messages": [feedback_msg]})
|
||||
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
We are not going to show the full code for the graph we are hosting, but you can see it [here](../../how-tos/human_in_the_loop/review-tool-calls.ipynb). Once this graph is hosted, we are ready to invoke it and wait for user input.
|
||||
|
||||
### SDK initialization
|
||||
|
||||
First, we need to setup our client so that we can communicate with our hosted graph:
|
||||
|
||||
|
||||
=== "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"
|
||||
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";
|
||||
const thread = await client.threads.create();
|
||||
```
|
||||
|
||||
=== "cURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
## Example of approving tool
|
||||
|
||||
First, let's run the agent with an input that requires tool calls with approval:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
input = {"messages": [{"role": "user", "content": "what's the weather in sf?"}]}
|
||||
|
||||
async for chunk in client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistant_id,
|
||||
input=input,
|
||||
stream_mode="updates",
|
||||
):
|
||||
if chunk.data and chunk.event != "metadata":
|
||||
print(chunk.data)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
const input = { "messages": [{ "role": "user", "content": "what's the weather in sf?" }] };
|
||||
|
||||
const streamResponse = client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistantId,
|
||||
{
|
||||
input: input,
|
||||
streamMode: "updates"
|
||||
}
|
||||
);
|
||||
|
||||
for await (const chunk of streamResponse) {
|
||||
if (chunk.data && chunk.event !== "metadata") {
|
||||
console.log(chunk.data);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== "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\": \"what's the weather in sf?\"}]},
|
||||
\"stream_mode\": [
|
||||
\"updates\"
|
||||
]
|
||||
}"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
{'call_llm': {'messages': [{'content': [{'text': "I'll help you check the weather in San Francisco.", 'type': 'text'}, {'id': 'toolu_01142G3woscA8JjFTLdqymtn', 'input': {'city': 'San Francisco'}, 'name': 'weather_search', 'type': 'tool_use'}], 'additional_kwargs': {}, 'response_metadata': {'id': 'msg_01Tdfufy4nZYXMbVZvgyNbhc', 'model': 'claude-3-5-sonnet-20241022', 'stop_reason': 'tool_use', 'stop_sequence': None, 'usage': {'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 0, 'input_tokens': 379, 'output_tokens': 66}, 'model_name': 'claude-3-5-sonnet-20241022'}, 'type': 'ai', 'name': None, 'id': 'run-a33434b2-f5ca-40c6-98e2-6288d349d4ce-0', 'example': False, 'tool_calls': [{'name': 'weather_search', 'args': {'city': 'San Francisco'}, 'id': 'toolu_01142G3woscA8JjFTLdqymtn', 'type': 'tool_call'}], 'invalid_tool_calls': [], 'usage_metadata': {'input_tokens': 379, 'output_tokens': 66, 'total_tokens': 445, 'input_token_details': {'cache_read': 0, 'cache_creation': 0}}}]}}
|
||||
{'__interrupt__': [{'value': {'question': 'Is this correct?', 'tool_call': {'name': 'weather_search', 'args': {'city': 'San Francisco'}, 'id': 'toolu_01142G3woscA8JjFTLdqymtn', 'type': 'tool_call'}}, 'resumable': True, 'ns': ['human_review_node:9caf42cf-1371-7213-a331-e6fe5d026be8'], 'when': 'during'}]}
|
||||
|
||||
To approve the tool call, we need to let `human_review_node` know what value to use for the `human_review` variable we defined inside the node. We can provide this value by invoking the graph with a `Command(resume=<human_review>)` input. Since we're approving the tool call, we'll provide `resume` value of `{"action": "continue"}` to navigate to `run_tool` node:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
# highlight-next-line
|
||||
from langgraph_sdk.schema import Command
|
||||
|
||||
async for chunk in client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistant_id,
|
||||
# highlight-next-line
|
||||
command=Command(resume={"action": "continue"}),
|
||||
stream_mode="updates",
|
||||
):
|
||||
if chunk.data and chunk.event != "metadata":
|
||||
print(chunk.data)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
const streamResponse = client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistantId,
|
||||
{
|
||||
// highlight-next-line
|
||||
command: { resume: { "action": "continue" } },
|
||||
streamMode: "updates"
|
||||
}
|
||||
);
|
||||
|
||||
for await (const chunk of streamResponse) {
|
||||
if (chunk.data && chunk.event !== "metadata") {
|
||||
console.log(chunk.data);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== "cURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"command\": {
|
||||
\"resume\": { \"action\": \"continue\"}
|
||||
},
|
||||
\"stream_mode\": [
|
||||
\"updates\"
|
||||
]
|
||||
}"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
{'human_review_node': None}
|
||||
{'run_tool': {'messages': [{'role': 'tool', 'name': 'weather_search', 'content': 'Sunny!', 'tool_call_id': 'toolu_01142G3woscA8JjFTLdqymtn'}]}}
|
||||
{'call_llm': {'messages': [{'content': "According to the search, it's sunny in San Francisco right now!", 'additional_kwargs': {}, 'response_metadata': {'id': 'msg_01JJE9AtT4a9Lob91RRiW9rU', 'model': 'claude-3-5-sonnet-20241022', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 0, 'input_tokens': 458, 'output_tokens': 18}, 'model_name': 'claude-3-5-sonnet-20241022'}, 'type': 'ai', 'name': None, 'id': 'run-5e8d80b5-c46a-4aad-af37-b01f8bb15963-0', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': {'input_tokens': 458, 'output_tokens': 18, 'total_tokens': 476, 'input_token_details': {'cache_read': 0, 'cache_creation': 0}}}]}}
|
||||
|
||||
## Edit Tool Call
|
||||
|
||||
Let's now say we want to edit the tool call. E.g. change some of the parameters (or even the tool called!) but then execute that tool.
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
input = {"messages": [{"role": "user", "content": "what's the weather in sf?"}]}
|
||||
|
||||
async for chunk in client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistant_id,
|
||||
input=input,
|
||||
stream_mode="updates",
|
||||
):
|
||||
if chunk.data and chunk.event != "metadata":
|
||||
print(chunk.data)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
const input = { "messages": [{ "role": "user", "content": "what's the weather in sf?" }] };
|
||||
|
||||
const streamResponse = client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistantId,
|
||||
{
|
||||
input: input,
|
||||
streamMode: "updates",
|
||||
}
|
||||
);
|
||||
|
||||
for await (const chunk of streamResponse) {
|
||||
if (chunk.data && chunk.event !== "metadata") {
|
||||
console.log(chunk.data);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== "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\": \"what's the weather in sf?\"}]},
|
||||
\"stream_mode\": [
|
||||
\"updates\"
|
||||
]
|
||||
}"
|
||||
```
|
||||
|
||||
To do this, we will use `Command` with a different resume value of `{"action": "update", "data": <tool call args>}`. This will do the following:
|
||||
|
||||
* combine existing tool call with user-provided tool call arguments and update the existing AI message with the new tool call
|
||||
* navigate to `run_tool` node with the updated AI message and continue execution
|
||||
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
# highlight-next-line
|
||||
from langgraph_sdk.schema import Command
|
||||
|
||||
async for chunk in client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistant_id,
|
||||
# highlight-next-line
|
||||
command=Command(
|
||||
# highlight-next-line
|
||||
resume={"action": "update", "data": {"city": "San Francisco, USA"}}
|
||||
# highlight-next-line
|
||||
),
|
||||
stream_mode="updates",
|
||||
):
|
||||
if chunk.data and chunk.event != "metadata":
|
||||
print(chunk.data)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
const streamResponse = client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistantId,
|
||||
{
|
||||
// highlight-next-line
|
||||
command: {
|
||||
// highlight-next-line
|
||||
resume: { "action": "update", "data": { "city": "San Francisco, USA" } }
|
||||
// highlight-next-line
|
||||
},
|
||||
streamMode: "updates"
|
||||
}
|
||||
);
|
||||
|
||||
for await (const chunk of streamResponse) {
|
||||
if (chunk.data && chunk.event !== "metadata") {
|
||||
console.log(chunk.data);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== "cURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"command\": {
|
||||
\"resume\": { \"action\": \"update\", \"data\": { \"city\": \"San Francisco, USA\" } }
|
||||
},
|
||||
\"stream_mode\": [
|
||||
\"updates\"
|
||||
]
|
||||
}"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
{'human_review_node': {'messages': [{'role': 'ai', 'content': [{'text': "I'll help you check the weather in San Francisco.", 'type': 'text'}, {'id': 'toolu_016L4EDPcaQRzzZxiB4Wq2wa', 'input': {'city': 'San Francisco'}, 'name': 'weather_search', 'type': 'tool_use'}], 'tool_calls': [{'id': 'toolu_016L4EDPcaQRzzZxiB4Wq2wa', 'name': 'weather_search', 'args': {'city': 'San Francisco, USA'}}], 'id': 'run-b07f0c35-4e93-43a5-9b48-363767ada3ca-0'}]}}
|
||||
{'run_tool': {'messages': [{'role': 'tool', 'name': 'weather_search', 'content': 'Sunny!', 'tool_call_id': 'toolu_016L4EDPcaQRzzZxiB4Wq2wa'}]}}
|
||||
{'call_llm': {'messages': [{'content': "According to the search, it's sunny in San Francisco right now!", 'additional_kwargs': {}, 'response_metadata': {'id': 'msg_01De5HurjNUMwMUpfRtMLbX1', 'model': 'claude-3-5-sonnet-20241022', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 0, 'input_tokens': 460, 'output_tokens': 18}, 'model_name': 'claude-3-5-sonnet-20241022'}, 'type': 'ai', 'name': None, 'id': 'run-85e2aaaa-6f61-4fa0-b594-b6e57129d7e7-0', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': {'input_tokens': 460, 'output_tokens': 18, 'total_tokens': 478, 'input_token_details': {'cache_read': 0, 'cache_creation': 0}}}]}}
|
||||
|
||||
## Give feedback to a tool call
|
||||
|
||||
Sometimes, you may not want to execute a tool call, but you also may not want to ask the user to manually modify the tool call. In that case it may be better to get natural language feedback from the user. You can then insert this feedback as a mock **RESULT** of the tool call.
|
||||
|
||||
There are multiple ways to do this:
|
||||
|
||||
1. You could add a new message to the state (representing the "result" of a tool call)
|
||||
2. You could add TWO new messages to the state - one representing an "error" from the tool call, other HumanMessage representing the feedback
|
||||
|
||||
Both are similar in that they involve adding messages to the state. The main difference lies in the logic AFTER the `human_review_node` and how it handles different types of messages.
|
||||
|
||||
For this example we will just add a single tool call representing the feedback (see `human_review_node` implementation). Let's see this in action!
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
input = {"messages": [{"role": "user", "content": "what's the weather in sf?"}]}
|
||||
|
||||
async for chunk in client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistant_id,
|
||||
input=input,
|
||||
stream_mode="updates",
|
||||
):
|
||||
if chunk.data and chunk.event != "metadata":
|
||||
print(chunk.data)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
const input = { "messages": [{ "role": "user", "content": "what's the weather in sf?" }] };
|
||||
|
||||
const streamResponse = client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistantId,
|
||||
{
|
||||
input: input,
|
||||
streamMode: "updates"
|
||||
}
|
||||
);
|
||||
|
||||
for await (const chunk of streamResponse) {
|
||||
if (chunk.data && chunk.event !== "metadata") {
|
||||
console.log(chunk.data);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== "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\": \"what's the weather in sf?\"}]},
|
||||
\"stream_mode\": [
|
||||
\"updates\"
|
||||
]
|
||||
}"
|
||||
```
|
||||
|
||||
To do this, we will use `Command` with a different resume value of `{"action": "feedback", "data": <feedback string>}`. This will do the following:
|
||||
|
||||
* create a new tool message that combines existing tool call from LLM with the with user-provided feedback as content
|
||||
* navigate to `call_llm` node with the updated tool message and continue execution
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
# highlight-next-line
|
||||
from langgraph_sdk.schema import Command
|
||||
|
||||
async for chunk in client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistant_id,
|
||||
# highlight-next-line
|
||||
command=Command(
|
||||
resume={
|
||||
"action": "feedback",
|
||||
"data": "User requested changes: use <city, country> format for location"
|
||||
}
|
||||
),
|
||||
stream_mode="updates",
|
||||
):
|
||||
if chunk.data and chunk.event != "metadata":
|
||||
print(chunk.data)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
const streamResponse = client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistantId,
|
||||
{
|
||||
// highlight-next-line
|
||||
command: {
|
||||
resume: {
|
||||
"action": "feedback",
|
||||
"data": "User requested changes: use <city, country> format for location"
|
||||
}
|
||||
},
|
||||
streamMode: "updates"
|
||||
}
|
||||
);
|
||||
|
||||
for await (const chunk of streamResponse) {
|
||||
if (chunk.data && chunk.event !== "metadata") {
|
||||
console.log(chunk.data);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== "cURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"command\": {
|
||||
\"resume\": { \"action\": \"feedback\", \"data\": \"User requested changes: use <city, country> format for location\" }
|
||||
},
|
||||
\"stream_mode\": [
|
||||
\"updates\"
|
||||
]
|
||||
}"
|
||||
```
|
||||
|
||||
|
||||
Output:
|
||||
|
||||
{'human_review_node': {'messages': [{'role': 'tool', 'content': 'User requested changes: use <city, country> format for location', 'name': 'weather_search', 'tool_call_id': 'toolu_01RkPHCjpfoUvPAktaq4Cqhm'}]}}
|
||||
{'call_llm': {'messages': [{'content': [{'text': 'Let me try that again with the correct format:', 'type': 'text'}, {'id': 'toolu_01Rdrag6cVufHZG26BwVaiE7', 'input': {'city': 'San Francisco, USA'}, 'name': 'weather_search', 'type': 'tool_use'}], 'additional_kwargs': {}, 'response_metadata': {'id': 'msg_01EBan969yY5f6iGk6sPgKcj', 'model': 'claude-3-5-sonnet-20241022', 'stop_reason': 'tool_use', 'stop_sequence': None, 'usage': {'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 0, 'input_tokens': 469, 'output_tokens': 68}, 'model_name': 'claude-3-5-sonnet-20241022'}, 'type': 'ai', 'name': None, 'id': 'run-64bbc255-d126-4db0-8ae5-3197cf29bed1-0', 'example': False, 'tool_calls': [{'name': 'weather_search', 'args': {'city': 'San Francisco, USA'}, 'id': 'toolu_01Rdrag6cVufHZG26BwVaiE7', 'type': 'tool_call'}], 'invalid_tool_calls': [], 'usage_metadata': {'input_tokens': 469, 'output_tokens': 68, 'total_tokens': 537, 'input_token_details': {'cache_read': 0, 'cache_creation': 0}}}]}}
|
||||
{'__interrupt__': [{'value': {'question': 'Is this correct?', 'tool_call': {'name': 'weather_search', 'args': {'city': 'San Francisco, USA'}, 'id': 'toolu_01Rdrag6cVufHZG26BwVaiE7', 'type': 'tool_call'}}, 'resumable': True, 'ns': ['human_review_node:e9856878-e28c-5dd1-d353-4d83aa1a3a2b'], 'when': 'during'}]}
|
||||
|
||||
We can see that we now get to another interrupt - because it went back to the model and got an entirely new prediction of what to call. Let's now approve this one and continue.
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
# highlight-next-line
|
||||
from langgraph_sdk.schema import Command
|
||||
|
||||
async for chunk in client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistant_id,
|
||||
# highlight-next-line
|
||||
command=Command(resume={"action": "continue"}),
|
||||
stream_mode="updates",
|
||||
):
|
||||
if chunk.data and chunk.event != "metadata":
|
||||
print(chunk.data)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
const streamResponse = client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistantId,
|
||||
{
|
||||
// highlight-next-line
|
||||
command: { resume: { "action": "continue" } },
|
||||
streamMode: "updates"
|
||||
}
|
||||
);
|
||||
|
||||
for await (const chunk of streamResponse) {
|
||||
if (chunk.data && chunk.event !== "metadata") {
|
||||
console.log(chunk.data);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== "cURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"command\": {
|
||||
\"resume\": { \"action\": \"continue\"}
|
||||
},
|
||||
\"stream_mode\": [
|
||||
\"updates\"
|
||||
]
|
||||
}"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
{'human_review_node': None}
|
||||
{'run_tool': {'messages': [{'role': 'tool', 'name': 'weather_search', 'content': 'Sunny!', 'tool_call_id': 'toolu_01Rdrag6cVufHZG26BwVaiE7'}]}}
|
||||
{'call_llm': {'messages': [{'content': 'The weather in San Francisco is sunny!', 'additional_kwargs': {}, 'response_metadata': {'id': 'msg_013WTDHhbg8WiYLiQ9n2CaTk', 'model': 'claude-3-5-sonnet-20241022', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 0, 'input_tokens': 550, 'output_tokens': 12}, 'model_name': 'claude-3-5-sonnet-20241022'}, 'type': 'ai', 'name': None, 'id': 'run-b6c815f0-989a-47cf-b150-33e3bbc4eab7-0', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': {'input_tokens': 550, 'output_tokens': 12, 'total_tokens': 562, 'input_token_details': {'cache_read': 0, 'cache_creation': 0}}}]}}
|
||||
@@ -1,10 +1,8 @@
|
||||
# Time travel
|
||||
# Time travel using Server API
|
||||
|
||||
LangGraph provides [**time travel**](../../concepts/time-travel.md) functionality to **resume execution from a prior checkpoint** — either replaying the same state or modifying it to explore alternatives. In all cases, resuming past execution produces a **new fork** in the history.
|
||||
LangGraph provides the [**time travel**](../../concepts/time-travel.md) functionality to resume execution from a prior checkpoint, either replaying the same state or modifying it to explore alternatives. In all cases, resuming past execution produces a new fork in the history.
|
||||
|
||||
## Use time travel
|
||||
|
||||
To use time-travel in LangGraph:
|
||||
To time travel using the LangGraph Server API (via the LangGraph SDK):
|
||||
|
||||
1. **Run the graph** with initial inputs using [LangGraph SDK](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/)'s [`client.runs.wait`][langgraph_sdk.client.RunsClient.wait] or [`client.runs.stream`][langgraph_sdk.client.RunsClient.stream] APIs.
|
||||
2. **Identify a checkpoint in an existing thread**: Use [`client.threads.get_history`][langgraph_sdk.client.ThreadsClient.get_history] method to retrieve the execution history for a specific `thread_id` and locate the desired `checkpoint_id`.
|
||||
@@ -12,7 +10,7 @@ To use time-travel in LangGraph:
|
||||
3. **(Optional) modify the graph state**: Use the [`client.threads.update_state`][langgraph_sdk.client.ThreadsClient.update_state] method to modify the graph’s state at the checkpoint and resume execution from alternative state.
|
||||
4. **Resume execution from the checkpoint**: Use the [`client.runs.wait`][langgraph_sdk.client.RunsClient.wait] or [`client.runs.stream`][langgraph_sdk.client.RunsClient.stream] APIs with an input of `None` and the appropriate `thread_id` and `checkpoint_id`.
|
||||
|
||||
## Example
|
||||
## Use time travel in a workflow
|
||||
|
||||
??? example "Example graph"
|
||||
|
||||
@@ -237,4 +235,4 @@ To use time-travel in LangGraph:
|
||||
|
||||
## Learn more
|
||||
|
||||
- [**LangGraph time travel guide**](../../how-tos/human_in_the_loop/time-travel.ipynb): learn more about using time travel in LangGraph.
|
||||
- [**LangGraph time travel guide**](../../how-tos/human_in_the_loop/time-travel.md): learn more about using time travel in LangGraph.
|
||||
@@ -247,5 +247,7 @@ Verify that the original, interrupted run was interrupted
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
'interrupted'
|
||||
```
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
!!!info "Prerequisites"
|
||||
- [Running agents](../../agents/run_agents.md#running-agents)
|
||||
|
||||
This guide shows how to submit a [run](../concepts/runs.md) to your application.
|
||||
This guide shows how to submit a [run](../../concepts/assistants.md#execution) to your application.
|
||||
|
||||
## Graph mode
|
||||
|
||||
@@ -29,11 +29,11 @@ Click the dropdown next to "Submit" and click the toggle to enable/disable strea
|
||||
To run your graph with breakpoints, click the "Interrupt" button. Select a node and whether to pause before and/or after that node has executed. Click "Continue" in the thread log to resume execution.
|
||||
|
||||
|
||||
For more information on breakpoints see [here](../../concepts/breakpoints.md).
|
||||
For more information on breakpoints see [here](../../concepts/human_in_the_loop.md).
|
||||
|
||||
### Submit run
|
||||
|
||||
To submit the run with the specified input and run settings, click the "Submit" button. This will add a [run](../concepts/runs.md) to the existing selected [thread](../concepts/threads.md). If no thread is currently selected, a new one will be created.
|
||||
To submit the run with the specified input and run settings, click the "Submit" button. This will add a [run](../../concepts/assistants.md#execution) to the existing selected [thread](../../concepts/persistence.md#threads). If no thread is currently selected, a new one will be created.
|
||||
|
||||
To cancel the ongoing run, click the "Cancel" button.
|
||||
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
# Stream outputs
|
||||
# Streaming API
|
||||
|
||||
## Streaming API
|
||||
[LangGraph SDK](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/) allows you to [stream outputs](../../concepts/streaming.md) from the LangGraph API server.
|
||||
|
||||
[LangGraph SDK](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/) allows you to stream outputs from the LangGraph API server.
|
||||
!!! note
|
||||
|
||||
LangGraph SDK and LangGraph Server are a part of [LangGraph Platform](../../concepts/langgraph_platform.md).
|
||||
|
||||
## Basic usage
|
||||
|
||||
Basic usage example:
|
||||
|
||||
@@ -833,3 +837,121 @@ To stream all events, including the state of the graph:
|
||||
\"stream_mode\": \"events\"
|
||||
}"
|
||||
```
|
||||
|
||||
## Stateless runs
|
||||
|
||||
If you don't want to **persist the outputs** of a streaming run in the [checkpointer](../../concepts/persistence.md) DB, you can create a stateless run without creating a thread:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
client = get_client(url=<DEPLOYMENT_URL>, api_key=<API_KEY>)
|
||||
|
||||
async for chunk in client.runs.stream(
|
||||
# highlight-next-line
|
||||
None, # (1)!
|
||||
assistant_id,
|
||||
input=inputs,
|
||||
stream_mode="updates"
|
||||
):
|
||||
print(chunk.data)
|
||||
```
|
||||
|
||||
1. We are passing `None` instead of a `thread_id` UUID.
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL>, apiKey: <API_KEY> });
|
||||
|
||||
// create a streaming run
|
||||
// highlight-next-line
|
||||
const streamResponse = client.runs.stream(
|
||||
// highlight-next-line
|
||||
null, // (1)!
|
||||
assistantID,
|
||||
{
|
||||
input,
|
||||
streamMode: "updates"
|
||||
}
|
||||
);
|
||||
for await (const chunk of streamResponse) {
|
||||
console.log(chunk.data);
|
||||
}
|
||||
```
|
||||
|
||||
1. We are passing `None` instead of a `thread_id` UUID.
|
||||
|
||||
=== "cURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'x-api-key: <API_KEY>'
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": <inputs>,
|
||||
\"stream_mode\": \"updates\"
|
||||
}"
|
||||
```
|
||||
|
||||
## Join and stream
|
||||
|
||||
LangGraph Platform allows you to join an active [background run](../how-tos/background_run.md) and stream outputs from it. To do so, you can use [LangGraph SDK's](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/) `client.runs.join_stream` method:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
client = get_client(url=<DEPLOYMENT_URL>, api_key=<API_KEY>)
|
||||
|
||||
# highlight-next-line
|
||||
async for chunk in client.runs.join_stream(
|
||||
thread_id,
|
||||
# highlight-next-line
|
||||
run_id, # (1)!
|
||||
):
|
||||
print(chunk)
|
||||
```
|
||||
|
||||
1. This is the `run_id` of an existing run you want to join.
|
||||
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL>, apiKey: <API_KEY> });
|
||||
|
||||
// highlight-next-line
|
||||
const streamResponse = client.runs.joinStream(
|
||||
threadID,
|
||||
// highlight-next-line
|
||||
runId // (1)!
|
||||
);
|
||||
for await (const chunk of streamResponse) {
|
||||
console.log(chunk);
|
||||
}
|
||||
```
|
||||
|
||||
1. This is the `run_id` of an existing run you want to join.
|
||||
|
||||
=== "cURL"
|
||||
|
||||
```bash
|
||||
curl --request GET \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/<RUN_ID>/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'x-api-key: <API_KEY>'
|
||||
```
|
||||
|
||||
!!! warning "Outputs not buffered"
|
||||
|
||||
When you use `.join_stream`, output is not buffered, so any output produced before joining will not be received.
|
||||
|
||||
## API Reference
|
||||
|
||||
For API usage and implementation, refer to the [API reference](../reference/api/api_ref.html#tag/thread-runs/POST/threads/{thread_id}/runs/stream).
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
!!! info "Prerequisites"
|
||||
|
||||
- [Assistants Overview](../../concepts/assistants.md)
|
||||
- [Assistants Overview](../../../concepts/assistants.md)
|
||||
|
||||
LangGraph Studio lets you view, edit, and update your assistants, and allows you to run your graph using these assistant configurations.
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ LangGraph Studio is accessed from the LangSmith UI, within the LangGraph Platfor
|
||||
|
||||
For applications that are [deployed](../../quick_start.md) on LangGraph Platform, you can access Studio as part of that deployment. To do so, navigate to the deployment in LangGraph Platform within the LangSmith UI and click the "LangGraph Studio" button.
|
||||
|
||||
This will load the Studio UI connected to your live deployment, allowing you to create, read, and update the [threads](../../concepts/threads.md), [assistants](../../../concepts/assistants.md), and [memory](../../../concepts//memory.md) in that deployment.
|
||||
This will load the Studio UI connected to your live deployment, allowing you to create, read, and update the [threads](../../../concepts/persistence.md#threads), [assistants](../../../concepts/assistants.md), and [memory](../../../concepts//memory.md) in that deployment.
|
||||
|
||||
## Local development server
|
||||
|
||||
@@ -73,9 +73,11 @@ langgraph dev --debug-port 5678
|
||||
Then attach your preferred debugger:
|
||||
|
||||
=== "VS Code"
|
||||
Add this configuration to `launch.json`:
|
||||
`json
|
||||
{
|
||||
|
||||
Add this configuration to `launch.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Attach to LangGraph",
|
||||
"type": "debugpy",
|
||||
"request": "attach",
|
||||
@@ -83,11 +85,16 @@ Add this configuration to `launch.json`:
|
||||
"host": "0.0.0.0",
|
||||
"port": 5678
|
||||
}
|
||||
}
|
||||
`
|
||||
Specify the port number you chose in the previous step.
|
||||
}
|
||||
```
|
||||
|
||||
=== "PyCharm" 1. Go to Run → Edit Configurations 2. Click + and select "Python Debug Server" 3. Set IDE host name: `localhost` 4. Set port: `5678` (or the port number you chose in the previous step) 5. Click "OK" and start debugging
|
||||
=== "PyCharm"
|
||||
|
||||
1. Go to Run → Edit Configurations
|
||||
2. Click + and select "Python Debug Server"
|
||||
3. Set IDE host name: `localhost`
|
||||
4. Set port: `5678` (or the port number you chose in the previous step)
|
||||
5. Click "OK" and start debugging
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
# Run experiments over a dataset
|
||||
|
||||
LangGraph Studio supports evaluations by allowing you to run your assistant over a pre-defined LangSmith dataset. This enables you to understand how your application performs over a variety of inputs, compare the results to reference outputs, and score the results using [evaluators](../../../agents/evals.md).
|
||||
|
||||
This guide shows you how to run an experiment end-to-end from Studio.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before running an experiment, ensure you have the following:
|
||||
|
||||
1. **A LangSmith dataset**: Your dataset should contain the inputs you want to test and optionally, reference outputs for comparison.
|
||||
|
||||
- The schema for the inputs must match the required input schema for the assistant. For more information on schemas, see [here](../../../concepts/low_level.md#schema).
|
||||
- For more on creating datasets, see [How to Manage Datasets](https://docs.smith.langchain.com/evaluation/how_to_guides/manage_datasets_in_application#set-up-your-dataset).
|
||||
|
||||
2. **(Optional) Evaluators**: You can attach evaluators (e.g., LLM-as-a-Judge, heuristics, or custom functions) to your dataset in LangSmith. These will run automatically after the graph has processed all inputs.
|
||||
|
||||
- To learn more, read about [Evaluation Concepts](https://docs.smith.langchain.com/evaluation/concepts#evaluators).
|
||||
|
||||
3. **A running application**: The experiment can be run against:
|
||||
- An application deployed on [LangGraph Platform](../../quick_start.md).
|
||||
- A locally running application started via the [langgraph-cli](../../../tutorials/langgraph-platform/local-server.md).
|
||||
|
||||
---
|
||||
|
||||
## Step-by-step guide
|
||||
|
||||
### 1. Launch the experiment
|
||||
|
||||
Click the **Run experiment** button in the top right corner of the Studio page.
|
||||
|
||||
### 2. Select your dataset
|
||||
|
||||
In the modal that appears, select the dataset (or a specific dataset split) to use for the experiment and click **Start**.
|
||||
|
||||
### 3. Monitor the progress
|
||||
|
||||
All of the inputs in the dataset will now be run against the active assistant. Monitor the experiment's progress via the badge in the top right corner.
|
||||
|
||||
You can continue to work in Studio while the experiment runs in the background. Click the arrow icon button at any time to navigate to LangSmith and view the detailed experiment results.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Run experiment" button is disabled
|
||||
|
||||
If the "Run experiment" button is disabled, check the following:
|
||||
|
||||
- **Deployed application**: If your application is deployed on LangGraph Platform, you may need to create a new revision to enable this feature.
|
||||
- **Local development server**: If you are running your application locally, make sure you have upgraded to the latest version of the `langgraph-cli` (`pip install -U langgraph-cli`). Additionally, ensure you have tracing enabled by setting the `LANGSMITH_API_KEY` in your project's `.env` file.
|
||||
|
||||
### Evaluator results are missing
|
||||
|
||||
When you run an experiment, any attached evaluators are scheduled for execution in a queue. If you don't see results immediately, it likely means they are still pending.
|
||||
@@ -1,10 +1,6 @@
|
||||
# Manage threads
|
||||
|
||||
!!! info "Prerequisites"
|
||||
|
||||
- [Threads Overview](../concepts/threads.md)
|
||||
|
||||
Studio allows you to view threads from the server and edit their state.
|
||||
Studio allows you to view [threads](../../concepts/persistence.md#threads) from the server and edit their state.
|
||||
|
||||
## View threads
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
How to integrate LangGraph into your React application# How to integrate LangGraph into your React application
|
||||
# How to integrate LangGraph into your React application
|
||||
|
||||
!!! info "Prerequisites"
|
||||
|
||||
@@ -503,6 +503,74 @@ const handleSubmit = (text: string) => {
|
||||
};
|
||||
```
|
||||
|
||||
### Cached Thread Display
|
||||
|
||||
Use the `initialValues` option to display cached thread data immediately while the history is being loaded from the server. This improves user experience by showing cached data instantly when navigating to existing threads.
|
||||
|
||||
```tsx
|
||||
import { useStream } from "@langchain/langgraph-sdk/react";
|
||||
|
||||
const CachedThreadExample = ({ threadId, cachedThreadData }) => {
|
||||
const stream = useStream({
|
||||
apiUrl: "http://localhost:2024",
|
||||
assistantId: "agent",
|
||||
threadId,
|
||||
// Show cached data immediately while history loads
|
||||
initialValues: cachedThreadData?.values,
|
||||
messagesKey: "messages",
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
{stream.messages.map((message) => (
|
||||
<div key={message.id}>{message.content as string}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Optimistic Thread Creation
|
||||
|
||||
Use the `threadId` option in `submit` function to enable optimistic UI patterns where you need to know the thread ID before the thread is actually created.
|
||||
|
||||
```tsx
|
||||
import { useState } from "react";
|
||||
import { useStream } from "@langchain/langgraph-sdk/react";
|
||||
|
||||
const OptimisticThreadExample = () => {
|
||||
const [threadId, setThreadId] = useState<string | null>(null);
|
||||
const [optimisticThreadId] = useState(() => crypto.randomUUID());
|
||||
|
||||
const stream = useStream({
|
||||
apiUrl: "http://localhost:2024",
|
||||
assistantId: "agent",
|
||||
threadId,
|
||||
onThreadId: setThreadId, // (3) Updated after thread has been created.
|
||||
messagesKey: "messages",
|
||||
});
|
||||
|
||||
const handleSubmit = (text: string) => {
|
||||
// (1) Perform a soft navigation to /threads/${optimisticThreadId}
|
||||
// without waiting for thread creation.
|
||||
window.history.pushState({}, "", `/threads/${optimisticThreadId}`);
|
||||
|
||||
// (2) Submit message to create thread with the predetermined ID.
|
||||
stream.submit(
|
||||
{ messages: [{ type: "human", content: text }] },
|
||||
{ threadId: optimisticThreadId }
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>Thread ID: {threadId ?? optimisticThreadId}</p>
|
||||
{/* Rest of component */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
|
||||
The `useStream()` hook is friendly for apps written in TypeScript and you can specify types for the state to get better type safety and IDE support.
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
# Use threads
|
||||
|
||||
!!! info "Prerequisites"
|
||||
|
||||
- [Threads Overview](../concepts/threads.md)
|
||||
|
||||
In this guide, we will show how to create, view, and inspect threads.
|
||||
In this guide, we will show how to create, view, and inspect [threads](../../concepts/persistence.md#threads).
|
||||
|
||||
## Create a thread
|
||||
|
||||
|
||||
@@ -8,15 +8,15 @@ Currently, the SDK does not provide built-in support for defining webhook endpoi
|
||||
|
||||
The following API endpoints accept a `webhook` parameter:
|
||||
|
||||
| Operation | HTTP Method | Endpoint |
|
||||
|-----------|------------|----------|
|
||||
| Create Run | `POST` | `/thread/{thread_id}/runs` |
|
||||
| Create Thread Cron | `POST` | `/thread/{thread_id}/runs/crons` |
|
||||
| Stream Run | `POST` | `/thread/{thread_id}/runs/stream` |
|
||||
| Wait Run | `POST` | `/thread/{thread_id}/runs/wait` |
|
||||
| Create Cron | `POST` | `/runs/crons` |
|
||||
| Stream Run Stateless | `POST` | `/runs/stream` |
|
||||
| Wait Run Stateless | `POST` | `/runs/wait` |
|
||||
| Operation | HTTP Method | Endpoint |
|
||||
|----------------------|-------------|-----------------------------------|
|
||||
| Create Run | `POST` | `/thread/{thread_id}/runs` |
|
||||
| Create Thread Cron | `POST` | `/thread/{thread_id}/runs/crons` |
|
||||
| Stream Run | `POST` | `/thread/{thread_id}/runs/stream` |
|
||||
| Wait Run | `POST` | `/thread/{thread_id}/runs/wait` |
|
||||
| Create Cron | `POST` | `/runs/crons` |
|
||||
| Stream Run Stateless | `POST` | `/runs/stream` |
|
||||
| Wait Run Stateless | `POST` | `/runs/wait` |
|
||||
|
||||
In this guide, we’ll show how to trigger a webhook after streaming a run.
|
||||
|
||||
@@ -25,36 +25,39 @@ In this guide, we’ll show how to trigger a webhook after streaming a run.
|
||||
Before making API calls, set up your assistant and thread.
|
||||
|
||||
=== "Python"
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
|
||||
client = get_client(url=<DEPLOYMENT_URL>)
|
||||
assistant_id = "agent"
|
||||
thread = await client.threads.create()
|
||||
print(thread)
|
||||
```
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
|
||||
client = get_client(url=<DEPLOYMENT_URL>)
|
||||
assistant_id = "agent"
|
||||
thread = await client.threads.create()
|
||||
print(thread)
|
||||
```
|
||||
|
||||
=== "JavaScript"
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL> });
|
||||
const assistantID = "agent";
|
||||
const thread = await client.threads.create();
|
||||
console.log(thread);
|
||||
```
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL> });
|
||||
const assistantID = "agent";
|
||||
const thread = await client.threads.create();
|
||||
console.log(thread);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/assistants/search \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{ "limit": 10, "offset": 0 }' | jq -c 'map(select(.config == null or .config == {})) | .[0]' && \
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/assistants/search \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{ "limit": 10, "offset": 0 }' | jq -c 'map(select(.config == null or .config == {})) | .[0]' && \
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
@@ -77,52 +80,55 @@ To use a webhook, specify the `webhook` parameter in your API request. When the
|
||||
For example, if your server listens for webhook events at `https://my-server.app/my-webhook-endpoint`, include this in your request:
|
||||
|
||||
=== "Python"
|
||||
```python
|
||||
input = { "messages": [{ "role": "user", "content": "Hello!" }] }
|
||||
|
||||
async for chunk in client.runs.stream(
|
||||
thread_id=thread["thread_id"],
|
||||
assistant_id=assistant_id,
|
||||
input=input,
|
||||
stream_mode="events",
|
||||
webhook="https://my-server.app/my-webhook-endpoint"
|
||||
):
|
||||
pass
|
||||
```
|
||||
```python
|
||||
input = { "messages": [{ "role": "user", "content": "Hello!" }] }
|
||||
|
||||
async for chunk in client.runs.stream(
|
||||
thread_id=thread["thread_id"],
|
||||
assistant_id=assistant_id,
|
||||
input=input,
|
||||
stream_mode="events",
|
||||
webhook="https://my-server.app/my-webhook-endpoint"
|
||||
):
|
||||
pass
|
||||
```
|
||||
|
||||
=== "JavaScript"
|
||||
```js
|
||||
const input = { messages: [{ role: "human", content: "Hello!" }] };
|
||||
|
||||
const streamResponse = client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistantID,
|
||||
{
|
||||
input: input,
|
||||
webhook: "https://my-server.app/my-webhook-endpoint"
|
||||
}
|
||||
);
|
||||
```js
|
||||
const input = { messages: [{ role: "human", content: "Hello!" }] };
|
||||
|
||||
for await (const chunk of streamResponse) {
|
||||
// Handle stream output
|
||||
}
|
||||
```
|
||||
const streamResponse = client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistantID,
|
||||
{
|
||||
input: input,
|
||||
webhook: "https://my-server.app/my-webhook-endpoint"
|
||||
}
|
||||
);
|
||||
|
||||
for await (const chunk of streamResponse) {
|
||||
// Handle stream output
|
||||
}
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"assistant_id": <ASSISTANT_ID>,
|
||||
"input": {"messages": [{"role": "user", "content": "Hello!"}]},
|
||||
"webhook": "https://my-server.app/my-webhook-endpoint"
|
||||
}'
|
||||
```
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"assistant_id": <ASSISTANT_ID>,
|
||||
"input": {"messages": [{"role": "user", "content": "Hello!"}]},
|
||||
"webhook": "https://my-server.app/my-webhook-endpoint"
|
||||
}'
|
||||
```
|
||||
|
||||
## Webhook payload
|
||||
|
||||
LangGraph Platform sends webhook notifications in the format of a [Run](../../cloud/concepts/runs.md). See the [API Reference](https://langchain-ai.github.io/langgraph/cloud/reference/api/api_ref.html#model/run) for details. The request payload includes run input, configuration, and other metadata in the `kwargs` field.
|
||||
LangGraph Platform sends webhook notifications in the format of a [Run](../../concepts/assistants.md#execution). See the [API Reference](https://langchain-ai.github.io/langgraph/cloud/reference/api/api_ref.html#model/run) for details. The request payload includes run input, configuration, and other metadata in the `kwargs` field.
|
||||
|
||||
## Secure webhooks
|
||||
|
||||
@@ -134,6 +140,22 @@ https://my-server.app/my-webhook-endpoint?token=YOUR_SECRET_TOKEN
|
||||
|
||||
Your server should extract and validate this token before processing requests.
|
||||
|
||||
## Disable webhooks
|
||||
|
||||
As of `langgraph-api>=0.2.78`, developers can disable webhooks in the `langgraph.json` file:
|
||||
|
||||
```json
|
||||
{
|
||||
"http": {
|
||||
"disable_webhooks": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This feature is primarily intended for self-hosted deployments, where platform administrators or developers may prefer to disable webhooks to simplify their security posture—especially if they are not configuring firewall rules or other network controls. Disabling webhooks helps prevent untrusted payloads from being sent to internal endpoints.
|
||||
|
||||
For full configuration details, refer to the [configuration file reference](https://langchain-ai.github.io/langgraph/cloud/reference/cli/?h=disable_webhooks#configuration-file).
|
||||
|
||||
## Test webhooks
|
||||
|
||||
You can test your webhook using online services like:
|
||||
|
||||
@@ -154,8 +154,9 @@ You can now test the API:
|
||||
|
||||
```bash
|
||||
curl -s --request POST \
|
||||
--url <DEPLOYMENT_URL> \
|
||||
--url <DEPLOYMENT_URL>/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header "X-Api-Key: <LANGSMITH API KEY> \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# API Reference
|
||||
# LangGraph Server API Reference
|
||||
|
||||
The LangGraph Platform API reference is available with each deployment at the `/docs` URL path (e.g. `http://localhost:8124/docs`).
|
||||
The LangGraph Server API reference is available within each deployment at the `/docs` endpoint (e.g. `http://localhost:8124/docs`).
|
||||
|
||||
Click <a href="/langgraph/cloud/reference/api/api_ref.html" target="_blank">here</a> to view the API reference.
|
||||
|
||||
## Authentication
|
||||
|
||||
For deployments to LangGraph Platform, authentication is required. Pass the `X-Api-Key` header with each request to the LangGraph Platform API. The value of the header should be set to a valid LangSmith API key for the organization where the API is deployed.
|
||||
For deployments to LangGraph Platform, authentication is required. Pass the `X-Api-Key` header with each request to the LangGraph Server. The value of the header should be set to a valid LangSmith API key for the organization where the LangGraph Server is deployed.
|
||||
|
||||
Example `curl` command:
|
||||
```shell
|
||||
@@ -18,5 +18,5 @@ curl --request POST \
|
||||
"metadata": {},
|
||||
"limit": 10,
|
||||
"offset": 0
|
||||
}'
|
||||
}'
|
||||
```
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
# LangGraph Control Plane API Reference
|
||||
|
||||
The LangGraph Control Plane API is used to programmatically create and manage LangGraph Server deployments. For example, the APIs can be orchestrated to create custom CI/CD workflows.
|
||||
|
||||
Click <a href="https://api.host.langchain.com/docs" target="_blank">here</a> to view the API reference.
|
||||
|
||||
## Host
|
||||
|
||||
LangGraph Control Plane hosts for Cloud SaaS data regions:
|
||||
|
||||
| US | EU |
|
||||
|----|----|
|
||||
| `https://api.host.langchain.com` | `https://eu.api.host.langchain.com` |
|
||||
|
||||
**Note**: Self-hosted deployments of LangGraph Platform will have a custom host for the LangGraph Control Plane.
|
||||
|
||||
## Authentication
|
||||
|
||||
To authenticate with the LangGraph Control Plane API, set the `X-Api-Key` header to a valid LangSmith API key.
|
||||
|
||||
Example `curl` command:
|
||||
```shell
|
||||
curl --request GET \
|
||||
--url http://localhost:8124/v2/deployments \
|
||||
--header 'X-Api-Key: LANGSMITH_API_KEY'
|
||||
```
|
||||
|
||||
## Versioning
|
||||
|
||||
Each endpoint path is prefixed with a version (e.g. `v1`, `v2`).
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. Call `POST /v2/deployments` to create a new Deployment. The response body contains the Deployment ID (`id`) and the ID of the latest (and first) revision (`latest_revision_id`).
|
||||
1. Call `GET /v2/deployments/{deployment_id}` to retrieve the Deployment. Set `deployment_id` in the URL to the value of Deployment ID (`id`).
|
||||
1. Poll for revision `status` until `status` is `DEPLOYED` by calling `GET /v2/deployments/{deployment_id}/revisions/{latest_revision_id}`.
|
||||
1. Call `PATCH /v2/deployments/{deployment_id}` to update the deployment.
|
||||
|
||||
## Example Code
|
||||
Below is example Python code that demonstrates how to orchestrate the LangGraph Control Plane APIs to create a deployment, update the deployment, and delete the deployment.
|
||||
```python
|
||||
import os
|
||||
import time
|
||||
|
||||
import requests
|
||||
from dotenv import load_dotenv
|
||||
|
||||
|
||||
load_dotenv()
|
||||
|
||||
# required environment variables
|
||||
CONTROL_PLANE_HOST = os.getenv("CONTROL_PLANE_HOST")
|
||||
LANGSMITH_API_KEY = os.getenv("LANGSMITH_API_KEY")
|
||||
INTEGRATION_ID = os.getenv("INTEGRATION_ID")
|
||||
MAX_WAIT_TIME = 1800 # 30 mins
|
||||
|
||||
|
||||
def get_headers() -> dict:
|
||||
"""Return common headers for requests to LangGraph Control Plane API."""
|
||||
return {
|
||||
"X-Api-Key": LANGSMITH_API_KEY,
|
||||
}
|
||||
|
||||
|
||||
def create_deployment() -> str:
|
||||
"""Create deployment. Return deployment ID."""
|
||||
headers = get_headers()
|
||||
headers["Content-Type"] = "application/json"
|
||||
|
||||
deployment_name = "my_deployment"
|
||||
|
||||
request_body = {
|
||||
"name": deployment_name,
|
||||
"source": "github",
|
||||
"source_config": {
|
||||
"integration_id": INTEGRATION_ID,
|
||||
"repo_url": "https://github.com/langchain-ai/langgraph-example",
|
||||
"deployment_type": "dev",
|
||||
"build_on_push": False,
|
||||
"custom_url": None,
|
||||
"resource_spec": None,
|
||||
},
|
||||
"source_revision_config": {
|
||||
"repo_ref": "main",
|
||||
"langgraph_config_path": "langgraph.json",
|
||||
"image_uri": None,
|
||||
},
|
||||
"secrets": [
|
||||
{
|
||||
"name": "OPENAI_API_KEY",
|
||||
"value": "test_openai_api_key",
|
||||
},
|
||||
{
|
||||
"name": "ANTHROPIC_API_KEY",
|
||||
"value": "test_anthropic_api_key",
|
||||
},
|
||||
{
|
||||
"name": "TAVILY_API_KEY",
|
||||
"value": "test_tavily_api_key",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
url=f"{CONTROL_PLANE_HOST}/v2/deployments",
|
||||
headers=headers,
|
||||
json=request_body,
|
||||
)
|
||||
|
||||
if response.status_code != 201:
|
||||
raise Exception(f"Failed to create deployment: {response.text}")
|
||||
|
||||
deployment_id = response.json()["id"]
|
||||
print(f"Created deployment {deployment_name} ({deployment_id})")
|
||||
return deployment_id
|
||||
|
||||
|
||||
def get_deployment(deployment_id: str) -> dict:
|
||||
"""Get deployment."""
|
||||
response = requests.get(
|
||||
url=f"{CONTROL_PLANE_HOST}/v2/deployments/{deployment_id}",
|
||||
headers=get_headers(),
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"Failed to get deployment ID {deployment_id}: {response.text}")
|
||||
|
||||
return response.json()
|
||||
|
||||
|
||||
def list_revisions(deployment_id: str) -> list[dict]:
|
||||
"""List revisions.
|
||||
|
||||
Return list is sorted by created_at in descending order (latest first).
|
||||
"""
|
||||
response = requests.get(
|
||||
url=f"{CONTROL_PLANE_HOST}/v2/deployments/{deployment_id}/revisions",
|
||||
headers=get_headers(),
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise Exception(
|
||||
f"Failed to list revisions for deployment ID {deployment_id}: {response.text}"
|
||||
)
|
||||
|
||||
return response.json()
|
||||
|
||||
|
||||
def get_revision(
|
||||
deployment_id: str,
|
||||
revision_id: str,
|
||||
) -> dict:
|
||||
"""Get revision."""
|
||||
response = requests.get(
|
||||
url=f"{CONTROL_PLANE_HOST}/v2/deployments/{deployment_id}/revisions/{revision_id}",
|
||||
headers=get_headers(),
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"Failed to get revision ID {revision_id}: {response.text}")
|
||||
|
||||
return response.json()
|
||||
|
||||
|
||||
def patch_deployment(deployment_id: str) -> None:
|
||||
"""Patch deployment."""
|
||||
headers = get_headers()
|
||||
headers["Content-Type"] = "application/json"
|
||||
|
||||
response = requests.patch(
|
||||
url=f"{CONTROL_PLANE_HOST}/v2/deployments/{deployment_id}",
|
||||
headers=headers,
|
||||
json={
|
||||
"source_config": {
|
||||
"build_on_push": True,
|
||||
},
|
||||
"source_revision_config": {
|
||||
"repo_ref": "main",
|
||||
"langgraph_config_path": "langgraph.json",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"Failed to patch deployment: {response.text}")
|
||||
|
||||
print(f"Patched deployment ID {deployment_id}")
|
||||
|
||||
|
||||
def wait_for_deployment(deployment_id: str, revision_id: str) -> None:
|
||||
"""Wait for revision status to be DEPLOYED."""
|
||||
start_time = time.time()
|
||||
revision, status = None, None
|
||||
while time.time() - start_time < MAX_WAIT_TIME:
|
||||
revision = get_revision(deployment_id, revision_id)
|
||||
status = revision["status"]
|
||||
if status == "DEPLOYED":
|
||||
break
|
||||
elif "FAILED" in status:
|
||||
raise Exception(f"Revision ID {revision_id} failed: {revision}")
|
||||
|
||||
print(f"Waiting for revision ID {revision_id} to be DEPLOYED...")
|
||||
time.sleep(60)
|
||||
|
||||
if status != "DEPLOYED":
|
||||
raise Exception(
|
||||
f"Timeout waiting for revision ID {revision_id} to be DEPLOYED: {revision}"
|
||||
)
|
||||
|
||||
|
||||
def delete_deployment(deployment_id: str) -> None:
|
||||
"""Delete deployment."""
|
||||
response = requests.delete(
|
||||
url=f"{CONTROL_PLANE_HOST}/v2/deployments/{deployment_id}",
|
||||
headers=get_headers(),
|
||||
)
|
||||
|
||||
if response.status_code != 204:
|
||||
raise Exception(
|
||||
f"Failed to delete deployment ID {deployment_id}: {response.text}"
|
||||
)
|
||||
|
||||
print(f"Deployment ID {deployment_id} deleted")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# create deployment and get the latest revision
|
||||
deployment_id = create_deployment()
|
||||
revisions = list_revisions(deployment_id)
|
||||
latest_revision = revisions["resources"][0]
|
||||
latest_revision_id = latest_revision["id"]
|
||||
|
||||
# wait for latest revision to be DEPLOYED
|
||||
wait_for_deployment(deployment_id, latest_revision_id)
|
||||
|
||||
# patch the deployment and get the latest revision
|
||||
patch_deployment(deployment_id)
|
||||
revisions = list_revisions(deployment_id)
|
||||
latest_revision = revisions["resources"][0]
|
||||
latest_revision_id = latest_revision["id"]
|
||||
|
||||
# wait for latest revision to be DEPLOYED
|
||||
wait_for_deployment(deployment_id, latest_revision_id)
|
||||
|
||||
# delete the deployment
|
||||
delete_deployment(deployment_id)
|
||||
```
|
||||
File diff suppressed because it is too large
Load Diff
@@ -43,15 +43,18 @@ The LangGraph CLI requires a JSON configuration file that follows this [schema](
|
||||
| <span style="white-space: nowrap;">`graphs`</span> | **Required**. Mapping from graph ID to path where the compiled graph or a function that makes a graph is defined. Example: <ul><li>`./your_package/your_file.py:variable`, where `variable` is an instance of `langgraph.graph.state.CompiledStateGraph`</li><li>`./your_package/your_file.py:make_graph`, where `make_graph` is a function that takes a config dictionary (`langchain_core.runnables.RunnableConfig`) and returns an instance of `langgraph.graph.state.StateGraph` or `langgraph.graph.state.CompiledStateGraph`. See [how to rebuild a graph at runtime](../../cloud/deployment/graph_rebuild.md) for more details.</li></ul> |
|
||||
| <span style="white-space: nowrap;">`auth`</span> | _(Added in v0.0.11)_ Auth configuration containing the path to your authentication handler. Example: `./your_package/auth.py:auth`, where `auth` is an instance of `langgraph_sdk.Auth`. See [authentication guide](../../concepts/auth.md) for details. |
|
||||
| <span style="white-space: nowrap;">`base_image`</span> | Optional. Base image to use for the LangGraph API server. Defaults to `langchain/langgraph-api` or `langchain/langgraphjs-api`. Use this to pin your builds to a particular version of the langgraph API, such as `"langchain/langgraph-server:0.2"`. See https://hub.docker.com/r/langchain/langgraph-server/tags for more details. (added in `langgraph-cli==0.2.8`) |
|
||||
| <span style="white-space: nowrap;">`image_distro`</span> | Optional. Linux distribution for the base image. Must be either `"debian"` or `"wolfi"`. If omitted, defaults to `"debian"`. Available in `langgraph-cli>=0.2.11`.|
|
||||
| <span style="white-space: nowrap;">`env`</span> | Path to `.env` file or a mapping from environment variable to its value. |
|
||||
| <span style="white-space: nowrap;">`store`</span> | Configuration for adding semantic search and/or time-to-live (TTL) to the BaseStore. Contains the following fields: <ul><li>`index` (optional): Configuration for semantic search indexing with fields `embed`, `dims`, and optional `fields`.</li><li>`ttl` (optional): Configuration for item expiration. An object with optional fields: `refresh_on_read` (boolean, defaults to `true`), `default_ttl` (float, lifespan in **minutes**, defaults to no expiration), and `sweep_interval_minutes` (integer, how often to check for expired items, defaults to no sweeping).</li></ul> |
|
||||
| <span style="white-space: nowrap;">`ui`</span> | Optional. Named definitions of UI components emitted by the agent, each pointing to a JS/TS file. (added in `langgraph-cli==0.1.84`) |
|
||||
| <span style="white-space: nowrap;">`python_version`</span> | `3.11`, `3.12`, or `3.13`. Defaults to `3.11`. |
|
||||
| <span style="white-space: nowrap;">`node_version`</span> | Specify `node_version: 20` to use LangGraph.js. |
|
||||
| <span style="white-space: nowrap;">`pip_config_file`</span> | Path to `pip` config file. |
|
||||
| <span style="white-space: nowrap;">`pip_installer`</span> | _(Added in v0.3)_ Optional. Python package installer selector. It can be set to `"auto"`, `"pip"`, or `"uv"`. From version 0.3 onward the default strategy is to run `uv pip`, which typically delivers faster builds while remaining a drop-in replacement. In the uncommon situation where `uv` cannot handle your dependency graph or the structure of your `pyproject.toml`, specify `"pip"` here to revert to the earlier behaviour. |
|
||||
| <span style="white-space: nowrap;">`keep_pkg_tools`</span> | _(Added in v0.3.4)_ Optional. Control whether to retain Python packaging tools (`pip`, `setuptools`, `wheel`) in the final image. Accepted values: <ul><li><code>true</code> : Keep all three tools (skip uninstall).</li><li><code>false</code> / omitted : Uninstall all three tools (default behaviour).</li><li><code>list[str]</code> : Names of tools <strong>to retain</strong>. Each value must be one of "pip", "setuptools", "wheel".</li></ul>. By default, all three tools are uninstalled. |
|
||||
| <span style="white-space: nowrap;">`dockerfile_lines`</span> | Array of additional lines to add to Dockerfile following the import from parent image. |
|
||||
| <span style="white-space: nowrap;">`checkpointer`</span> | Configuration for the checkpointer. Contains a `ttl` field which is an object with the following keys: <ul><li>`strategy`: How to handle expired checkpoints (e.g., `"delete"`).</li><li>`sweep_interval_minutes`: How often to check for expired checkpoints (integer).</li><li>`default_ttl`: Default time-to-live for checkpoints in **minutes** (integer). Defines how long checkpoints are kept before the specified strategy is applied.</li></ul> |
|
||||
| <span style="white-space: nowrap;">`http`</span> | HTTP server configuration with the following fields: <ul><li>`app`: Path to custom Starlette/FastAPI app (e.g., `"./src/agent/webapp.py:app"`). See [custom routes guide](../../how-tos/http/custom_routes.md).</li><li>`disable_assistants`: Disable `/assistants` routes</li><li>`disable_threads`: Disable `/threads` routes</li><li>`disable_runs`: Disable `/runs` routes</li><li>`disable_store`: Disable `/store` routes</li><li>`disable_meta`: Disable `/ok`, `/info`, `/metrics`, and `/docs` routes</li><li>`cors`: CORS configuration with fields for `allow_origins`, `allow_methods`, `allow_headers`, etc.</li><li>`configurable_headers`: Define which request headers to exclude or include as a run's configurable values.</li></ul> |
|
||||
| <span style="white-space: nowrap;">`http`</span> | HTTP server configuration with the following fields: <ul><li>`app`: Path to custom Starlette/FastAPI app (e.g., `"./src/agent/webapp.py:app"`). See [custom routes guide](../../how-tos/http/custom_routes.md).</li><li>`cors`: CORS configuration with fields for `allow_origins`, `allow_methods`, `allow_headers`, etc.</li><li>`configurable_headers`: Define which request headers to exclude or include as a run's configurable values.</li><li>`disable_assistants`: Disable `/assistants` routes</li><li>`disable_mcp`: Disable `/mcp` routes</li><li>`disable_meta`: Disable `/ok`, `/info`, `/metrics`, and `/docs` routes</li><li>`disable_runs`: Disable `/runs` routes</li><li>`disable_store`: Disable `/store` routes</li><li>`disable_threads`: Disable `/threads` routes</li><li>`disable_ui`: Disable `/ui` routes</li><li>`disable_webhooks`: Disable webhooks calls on run completion in all routes</li><li>`mount_prefix`: Prefix for mounted routes (e.g., "/my-deployment/api")</li></ul> |
|
||||
|
||||
=== "JS"
|
||||
|
||||
@@ -79,6 +82,20 @@ The LangGraph CLI requires a JSON configuration file that follows this [schema](
|
||||
}
|
||||
```
|
||||
|
||||
#### Using Wolfi Base Images
|
||||
|
||||
You can specify the Linux distribution for your base image using the `image_distro` field. Valid options are `debian` or `wolfi`. Wolfi is the recommended option as it provides smaller and more secure images. This is available in `langgraph-cli>=0.2.11`.
|
||||
|
||||
```json
|
||||
{
|
||||
"dependencies": ["."],
|
||||
"graphs": {
|
||||
"chat": "./chat/graph.py:graph"
|
||||
},
|
||||
"image_distro": "wolfi"
|
||||
}
|
||||
```
|
||||
|
||||
#### Adding semantic search to the store
|
||||
|
||||
All deployments come with a DB-backed BaseStore. Adding an "index" configuration to your `langgraph.json` will enable [semantic search](../deployment/semantic_search.md) within the BaseStore of your deployment.
|
||||
@@ -113,7 +130,7 @@ The LangGraph CLI requires a JSON configuration file that follows this [schema](
|
||||
- `cohere:embed-english-v3.0`: 1024
|
||||
- `cohere:embed-english-light-v3.0`: 384
|
||||
- `cohere:embed-multilingual-v3.0`: 1024
|
||||
- `cohere:embed-multilingual-light-v3.0`: 384
|
||||
- `cohere:embed-multilingual-light-v3.0`: 384
|
||||
|
||||
#### Semantic search with a custom embedding function
|
||||
|
||||
@@ -346,8 +363,8 @@ The LangGraph CLI requires a JSON configuration file that follows this [schema](
|
||||
|
||||
**Options**
|
||||
|
||||
| Option | Default | Description |
|
||||
| -------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Option | Default | Description |
|
||||
| -------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------- |
|
||||
| `--platform TEXT` | | Target platform(s) to build the Docker image for. Example: `langgraph build --platform linux/amd64,linux/arm64` |
|
||||
| `-t, --tag TEXT` | | **Required**. Tag for the Docker image. Example: `langgraph build -t my-image` |
|
||||
| `--pull / --no-pull` | `--pull` | Build with latest remote Docker image. Use `--no-pull` for running the LangGraph Platform API server with locally built images. |
|
||||
@@ -366,8 +383,8 @@ The LangGraph CLI requires a JSON configuration file that follows this [schema](
|
||||
|
||||
**Options**
|
||||
|
||||
| Option | Default | Description |
|
||||
| -------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Option | Default | Description |
|
||||
| -------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------- |
|
||||
| `--platform TEXT` | | Target platform(s) to build the Docker image for. Example: `langgraph build --platform linux/amd64,linux/arm64` |
|
||||
| `-t, --tag TEXT` | | **Required**. Tag for the Docker image. Example: `langgraph build -t my-image` |
|
||||
| `--no-pull` | | Use locally built images. Defaults to `false` to build with latest remote Docker image. |
|
||||
@@ -379,7 +396,7 @@ The LangGraph CLI requires a JSON configuration file that follows this [schema](
|
||||
|
||||
=== "Python"
|
||||
|
||||
Start LangGraph API server. For local testing, requires a LangSmith API key with access to LangGraph Platform closed beta. Requires a license key for production use.
|
||||
Start LangGraph API server. For local testing, requires a LangSmith API key with access to LangGraph Platform. Requires a license key for production use.
|
||||
|
||||
**Usage**
|
||||
|
||||
@@ -392,6 +409,8 @@ The LangGraph CLI requires a JSON configuration file that follows this [schema](
|
||||
| Option | Default | Description |
|
||||
| ---------------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
||||
| `--wait` | | Wait for services to start before returning. Implies --detach |
|
||||
| `--base-image TEXT` | `langchain/langgraph-api` | Base image to use for the LangGraph API server. Pin to specific versions using version tags. |
|
||||
| `--image TEXT` | | Docker image to use for the langgraph-api service. If specified, skips building and uses this image directly. |
|
||||
| `--postgres-uri TEXT` | Local database | Postgres URI to use for the database. |
|
||||
| `--watch` | | Restart on file changes |
|
||||
| `--debugger-base-url TEXT` | `http://127.0.0.1:[PORT]` | URL used by the debugger to access LangGraph API. |
|
||||
@@ -406,7 +425,7 @@ The LangGraph CLI requires a JSON configuration file that follows this [schema](
|
||||
|
||||
=== "JS"
|
||||
|
||||
Start LangGraph API server. For local testing, requires a LangSmith API key with access to LangGraph Platform closed beta. Requires a license key for production use.
|
||||
Start LangGraph API server. For local testing, requires a LangSmith API key with access to LangGraph Platform. Requires a license key for production use.
|
||||
|
||||
**Usage**
|
||||
|
||||
@@ -419,6 +438,8 @@ The LangGraph CLI requires a JSON configuration file that follows this [schema](
|
||||
| Option | Default | Description |
|
||||
| ---------------------------------------------------------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
||||
| <span style="white-space: nowrap;">`--wait`</span> | | Wait for services to start before returning. Implies --detach |
|
||||
| <span style="white-space: nowrap;">`--base-image TEXT`</span> | <span style="white-space: nowrap;">`langchain/langgraph-api`</span> | Base image to use for the LangGraph API server. Pin to specific versions using version tags. |
|
||||
| <span style="white-space: nowrap;">`--image TEXT`</span> | | Docker image to use for the langgraph-api service. If specified, skips building and uses this image directly. |
|
||||
| <span style="white-space: nowrap;">`--postgres-uri TEXT`</span> | Local database | Postgres URI to use for the database. |
|
||||
| <span style="white-space: nowrap;">`--watch`</span> | | Restart on file changes |
|
||||
| <span style="white-space: nowrap;">`-c, --config FILE`</span> | `langgraph.json` | Path to configuration file declaring dependencies, graphs and environment variables. |
|
||||
|
||||
@@ -10,6 +10,10 @@ This environment variable should be set to `True` if the implementation of a gra
|
||||
|
||||
Defaults to `False`.
|
||||
|
||||
## `BG_JOB_SHUTDOWN_GRACE_PERIOD_SECS`
|
||||
|
||||
Specifies, in seconds, how long the server will wait for background jobs to finish after the queue receives a shutdown signal. After this period, the server will force termination. Defaults to `180` seconds. Set this to ensure jobs have enough time to complete cleanly during shutdown. Added in `langgraph-api==0.2.16`.
|
||||
|
||||
## `BG_JOB_TIMEOUT_SECS`
|
||||
|
||||
The timeout of a background run can be increased. However, the infrastructure for a Cloud SaaS deployment enforces a 1 hour timeout limit for API requests. This means the connection between client and server will timeout after 1 hour. This is not configurable.
|
||||
@@ -18,16 +22,15 @@ A background run can execute for longer than 1 hour, but a client must reconnect
|
||||
|
||||
Defaults to `3600`.
|
||||
|
||||
## `BG_JOB_SHUTDOWN_GRACE_PERIOD_SECS`
|
||||
|
||||
Specifies, in seconds, how long the server will wait for background jobs to finish after the queue receives a shutdown signal. After this period, the server will force termination. Defaults to `3600` seconds. Set this to ensure jobs have enough time to complete cleanly during shutdown. Added in `langgraph-api==0.2.16`.
|
||||
|
||||
## `DD_API_KEY`
|
||||
|
||||
Specify `DD_API_KEY` (your [Datadog API Key](https://docs.datadoghq.com/account_management/api-app-keys/)) to automatically enable Datadog tracing for the deployment. Specify other [`DD_*` environment variables](https://ddtrace.readthedocs.io/en/stable/configuration.html) to configure the tracing instrumentation.
|
||||
|
||||
If `DD_API_KEY` is specified, the application process is wrapped in the [`ddtrace-run` command](https://ddtrace.readthedocs.io/en/stable/installation_quickstart.html). Other `DD_*` environment variables (e.g. `DD_SITE`, `DD_ENV`, `DD_SERVICE`, `DD_TRACE_ENABLED`) are typically needed to properly configure the tracing instrumentation. See [`DD_*` environment variables](https://ddtrace.readthedocs.io/en/stable/configuration.html) for more details.
|
||||
|
||||
!!! note
|
||||
Enabling `DD_API_KEY` (and thus `ddtrace-run`) can override or interfere with other auto-instrumentation solutions (such as OpenTelemetry) that you may have instrumented into your application code.
|
||||
|
||||
## `LANGCHAIN_TRACING_SAMPLING_RATE`
|
||||
|
||||
Sampling rate for traces sent to LangSmith. Valid values: Any float between `0` and `1`.
|
||||
@@ -40,6 +43,14 @@ Type of authentication for the LangGraph Server deployment. Valid values: `langs
|
||||
|
||||
For deployments to LangGraph Platform, this environment variable is set automatically. For local development or deployments where authentication is handled externally (e.g. self-hosted), set this environment variable to `noop`.
|
||||
|
||||
## `LANGGRAPH_POSTGRES_POOL_MAX_SIZE`
|
||||
|
||||
Beginning with langgraph-api version `0.2.12`, the maximum size of the Postgres connection pool (per replica) can be controlled using the `LANGGRAPH_POSTGRES_POOL_MAX_SIZE` environment variable. By setting this variable, you can determine the upper bound on the number of simultaneous connections the server will establish with the Postgres database.
|
||||
|
||||
For example, if a deployment is scaled up to 10 replicas and `LANGGRAPH_POSTGRES_POOL_MAX_SIZE` is configured to `150`, then up to `1500` connections to Postgres can be established. This is particularly useful for deployments where database resources are limited (or more available) or where you need to tune connection behavior for performance or scaling reasons.
|
||||
|
||||
Defaults to `150` connections.
|
||||
|
||||
## `LANGSMITH_RUNS_ENDPOINTS`
|
||||
|
||||
For deployments with [self-hosted LangSmith](https://docs.smith.langchain.com/self_hosting) only.
|
||||
@@ -50,11 +61,14 @@ Set this environment variable to have a deployment send traces to a self-hosted
|
||||
|
||||
## `LANGSMITH_TRACING`
|
||||
|
||||
!!! info "Only for Self-Hosted Data Plane, Self-Hosted Control Plane, and Standalone Container"
|
||||
Disabling LangSmith tracing is only available for [Self-Hosted Data Plane](../../concepts/langgraph_self_hosted_data_plane.md), [Self-Hosted Control Plane](../../concepts/langgraph_self_hosted_control_plane.md), and [Standalone Container](../../concepts/langgraph_standalone_container.md) deployments.
|
||||
|
||||
Set `LANGSMITH_TRACING` to `false` to disable tracing to LangSmith.
|
||||
|
||||
Defaults to `true`.
|
||||
|
||||
## `LOG_COLOR`
|
||||
|
||||
This is mainly relevant in the context of using the dev server via the `langgraph dev` command. Set `LOG_COLOR` to `true` to enable ANSI-colored console output when using the default console renderer. Disabling color output by setting this variable to `false` produces monochrome logs. Defaults to `true`.
|
||||
|
||||
## `LOG_LEVEL`
|
||||
|
||||
Configure [log level](https://docs.python.org/3/library/logging.html#logging-levels). Defaults to `INFO`.
|
||||
@@ -63,9 +77,14 @@ Configure [log level](https://docs.python.org/3/library/logging.html#logging-lev
|
||||
|
||||
Set `LOG_JSON` to `true` to render all log messages as JSON objects using the configured `JSONRenderer`. This produces structured logs that can be easily parsed or ingested by log management systems. Defaults to `false`.
|
||||
|
||||
## `LOG_COLOR`
|
||||
## `MOUNT_PREFIX`
|
||||
|
||||
This is mainly relevant in the context of using the dev server via the `langgraph dev` command. Set `LOG_COLOR` to `true` to enable ANSI-colored console output when using the default console renderer. Disabling color output by setting this variable to `false` produces monochrome logs. Defaults to `true`.
|
||||
!!! info "Only Allowed in Self-Hosted Deployments"
|
||||
The `MOUNT_PREFIX` environment variable is only allowed in Self-Hosted Deployment models, LangGraph Platform SaaS will not allow this environment variable.
|
||||
|
||||
Set `MOUNT_PREFIX` to serve the LangGraph Server under a specific path prefix. This is useful for deployments where the server is behind a reverse proxy or load balancer that requires a specific path prefix.
|
||||
|
||||
For example, if the server is to be served under `https://example.com/langgraph`, set `MOUNT_PREFIX` to `/langgraph`.
|
||||
|
||||
## `N_JOBS_PER_WORKER`
|
||||
|
||||
@@ -95,16 +114,14 @@ Database Connectivity:
|
||||
|
||||
- The custom Postgres instance must be accessible by the LangGraph Server. The user is responsible for ensuring connectivity.
|
||||
|
||||
## `LANGGRAPH_POSTGRES_POOL_MAX_SIZE`
|
||||
## `REDIS_CLUSTER`
|
||||
|
||||
Beginning with langgraph-api version `0.2.12`, the maximum size of the Postgres connection pool can be controlled using the `LANGGRAPH_POSTGRES_POOL_MAX_SIZE` environment variable. By setting this variable, you can determine the upper bound on the number of simultaneous connections the server will establish with the Postgres database. This is particularly useful for deployments where database resources are limited (or more available) or where you need to tune connection behavior for performance or scaling reasons. If not specified, the pool size defaults to 150 connections.
|
||||
!!! info "Only Allowed in Self-Hosted Deployments"
|
||||
Redis Cluster mode is only available in Self-Hosted Deployment models, LangGraph Platform SaaS will provision a redis instance for you by default.
|
||||
|
||||
## `REDIS_URI_CUSTOM`
|
||||
Set `REDIS_CLUSTER` to `True` to enable Redis Cluster mode. When enabled, the system will connect to Redis using cluster mode. This is useful when connecting to a Redis Cluster deployment.
|
||||
|
||||
!!! info "Only for Self-Hosted Data Plane and Self-Hosted Control Plane"
|
||||
Custom Redis instances are only available for [Self-Hosted Data Plane](../../concepts/langgraph_self_hosted_data_plane.md) and [Self-Hosted Control Plane](../../concepts/langgraph_self_hosted_control_plane.md) deployments.
|
||||
|
||||
Specify `REDIS_URI_CUSTOM` to use a custom Redis instance. The value of `REDIS_URI_CUSTOM` must be a valid [Redis connection URI](https://redis-py.readthedocs.io/en/stable/connections.html#redis.Redis.from_url).
|
||||
Defaults to `False`.
|
||||
|
||||
## `REDIS_KEY_PREFIX`
|
||||
|
||||
@@ -115,11 +132,19 @@ Specify a prefix for Redis keys. This allows multiple LangGraph Server instances
|
||||
|
||||
Defaults to `''`.
|
||||
|
||||
## `REDIS_CLUSTER`
|
||||
## `REDIS_URI_CUSTOM`
|
||||
|
||||
!!! info "Only Allowed in Self-Hosted Deployments"
|
||||
Redis Cluster mode is only available in Self-Hosted Deployment models, LangGraph Platform SaaS will provision a redis instance for you by default.
|
||||
!!! info "Only for Self-Hosted Data Plane and Self-Hosted Control Plane"
|
||||
Custom Redis instances are only available for [Self-Hosted Data Plane](../../concepts/langgraph_self_hosted_data_plane.md) and [Self-Hosted Control Plane](../../concepts/langgraph_self_hosted_control_plane.md) deployments.
|
||||
|
||||
Set `REDIS_CLUSTER` to `True` to enable Redis Cluster mode. When enabled, the system will connect to Redis using cluster mode. This is useful when connecting to a Redis Cluster deployment.
|
||||
Specify `REDIS_URI_CUSTOM` to use a custom Redis instance. The value of `REDIS_URI_CUSTOM` must be a valid [Redis connection URI](https://redis-py.readthedocs.io/en/stable/connections.html#redis.Redis.from_url).
|
||||
|
||||
Defaults to `False`.
|
||||
## `RESUMABLE_STREAM_TTL_SECONDS`
|
||||
|
||||
Time-to-live in seconds for resumable stream data in Redis.
|
||||
|
||||
When a run is created and the output is streamed, the stream can be configured to be resumable (e.g. `stream_resumable=True`). If a stream is resumable, output from the stream is temporarily stored in Redis. The TTL for this data can be configured by setting `RESUMABLE_STREAM_TTL_SECONDS`.
|
||||
|
||||
See the [Python](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/#langgraph_sdk.client.RunsClient.stream) and [JS/TS](https://langchain-ai.github.io/langgraphjs/reference/classes/sdk_client.RunsClient.html#stream) SDKs for more details on how to implement resumable streams.
|
||||
|
||||
Defaults to `120` seconds.
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
# LangGraph Server Changelog
|
||||
|
||||
[LangGraph Server](../../concepts/langgraph_server.md) is an API platform for creating and managing agent-based applications. It provides built-in persistence, a task queue, and supports deploying, configuring, and running assistants (agentic workflows) at scale. This changelog documents all notable updates, features, and fixes to LangGraph Server releases.
|
||||
|
||||
---
|
||||
|
||||
## v0.2.108 (2025-07-28)
|
||||
- Added compatibility for langgraph v0.6, including new context API support and a migration to enhance context handling in assistant operations.
|
||||
|
||||
## v0.2.107 (2025-07-27)
|
||||
- Implemented caching for authentication processes to improve performance.
|
||||
- Merged count and select queries to improve database query efficiency.
|
||||
|
||||
## v0.2.106 (2025-07-27)
|
||||
- Log whether run uses resumable streams.
|
||||
|
||||
## v0.2.105 (2025-07-27)
|
||||
- Added a `/heapdump` endpoint to capture and save JS process heap data.
|
||||
|
||||
## v0.2.103 (2025-07-25)
|
||||
- Corrected the metadata endpoint to ensure accurate data retrieval.
|
||||
|
||||
## v0.2.102 (2025-07-24)
|
||||
- Captured interrupt events in the wait method to preserve legacy behavior and stream updates by default.
|
||||
- Added support for SDK structlog in the JavaScript environment, enhancing logging capabilities.
|
||||
|
||||
## v0.2.101 (2025-07-24)
|
||||
- Used the correct metadata endpoint for self-hosted environments, resolving an access issue.
|
||||
|
||||
## v0.2.99 (2025-07-22)
|
||||
- Improved license validation by adding an in-memory cache and handling Redis connection errors more effectively.
|
||||
- Automatically remove agents from memory that are removed from `langgraph.json` to prevent persistence issues.
|
||||
- Ensured the UI namespace for generated UI is a valid JavaScript property name to prevent errors.
|
||||
- Raised a 422 error for improved request validation feedback.
|
||||
|
||||
## v0.2.98 (2025-07-19)
|
||||
- Added langgraph node context for improved log filtering and trace visibility.
|
||||
|
||||
## v0.2.97 (2025-07-19)
|
||||
- Fixed scheduling issue with ckpt ingestion worker that occurred on isolated background loops.
|
||||
- Ensured queue worker starts only after all migrations have completed.
|
||||
- Added more detailed error messages for thread state issues and improved response handling when state updates fail.
|
||||
- Exposed interrupt ID while retrieving thread state for enhanced API response details.
|
||||
|
||||
## v0.2.96 (2025-07-17)
|
||||
- Added a fallback mechanism for configurable header patterns to handle exclude/include settings more effectively.
|
||||
|
||||
## v0.2.95 (2025-07-17)
|
||||
- Avoided setting the future if it is already done to prevent redundant operations.
|
||||
- Resolved compatibility errors in CI by switching from `typing.TypedDict` to `typing_extensions.TypedDict` for Python versions below 3.12.
|
||||
|
||||
## v0.2.94 (2025-07-16)
|
||||
- Improved performance by omitting pending sends for langgraph versions 0.5 and above.
|
||||
- Improved server startup logs to provide clearer warnings when the DD_API_KEY environment variable is set.
|
||||
|
||||
## v0.2.93 (2025-07-16)
|
||||
- Removed the GIN index for run metadata to improve performance.
|
||||
|
||||
## v0.2.92 (2025-07-16)
|
||||
- Enabled copying functionality for blobs and checkpoints, improving data management flexibility.
|
||||
|
||||
## v0.2.91 (2025-07-16)
|
||||
- Reduced writes to the `checkpoint_blobs` table by inlining small values (null, numeric, str, etc.). This means we don't need to store extra values for channels that haven't been updated.
|
||||
|
||||
## v0.2.90 (2025-07-16)
|
||||
- Improve checkpoint writes via node-local background queueing.
|
||||
|
||||
|
||||
## v0.2.89 (2025-07-15)
|
||||
- Decoupled checkpoint writing from thread/run state by removing foreign keys and updated logger to prevent timeout-related failures.
|
||||
|
||||
## v0.2.88 (2025-07-14)
|
||||
- Removed the foreign key constraint for `thread` in the `run` table to simplify database schema.
|
||||
|
||||
## v0.2.87 (2025-07-14)
|
||||
- Added more detailed logs for Redis worker signaling to improve debugging.
|
||||
|
||||
## v0.2.86 (2025-07-11)
|
||||
- Honored tool descriptions in the `/mcp` endpoint to align with expected functionality.
|
||||
|
||||
## v0.2.85 (2025-07-10)
|
||||
- Added support for the `on_disconnect` field to `runs/wait` and included disconnect logs for better debugging.
|
||||
|
||||
## v0.2.84 (2025-07-09)
|
||||
- Removed unnecessary status updates to streamline thread handling and updated version to 0.2.84.
|
||||
|
||||
## v0.2.83 (2025-07-09)
|
||||
- Reduced the default time-to-live for resumable streams to 2 minutes.
|
||||
- Enhanced data submission logic to send data to both Beacon and LangSmith instance based on license configuration.
|
||||
- Enabled submission of self-hosted data to a Langsmith instance when the endpoint is configured.
|
||||
|
||||
## v0.2.82 (2025-07-03)
|
||||
- Addressed a race condition in background runs by implementing a lock using join, ensuring reliable execution across CTEs.
|
||||
|
||||
## v0.2.81 (2025-07-03)
|
||||
- Optimized run streams by reducing initial wait time to improve responsiveness for older or non-existent runs.
|
||||
|
||||
## v0.2.80 (2025-07-03)
|
||||
- Corrected parameter passing in the `logger.ainfo()` API call to resolve a TypeError.
|
||||
|
||||
## v0.2.79 (2025-07-02)
|
||||
- Fixed a JsonDecodeError in checkpointing with remote graph by correcting JSON serialization to handle trailing slashes properly.
|
||||
- Introduced a configuration flag to disable webhooks globally across all routes.
|
||||
|
||||
## v0.2.78 (2025-07-02)
|
||||
- Added timeout retries to webhook calls to improve reliability.
|
||||
- Added HTTP request metrics, including a request count and latency histogram, for enhanced monitoring capabilities.
|
||||
|
||||
## v0.2.77 (2025-07-02)
|
||||
- Added HTTP metrics to improve performance monitoring.
|
||||
- Changed the Redis cache delimiter to reduce conflicts with subgraph message names and updated caching behavior.
|
||||
|
||||
## v0.2.76 (2025-07-01)
|
||||
- Updated Redis cache delimiter to prevent conflicts with subgraph messages.
|
||||
|
||||
## v0.2.74 (2025-06-30)
|
||||
- Scheduled webhooks in an isolated loop to ensure thread-safe operations and prevent errors with PYTHONASYNCIODEBUG=1.
|
||||
|
||||
## v0.2.73 (2025-06-27)
|
||||
- Fixed an infinite frame loop issue and removed the dict_parser due to structlog's unexpected behavior.
|
||||
- Throw a 409 error on deadlock occurrence during run cancellations to handle lock conflicts gracefully.
|
||||
|
||||
## v0.2.72 (2025-06-27)
|
||||
- Ensured compatibility with future langgraph versions.
|
||||
- Implemented a 409 response status to handle deadlock issues during cancellation.
|
||||
|
||||
## v0.2.71 (2025-06-26)
|
||||
- Improved logging for better clarity and detail regarding log types.
|
||||
|
||||
## v0.2.70 (2025-06-26)
|
||||
- Improved error handling to better distinguish and log TimeoutErrors caused by users from internal run timeouts.
|
||||
|
||||
## v0.2.69 (2025-06-26)
|
||||
- Added sorting and pagination to the crons API and updated schema definitions for improved accuracy.
|
||||
|
||||
## v0.2.66 (2025-06-26)
|
||||
- Fixed a 404 error when creating multiple runs with the same thread_id using `on_not_exist="create"`.
|
||||
|
||||
## v0.2.65 (2025-06-25)
|
||||
- Ensured that only fields from `assistant_versions` are returned when necessary.
|
||||
- Ensured consistent data types for in-memory and PostgreSQL users, improving internal authentication handling.
|
||||
|
||||
## v0.2.64 (2025-06-24)
|
||||
- Added descriptions to version entries for better clarity.
|
||||
|
||||
## v0.2.62 (2025-06-23)
|
||||
- Improved user handling for custom authentication in the JS Studio.
|
||||
- Added Prometheus-format run statistics to the metrics endpoint for better monitoring.
|
||||
- Added run statistics in Prometheus format to the metrics endpoint.
|
||||
|
||||
## v0.2.61 (2025-06-20)
|
||||
- Set a maximum idle time for Redis connections to prevent unnecessary open connections.
|
||||
|
||||
## v0.2.60 (2025-06-20)
|
||||
- Enhanced error logging to include traceback details for dictionary operations.
|
||||
- Added a `/metrics` endpoint to expose queue worker metrics for monitoring.
|
||||
|
||||
## v0.2.57 (2025-06-18)
|
||||
- Removed CancelledError from retriable exceptions to allow local interrupts while maintaining retriability for workers.
|
||||
- Introduced middleware to gracefully shut down the server after completing in-flight requests upon receiving a SIGINT.
|
||||
- Reduced metadata stored in checkpoint to only include necessary information.
|
||||
- Improved error handling in join runs to return error details when present.
|
||||
|
||||
## v0.2.56 (2025-06-17)
|
||||
- Improved application stability by adding a handler for SIGTERM signals.
|
||||
|
||||
## v0.2.55 (2025-06-17)
|
||||
- Improved the handling of cancellations in the queue entrypoint.
|
||||
- Improved cancellation handling in the queue entry point.
|
||||
|
||||
## v0.2.54 (2025-06-16)
|
||||
- Enhanced error message for LuaLock timeout during license validation.
|
||||
- Fixed the $contains filter in custom auth by requiring an explicit ::text cast and updated tests accordingly.
|
||||
- Ensured project and tenant IDs are formatted as UUIDs for consistency.
|
||||
|
||||
## v0.2.53 (2025-06-13)
|
||||
- Resolved a timing issue to ensure the queue starts only after the graph is registered.
|
||||
- Improved performance by setting thread and run status in a single query and enhanced error handling during checkpoint writes.
|
||||
- Reduced the default background grace period to 3 minutes.
|
||||
|
||||
## v0.2.52 (2025-06-12)
|
||||
- Now logging expected graphs when one is omitted to improve traceability.
|
||||
- Implemented a time-to-live (TTL) feature for resumable streams.
|
||||
- Improved query efficiency and consistency by adding a unique index and optimizing row locking.
|
||||
|
||||
## v0.2.51 (2025-06-12)
|
||||
- Handled `CancelledError` by marking tasks as ready to retry, improving error management in worker processes.
|
||||
- Added LG API version and request ID to metadata and logs for better tracking.
|
||||
- Added LG API version and request ID to metadata and logs to improve traceability.
|
||||
- Improved database performance by creating indexes concurrently.
|
||||
- Ensured postgres write is committed only after the Redis running marker is set to prevent race conditions.
|
||||
- Enhanced query efficiency and reliability by adding a unique index on thread_id/running, optimizing row locks, and ensuring deterministic run selection.
|
||||
- Resolved a race condition by ensuring Postgres updates only occur after the Redis running marker is set.
|
||||
|
||||
## v0.2.46 (2025-06-07)
|
||||
- Introduced a new connection for each operation while preserving transaction characteristics in Threads state `update()` and `bulk()` commands.
|
||||
|
||||
## v0.2.45 (2025-06-05)
|
||||
- Enhanced streaming feature by incorporating tracing contexts.
|
||||
- Removed an unnecessary query from the Crons.search function.
|
||||
- Resolved connection reuse issue when scheduling next run for multiple cron jobs.
|
||||
- Removed an unnecessary query in the Crons.search function to improve efficiency.
|
||||
- Resolved an issue with scheduling the next cron run by improving connection reuse.
|
||||
|
||||
## v0.2.44 (2025-06-04)
|
||||
- Enhanced the worker logic to exit the pipeline before continuing when the Redis message limit is reached.
|
||||
- Introduced a ceiling for Redis message size with an option to skip messages larger than 128 MB for improved performance.
|
||||
- Ensured the pipeline always closes properly to prevent resource leaks.
|
||||
|
||||
## v0.2.43 (2025-06-04)
|
||||
- Improved performance by omitting logs in metadata calls and ensuring output schema compliance in value streaming.
|
||||
- Ensured the connection is properly closed after use.
|
||||
- Aligned output format to strictly adhere to the specified schema.
|
||||
- Stopped sending internal logs in metadata requests to improve privacy.
|
||||
|
||||
## v0.2.42 (2025-06-04)
|
||||
- Added timestamps to track the start and end of a request's run.
|
||||
- Added tracer information to the configuration settings.
|
||||
- Added support for streaming with tracing contexts.
|
||||
|
||||
## v0.2.41 (2025-06-03)
|
||||
- Added locking mechanism to prevent errors in pipelined executions.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -58,10 +58,10 @@ Tools are useful whenever you want an agent to interact with external systems. E
|
||||
|
||||
### Memory
|
||||
|
||||
[Memory](./memory.md) is crucial for agents, enabling them to retain and utilize information across multiple steps of problem-solving. It operates on different scales:
|
||||
[Memory](../how-tos/memory/add-memory.md) is crucial for agents, enabling them to retain and utilize information across multiple steps of problem-solving. It operates on different scales:
|
||||
|
||||
1. [Short-term memory](./memory.md#short-term-memory): Allows the agent to access information acquired during earlier steps in a sequence.
|
||||
2. [Long-term memory](./memory.md#long-term-memory): Enables the agent to recall information from previous interactions, such as past messages in a conversation.
|
||||
1. [Short-term memory](../how-tos/memory/add-memory.md#add-short-term-memory): Allows the agent to access information acquired during earlier steps in a sequence.
|
||||
2. [Long-term memory](../how-tos/memory/add-memory.md#add-long-term-memory): Enables the agent to recall information from previous interactions, such as past messages in a conversation.
|
||||
|
||||
LangGraph provides full control over memory implementation:
|
||||
|
||||
@@ -69,9 +69,7 @@ LangGraph provides full control over memory implementation:
|
||||
- [`Checkpointer`](./persistence.md#checkpoints): Mechanism to store state at every step across different interactions within a session.
|
||||
- [`Store`](./persistence.md#memory-store): Mechanism to store user-specific or application-level data across sessions.
|
||||
|
||||
This flexible approach allows you to tailor the memory system to your specific agent architecture needs. For a practical guide on adding memory to your graph, see [this tutorial](../how-tos/persistence.ipynb).
|
||||
|
||||
Effective [memory management](../how-tos/memory.ipynb) enhances an agent's ability to maintain context, learn from past experiences, and make more informed decisions over time.
|
||||
This flexible approach allows you to tailor the memory system to your specific agent architecture needs. Effective memory management enhances an agent's ability to maintain context, learn from past experiences, and make more informed decisions over time. For a practical guide on adding and managing memory, see [Memory](../how-tos/memory/add-memory.md).
|
||||
|
||||
### Planning
|
||||
|
||||
@@ -99,7 +97,7 @@ Parallel processing is vital for efficient multi-agent systems and complex tasks
|
||||
- Implementation of map-reduce-like operations
|
||||
- Efficient handling of independent subtasks
|
||||
|
||||
For practical implementation, see our [map-reduce tutorial](../how-tos/graph-api.ipynb#map-reduce-and-the-send-api)
|
||||
For practical implementation, see our [map-reduce tutorial](../how-tos/graph-api.md#map-reduce-and-the-send-api)
|
||||
|
||||
### Subgraphs
|
||||
|
||||
@@ -109,7 +107,7 @@ For practical implementation, see our [map-reduce tutorial](../how-tos/graph-api
|
||||
- Hierarchical organization of agent teams
|
||||
- Controlled communication between agents and the main system
|
||||
|
||||
Subgraphs communicate with the parent graph through overlapping keys in the state schema. This enables flexible, modular agent design. For implementation details, refer to our [subgraph how-to guide](../how-tos/subgraph.ipynb).
|
||||
Subgraphs communicate with the parent graph through overlapping keys in the state schema. This enables flexible, modular agent design. For implementation details, refer to our [subgraph how-to guide](../how-tos/subgraph.md).
|
||||
|
||||
### Reflection
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ Below are examples of directory structures for Python and JavaScript application
|
||||
│ ├── utils # utilities for your graph
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── tools.py # tools for your graph
|
||||
│ │ ├── nodes.py # node functions for you graph
|
||||
│ │ ├── nodes.py # node functions for your graph
|
||||
│ │ └── state.py # state definition of your graph
|
||||
│ ├── __init__.py
|
||||
│ └── agent.py # code for constructing your graph
|
||||
@@ -64,7 +64,7 @@ Below are examples of directory structures for Python and JavaScript application
|
||||
├── src # all project code lies within here
|
||||
│ ├── utils # optional utilities for your graph
|
||||
│ │ ├── tools.ts # tools for your graph
|
||||
│ │ ├── nodes.ts # node functions for you graph
|
||||
│ │ ├── nodes.ts # node functions for your graph
|
||||
│ │ └── state.ts # state definition of your graph
|
||||
│ └── agent.ts # code for constructing your graph
|
||||
├── package.json # package dependencies
|
||||
|
||||
@@ -1,29 +1,31 @@
|
||||
# Assistants
|
||||
|
||||
!!! info "Prerequisites"
|
||||
**Assistants** allow you to manage configurations (like prompts, LLM selection, tools) separately from your graph's core logic, enabling rapid changes that don't alter the graph architecture. It is a way to create multiple specialized versions of the same graph architecture, each optimized for different use cases through context/configuration variations rather than structural changes.
|
||||
|
||||
- [LangGraph Server](./langgraph_server.md)
|
||||
- [Configuration](./low_level.md#configuration)
|
||||
|
||||
When building agents, it is common to make rapid changes that _do not_ alter the graph logic. For example, simply changing prompts or the LLM selection can have significant impacts on the behavior of the agent but does not require updating your graph's architecture. Assistants offer a straightforward way to manage these configurations separately from your graph's core logic.
|
||||
|
||||
Imagine a general-purpose writing agent built on a common graph architecture. While the structure remains the same, different writing styles—such as blog posts and tweets—require tailored configurations to optimize performance. To support these variations, you can create multiple assistants (e.g., one for blogs and another for tweets) that share the underlying graph but differ in model selection and system prompt.
|
||||
For example, imagine a general-purpose writing agent built on a common graph architecture. While the structure remains the same, different writing styles—such as blog posts and tweets—require tailored configurations to optimize performance. To support these variations, you can create multiple assistants (e.g., one for blogs and another for tweets) that share the underlying graph but differ in model selection and system prompt.
|
||||
|
||||

|
||||
|
||||
## Configuring assistants
|
||||
The LangGraph Cloud API provides several endpoints for creating and managing assistants and their versions. See the [API reference](../cloud/reference/api/api_ref.html#tag/assistants) for more details.
|
||||
|
||||
Assistants build on the LangGraph open source concept of [configuration](low_level.md#configuration).
|
||||
While configuration is available in the open source LangGraph library, assistants are only present in [LangGraph Platform](langgraph_platform.md).
|
||||
This is due to the fact that assistants are tightly coupled to your deployed graph. Upon deployment, LangGraph Server will automatically create a default assistant for each graph using the graph's default configuration settings.
|
||||
!!! info
|
||||
|
||||
Assistants are a [LangGraph Platform](langgraph_platform.md) concept. They are not available in the open source LangGraph library.
|
||||
|
||||
## Configuration
|
||||
|
||||
Assistants build on the LangGraph open source concepts of configuration and [runtime context](low_level.md#runtime-context).
|
||||
While these features are available in the open source LangGraph library, assistants are only present in [LangGraph Platform](langgraph_platform.md). This is due to the fact that assistants are tightly coupled to your deployed graph. Upon deployment, LangGraph Server will automatically create a default assistant for each graph using the graph's default context and configuration settings.
|
||||
|
||||
In practice, an assistant is just an _instance_ of a graph with a specific configuration. Therefore, multiple assistants can reference the same graph but can contain different configurations (e.g. prompts, models, tools). The LangGraph Server API provides several endpoints for creating and managing assistants. See the [API reference](../cloud/reference/api/api_ref.html) and [this how-to](../cloud/how-tos/configuration_cloud.md) for more details on how to create assistants.
|
||||
|
||||
## Versioning assistants
|
||||
## Versioning
|
||||
|
||||
Assistants support versioning to track changes over time.
|
||||
Once you've created an assistant, subsequent edits to that assistant will create new versions. See [this how-to](../cloud/how-tos/configuration_cloud.md#create-a-new-version-for-your-assistant) for more details on how to manage assistant versions.
|
||||
|
||||
## Learn more
|
||||
## Execution
|
||||
|
||||
* The LangGraph Cloud API provides several endpoints for creating and managing assistants and their versions. See the [API reference](../cloud/reference/api/api_ref.html#tag/assistants) for more details.
|
||||
A **run** is an invocation of an assistant. Each run may have its own input, configuration, context, and metadata, which may affect execution and output of the underlying graph. A run can optionally be executed on a [thread](./persistence.md#threads).
|
||||
|
||||
The LangGraph Platform API provides several endpoints for creating and managing runs. See the [API reference](../cloud/reference/api/api_ref.html#tag/thread-runs/) for more details.
|
||||
|
||||
@@ -143,6 +143,54 @@ The returned user information is available:
|
||||
In many of our tutorials, we will just show the "authorization" parameter to be concise, but you can opt to accept more information as needed
|
||||
to implement your custom authentication scheme.
|
||||
|
||||
### Agent authentication
|
||||
|
||||
Custom authentication permits delegated access. The values you return in `@auth.authenticate` are added to the run context, giving agents user-scoped credentials lets them access resources on the user’s behalf.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
%% Actors
|
||||
participant ClientApp as Client
|
||||
participant AuthProv as Auth Provider
|
||||
participant LangGraph as LangGraph Backend
|
||||
participant SecretStore as Secret Store
|
||||
participant ExternalService as External Service
|
||||
|
||||
%% Platform login / AuthN
|
||||
ClientApp ->> AuthProv: 1. Login (username / password)
|
||||
AuthProv -->> ClientApp: 2. Return token
|
||||
ClientApp ->> LangGraph: 3. Request with token
|
||||
|
||||
Note over LangGraph: 4. Validate token (@auth.authenticate)
|
||||
LangGraph -->> AuthProv: 5. Fetch user info
|
||||
AuthProv -->> LangGraph: 6. Confirm validity
|
||||
|
||||
%% Fetch user tokens from secret store
|
||||
LangGraph ->> SecretStore: 6a. Fetch user tokens
|
||||
SecretStore -->> LangGraph: 6b. Return tokens
|
||||
|
||||
Note over LangGraph: 7. Apply access control (@auth.on.*)
|
||||
|
||||
%% External Service round-trip
|
||||
LangGraph ->> ExternalService: 8. Call external service (with header)
|
||||
Note over ExternalService: 9. External service validates header and executes action
|
||||
ExternalService -->> LangGraph: 10. Service response
|
||||
|
||||
%% Return to caller
|
||||
LangGraph -->> ClientApp: 11. Return resources
|
||||
```
|
||||
|
||||
After authentication, the platform creates a special configuration object that is passed to your graph and all nodes via the configurable context.
|
||||
This object contains information about the current user, including any custom fields you return from your [`@auth.authenticate`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.Auth.authenticate) handler.
|
||||
|
||||
To enable an agent to act on behalf of the user, use [custom authentication middleware](../how-tos/auth/custom_auth.md). This will allow the agent to interact with external systems like MCP servers, external databases, and even other agents on behalf of the user.
|
||||
|
||||
For more information, see the [Use custom auth](../how-tos/auth/custom_auth.md#enable-agent-authentication) guide.
|
||||
|
||||
### Agent authentication with MCP
|
||||
|
||||
For information on how to authenticate an agent to an MCP server, see the [MCP conceptual guide](../concepts/mcp.md).
|
||||
|
||||
## Authorization
|
||||
|
||||
After authentication, LangGraph calls your [`@auth.on`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.Auth.on) handlers to control access to specific resources (e.g., threads, assistants, crons). These handlers can:
|
||||
@@ -198,7 +246,7 @@ async def add_owner(
|
||||
You can register handlers for specific resources and actions by chaining the resource and action names together with the [`@auth.on`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.Auth.on) decorator.
|
||||
When a request is made, the most specific handler that matches that resource and action is called. Below is an example of how to register handlers for specific resources and actions. For the following setup:
|
||||
|
||||
1. Authenticated users are able to create threads, read thread, create runs on threads
|
||||
1. Authenticated users are able to create threads, read threads, and create runs on threads
|
||||
2. Only users with the "assistants:create" permission are allowed to create new assistants
|
||||
3. All other endpoints (e.g., e.g., delete assistant, crons, store) are disabled for all users.
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Breakpoints
|
||||
|
||||
Breakpoints pause graph execution at defined points and let you step through each stage. They use LangGraph's [**persistence layer**](./persistence.md), which saves the graph state after each step.
|
||||
|
||||
With breakpoints, you can inspect the graph's state and node inputs at any point. Execution pauses **indefinitely** until you resume, as the checkpointer preserves the state.
|
||||
|
||||
<figure markdown="1">
|
||||
{: style="max-height:400px"}
|
||||
<figcaption>An example graph consisting of 3 sequential steps with a breakpoint before step_3. </figcaption> </figure>
|
||||
@@ -5,13 +5,22 @@ search:
|
||||
|
||||
# Deployment Options
|
||||
|
||||
There are 4 main options for deploying with the LangGraph Platform:
|
||||
## Free deployment
|
||||
|
||||
There are two free options for deploying LangGraph applications via the LangGraph Server:
|
||||
|
||||
1. [Local](../tutorials/langgraph-platform/local-server.md): Deploy for local testing and development.
|
||||
1. [Standalone Container (Lite)](../concepts/langgraph_standalone_container.md): A limited version of Standalone Container for deployments unlikely to see more than 1 million node executions per year and that do not need crons and other enterprise features. Standalone Container (Lite) deployment option is free with a LangSmith API key.
|
||||
|
||||
## Production deployment
|
||||
|
||||
There are 4 main options for deploying with the [LangGraph Platform](langgraph_platform.md):
|
||||
|
||||
1. [Cloud SaaS](#cloud-saas)
|
||||
|
||||
1. [Self-Hosted Data Plane<sup>(Beta)</sup>](#self-hosted-data-plane)
|
||||
1. [Self-Hosted Data Plane](#self-hosted-data-plane)
|
||||
|
||||
1. [Self-Hosted Control Plane<sup>(Beta)</sup>](#self-hosted-control-plane)
|
||||
1. [Self-Hosted Control Plane](#self-hosted-control-plane)
|
||||
|
||||
1. [Standalone Container](#standalone-container)
|
||||
|
||||
@@ -22,7 +31,7 @@ A quick comparison:
|
||||
|----------------------|----------------|----------------------------|-------------------------------|--------------------------|
|
||||
| **[Control plane UI/API](../concepts/langgraph_control_plane.md)** | Yes | Yes | Yes | No |
|
||||
| **CI/CD** | Managed internally by platform | Managed externally by you | Managed externally by you | Managed externally by you |
|
||||
| **Data/compute residency** | LangChain’s cloud | Your cloud | Your cloud | Your cloud |
|
||||
| **Data/compute residency** | LangChain's cloud | Your cloud | Your cloud | Your cloud |
|
||||
| **LangSmith compatibility** | Trace to LangSmith SaaS | Trace to LangSmith SaaS | Trace to Self-Hosted LangSmith | Optional tracing |
|
||||
| **[Server version compatibility](../concepts/langgraph_server.md#server-versions)** | Enterprise | Enterprise | Enterprise | Lite, Enterprise |
|
||||
| **[Pricing](https://www.langchain.com/pricing-langgraph-platform)** | Plus | Enterprise | Enterprise | Developer |
|
||||
@@ -41,7 +50,7 @@ For more information, please see:
|
||||
## Self-Hosted Data Plane
|
||||
|
||||
!!! info "Important"
|
||||
The Self-Hosted Data Plane deployment option is currently in beta stage and requires an [Enterprise](../concepts/plans.md) plan.
|
||||
The Self-Hosted Data Plane deployment option requires an [Enterprise](../concepts/plans.md) plan.
|
||||
|
||||
The [Self-Hosted Data Plane](./langgraph_self_hosted_data_plane.md) deployment option is a "hybrid" model for deployment where we manage the [control plane](./langgraph_control_plane.md) in our cloud and you manage the [data plane](./langgraph_data_plane.md) in your cloud. This option provides a way to securely manage your data plane infrastructure, while offloading control plane management to us.
|
||||
|
||||
@@ -57,7 +66,7 @@ For more information, please see:
|
||||
## Self-Hosted Control Plane
|
||||
|
||||
!!! info "Important"
|
||||
The Self-Hosted Control Plane deployment option is currently in beta stage and requires an [Enterprise](../concepts/plans.md) plan.
|
||||
The Self-Hosted Control Plane deployment option requires an [Enterprise](../concepts/plans.md) plan.
|
||||
|
||||
The [Self-Hosted Control Plane](./langgraph_self_hosted_control_plane.md) deployment option is a fully self-hosted model for deployment where you manage the [control plane](./langgraph_control_plane.md) and [data plane](./langgraph_data_plane.md) in your cloud. This option gives you full control and responsibility of the control plane and data plane infrastructure.
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ If a [node](./low_level.md#nodes) contains multiple operations, you may find it
|
||||
from typing_extensions import TypedDict
|
||||
import uuid
|
||||
|
||||
from langgraph.checkpoint.memory import MemorySaver
|
||||
from langgraph.checkpoint.memory import InMemorySaver
|
||||
from langgraph.graph import StateGraph, START, END
|
||||
import requests
|
||||
|
||||
@@ -74,7 +74,7 @@ If a [node](./low_level.md#nodes) contains multiple operations, you may find it
|
||||
builder.add_edge("call_api", END)
|
||||
|
||||
# Specify a checkpointer
|
||||
checkpointer = MemorySaver()
|
||||
checkpointer = InMemorySaver()
|
||||
|
||||
# Compile the graph with the checkpointer
|
||||
graph = builder.compile(checkpointer=checkpointer)
|
||||
@@ -94,7 +94,7 @@ If a [node](./low_level.md#nodes) contains multiple operations, you may find it
|
||||
from typing_extensions import TypedDict
|
||||
import uuid
|
||||
|
||||
from langgraph.checkpoint.memory import MemorySaver
|
||||
from langgraph.checkpoint.memory import InMemorySaver
|
||||
from langgraph.func import task
|
||||
from langgraph.graph import StateGraph, START, END
|
||||
import requests
|
||||
@@ -129,7 +129,7 @@ If a [node](./low_level.md#nodes) contains multiple operations, you may find it
|
||||
builder.add_edge("call_api", END)
|
||||
|
||||
# Specify a checkpointer
|
||||
checkpointer = MemorySaver()
|
||||
checkpointer = InMemorySaver()
|
||||
|
||||
# Compile the graph with the checkpointer
|
||||
graph = builder.compile(checkpointer=checkpointer)
|
||||
|
||||
@@ -47,7 +47,7 @@ LangGraph is a stateful, orchestration framework that brings added control to ag
|
||||
|
||||
No. LangGraph Platform is proprietary software.
|
||||
|
||||
There is a free, self-hosted version of LangGraph Platform with access to basic features. The Cloud SaaS deployment option is free while in beta, but will eventually be a paid service. We will always give ample notice before charging for a service and reward our early adopters with preferential pricing. The Self-Hosted deployment options are paid services. [Contact our sales team](https://www.langchain.com/contact-sales) to learn more.
|
||||
There is a free, self-hosted version of LangGraph Platform with access to basic features. The Cloud SaaS deployment option and the Self-Hosted deployment options are paid services. [Contact our sales team](https://www.langchain.com/contact-sales) to learn more.
|
||||
|
||||
For more information, see our [LangGraph Platform pricing page](https://www.langchain.com/pricing-langgraph-platform).
|
||||
|
||||
@@ -63,4 +63,8 @@ Yes! LangGraph is totally ambivalent to what LLMs are used under the hood. The m
|
||||
|
||||
Yes! You can use the [development version of LangGraph Server](../tutorials/langgraph-platform/local-server.md) to run the backend locally.
|
||||
This will connect to the studio frontend hosted as part of LangSmith.
|
||||
If you set an environment variable of `LANGSMITH_TRACING=false`, then no traces will be sent to LangSmith.
|
||||
If you set an environment variable of `LANGSMITH_TRACING=false`, then no traces will be sent to LangSmith.
|
||||
|
||||
## What does "nodes executed" mean for LangGraph Platform usage?
|
||||
|
||||
**Nodes Executed** is the aggregate number of nodes in a LangGraph application that are called and completed successfully during an invocation of the application. If a node in the graph is not called during execution or ends in an error state, these nodes will not be counted. If a node is called and completes successfully multiple times, each occurrence will be counted.
|
||||
@@ -7,7 +7,7 @@ search:
|
||||
|
||||
## Overview
|
||||
|
||||
The **Functional API** allows you to add LangGraph's key features — [persistence](./persistence.md), [memory](./memory.md), [human-in-the-loop](./human_in_the_loop.md), and [streaming](./streaming.md) — to your applications with minimal changes to your existing code.
|
||||
The **Functional API** allows you to add LangGraph's key features — [persistence](./persistence.md), [memory](../how-tos/memory/add-memory.md), [human-in-the-loop](./human_in_the_loop.md), and [streaming](./streaming.md) — to your applications with minimal changes to your existing code.
|
||||
|
||||
It is designed to integrate these features into existing code that may use standard language primitives for branching and control flow, such as `if` statements, `for` loops, and function calls. Unlike many data orchestration frameworks that require restructuring code into an explicit pipeline or DAG, the Functional API allows you to incorporate these capabilities without enforcing a rigid execution model.
|
||||
|
||||
@@ -18,17 +18,28 @@ The Functional API uses two key building blocks:
|
||||
|
||||
This provides a minimal abstraction for building workflows with state management and streaming.
|
||||
|
||||
!!! tip
|
||||
!!! tip
|
||||
|
||||
For information on how to use the functional API, see [Use Functional API](../how-tos/use-functional-api.md).
|
||||
|
||||
## Functional API vs. Graph API
|
||||
|
||||
For users who prefer a more declarative approach, LangGraph's [Graph API](./low_level.md) allows you to define workflows using a Graph paradigm. Both APIs share the same underlying runtime, so you can use them together in the same application.
|
||||
|
||||
Here are some key differences:
|
||||
|
||||
- **Control flow**: The Functional API does not require thinking about graph structure. You can use standard Python constructs to define workflows. This will usually trim the amount of code you need to write.
|
||||
- **Short-term memory**: The **GraphAPI** requires declaring a [**State**](./low_level.md#state) and may require defining [**reducers**](./low_level.md#reducers) to manage updates to the graph state. `@entrypoint` and `@tasks` do not require explicit state management as their state is scoped to the function and is not shared across functions.
|
||||
- **Checkpointing**: Both APIs generate and use checkpoints. In the **Graph API** a new checkpoint is generated after every [superstep](./low_level.md). In the **Functional API**, when tasks are executed, their results are saved to an existing checkpoint associated with the given entrypoint instead of creating a new checkpoint.
|
||||
- **Visualization**: The Graph API makes it easy to visualize the workflow as a graph which can be useful for debugging, understanding the workflow, and sharing with others. The Functional API does not support visualization as the graph is dynamically generated during runtime.
|
||||
|
||||
For users who prefer a more declarative approach, LangGraph's [Graph API](./low_level.md) allows you to define workflows using a Graph paradigm. Both APIs share the same underlying runtime, so you can use them together in the same application.
|
||||
Please see the [Functional API vs. Graph API](#functional-api-vs-graph-api) section for a comparison of the two paradigms.
|
||||
|
||||
## Example
|
||||
|
||||
Below we demonstrate a simple application that writes an essay and [interrupts](human_in_the_loop.md) to request human review.
|
||||
|
||||
```python
|
||||
from langgraph.checkpoint.memory import MemorySaver
|
||||
from langgraph.checkpoint.memory import InMemorySaver
|
||||
from langgraph.func import entrypoint, task
|
||||
from langgraph.types import interrupt
|
||||
|
||||
@@ -39,7 +50,7 @@ def write_essay(topic: str) -> str:
|
||||
time.sleep(1) # A placeholder for a long-running task.
|
||||
return f"An essay about topic: {topic}"
|
||||
|
||||
@entrypoint(checkpointer=MemorySaver())
|
||||
@entrypoint(checkpointer=InMemorySaver())
|
||||
def workflow(topic: str) -> dict:
|
||||
"""A simple workflow that writes an essay and asks for a review."""
|
||||
essay = write_essay("cat").result()
|
||||
@@ -68,51 +79,54 @@ def workflow(topic: str) -> dict:
|
||||
```python
|
||||
import time
|
||||
import uuid
|
||||
|
||||
from langgraph.func import entrypoint, task
|
||||
from langgraph.types import interrupt
|
||||
from langgraph.checkpoint.memory import MemorySaver
|
||||
from langgraph.checkpoint.memory import InMemorySaver
|
||||
|
||||
|
||||
@task
|
||||
def write_essay(topic: str) -> str:
|
||||
"""Write an essay about the given topic."""
|
||||
time.sleep(1) # This is a placeholder for a long-running task.
|
||||
time.sleep(1) # This is a placeholder for a long-running task.
|
||||
return f"An essay about topic: {topic}"
|
||||
|
||||
@entrypoint(checkpointer=MemorySaver())
|
||||
@entrypoint(checkpointer=InMemorySaver())
|
||||
def workflow(topic: str) -> dict:
|
||||
"""A simple workflow that writes an essay and asks for a review."""
|
||||
essay = write_essay("cat").result()
|
||||
is_approved = interrupt({
|
||||
# Any json-serializable payload provided to interrupt as argument.
|
||||
# It will be surfaced on the client side as an Interrupt when streaming data
|
||||
# from the workflow.
|
||||
"essay": essay, # The essay we want reviewed.
|
||||
# We can add any additional information that we need.
|
||||
# For example, introduce a key called "action" with some instructions.
|
||||
"action": "Please approve/reject the essay",
|
||||
})
|
||||
|
||||
is_approved = interrupt(
|
||||
{
|
||||
# Any json-serializable payload provided to interrupt as argument.
|
||||
# It will be surfaced on the client side as an Interrupt when streaming data
|
||||
# from the workflow.
|
||||
"essay": essay, # The essay we want reviewed.
|
||||
# We can add any additional information that we need.
|
||||
# For example, introduce a key called "action" with some instructions.
|
||||
"action": "Please approve/reject the essay",
|
||||
}
|
||||
)
|
||||
return {
|
||||
"essay": essay, # The essay that was generated
|
||||
"is_approved": is_approved, # Response from HIL
|
||||
"essay": essay, # The essay that was generated
|
||||
"is_approved": is_approved, # Response from HIL
|
||||
}
|
||||
|
||||
|
||||
thread_id = str(uuid.uuid4())
|
||||
|
||||
config = {
|
||||
"configurable": {
|
||||
"thread_id": thread_id
|
||||
}
|
||||
}
|
||||
|
||||
config = {"configurable": {"thread_id": thread_id}}
|
||||
for item in workflow.stream("cat", config):
|
||||
print(item)
|
||||
```
|
||||
|
||||
```pycon
|
||||
{'write_essay': 'An essay about topic: cat'}
|
||||
{'__interrupt__': (Interrupt(value={'essay': 'An essay about topic: cat', 'action': 'Please approve/reject the essay'}, resumable=True, ns=['workflow:f7b8508b-21c0-8b4c-5958-4e8de74d2684'], when='during'),)}
|
||||
# > {'write_essay': 'An essay about topic: cat'}
|
||||
# > {
|
||||
# > '__interrupt__': (
|
||||
# > Interrupt(
|
||||
# > value={
|
||||
# > 'essay': 'An essay about topic: cat',
|
||||
# > 'action': 'Please approve/reject the essay'
|
||||
# > },
|
||||
# > id='b9b2b9d788f482663ced6dc755c9e981'
|
||||
# > ),
|
||||
# > )
|
||||
# > }
|
||||
```
|
||||
|
||||
An essay has been written and is ready for review. Once the review is provided, we can resume the workflow:
|
||||
@@ -532,15 +546,6 @@ While different runs of a workflow can produce different results, resuming a **s
|
||||
|
||||
Idempotency ensures that running the same operation multiple times produces the same result. This helps prevent duplicate API calls and redundant processing if a step is rerun due to a failure. Always place API calls inside **tasks** functions for checkpointing, and design them to be idempotent in case of re-execution. Re-execution can occur if a **task** starts, but does not complete successfully. Then, if the workflow is resumed, the **task** will run again. Use idempotency keys or verify existing results to avoid duplication.
|
||||
|
||||
## Functional API vs. Graph API
|
||||
|
||||
The **Functional API** and the [Graph APIs (StateGraph)](./low_level.md#stategraph) provide two different paradigms to create applications with LangGraph. Here are some key differences:
|
||||
|
||||
- **Control flow**: The Functional API does not require thinking about graph structure. You can use standard Python constructs to define workflows. This will usually trim the amount of code you need to write.
|
||||
- **Short-term memory**: The **GraphAPI** requires declaring a [**State**](./low_level.md#state) and may require defining [**reducers**](./low_level.md#reducers) to manage updates to the graph state. `@entrypoint` and `@tasks` do not require explicit state management as their state is scoped to the function and is not shared across functions.
|
||||
- **Checkpointing**: Both APIs generate and use checkpoints. In the **Graph API** a new checkpoint is generated after every [superstep](./low_level.md). In the **Functional API**, when tasks are executed, their results are saved to an existing checkpoint associated with the given entrypoint instead of creating a new checkpoint.
|
||||
- **Visualization**: The Graph API makes it easy to visualize the workflow as a graph which can be useful for debugging, understanding the workflow, and sharing with others. The Functional API does not support visualization as the graph is dynamically generated during runtime.
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
### Handling side effects
|
||||
|
||||
@@ -11,21 +11,36 @@ hide:
|
||||
|
||||
# Human-in-the-loop
|
||||
|
||||
LangGraph supports robust **human-in-the-loop (HIL)** workflows, enabling human intervention at any point in an automated process. This is especially useful in large language model (LLM)-driven applications where model output may require validation, correction, or additional context.
|
||||
To review, edit, and approve tool calls in an agent or workflow, [use LangGraph's human-in-the-loop features](../how-tos/human_in_the_loop/add-human-in-the-loop.md) to enable human intervention at any point in a workflow. This is especially useful in large language model (LLM)-driven applications where model output may require validation, correction, or additional context.
|
||||
|
||||
<figure markdown="1">
|
||||
{: style="max-height:400px"}
|
||||
</figure>
|
||||
|
||||
!!! tip
|
||||
|
||||
For information on how to use human-in-the-loop, see [Enable human intervention](../how-tos/human_in_the_loop/add-human-in-the-loop.md) and [Human-in-the-loop using Server API](../cloud/how-tos/add-human-in-the-loop.md).
|
||||
|
||||
## Key capabilities
|
||||
|
||||
* **Persistent execution state**: LangGraph checkpoints the graph state after each step, allowing execution to pause indefinitely at defined nodes. This supports asynchronous human review or input without time constraints.
|
||||
* **Persistent execution state**: Interrupts use LangGraph's [persistence](./persistence.md) layer, which saves the graph state, to indefinitely pause graph execution until you resume. This is possible because LangGraph checkpoints the graph state after each step, which allows the system to persist execution context and later resume the workflow, continuing from where it left off. This supports asynchronous human review or input without time constraints.
|
||||
|
||||
* **Flexible integration points**: HIL logic can be introduced at any point in the workflow. This allows targeted human involvement, such as approving API calls, correcting outputs, or guiding conversations.
|
||||
There are two ways to pause a graph:
|
||||
|
||||
## Typical use cases
|
||||
- [Dynamic interrupts](../how-tos/human_in_the_loop/add-human-in-the-loop.md#pause-using-interrupt): Use `interrupt` to pause a graph from inside a specific node, based on the current state of the graph.
|
||||
- [Static interrupts](../how-tos/human_in_the_loop/add-human-in-the-loop.md#debug-with-interrupts): Use `interrupt_before` and `interrupt_after` to pause the graph at defined points, either before or after a node executes.
|
||||
|
||||
1. [**🛠️ Reviewing tool calls**](../how-tos/human_in_the_loop/add-human-in-the-loop.md#review-tool-calls): Humans can review, edit, or approve tool calls requested by the LLM before tool execution.
|
||||
2. **✅ Validating LLM outputs**: Humans can review, edit, or approve content generated by the LLM.
|
||||
3. **💡 Providing context**: Enable the LLM to explicitly request human input for clarification or additional details or to support multi-turn conversations.
|
||||
<figure markdown="1">
|
||||
{: style="max-height:400px"}
|
||||
<figcaption>An example graph consisting of 3 sequential steps with a breakpoint before step_3. </figcaption> </figure>
|
||||
|
||||
## Implementation
|
||||
* **Flexible integration points**: Human-in-the-loop logic can be introduced at any point in the workflow. This allows targeted human involvement, such as approving API calls, correcting outputs, or guiding conversations.
|
||||
|
||||
* `interrupt` function: Pauses execution at a specific point, presents information for human review.
|
||||
* `Command` primitive: Used to resume execution with a value provided by the human.
|
||||
## Patterns
|
||||
|
||||
There are four typical design patterns that you can implement using `interrupt` and `Command`:
|
||||
|
||||
- [Approve or reject](../how-tos/human_in_the_loop/add-human-in-the-loop.md#approve-or-reject): Pause the graph before a critical step, such as an API call, to review and approve the action. If the action is rejected, you can prevent the graph from executing the step, and potentially take an alternative action. This pattern often involves routing the graph based on the human's input.
|
||||
- [Edit graph state](../how-tos/human_in_the_loop/add-human-in-the-loop.md#review-and-edit-state): Pause the graph to review and edit the graph state. This is useful for correcting mistakes or updating the state with additional information. This pattern often involves updating the state with the human's input.
|
||||
- [Review tool calls](../how-tos/human_in_the_loop/add-human-in-the-loop.md#review-tool-calls): Pause the graph to review and edit tool calls requested by the LLM before tool execution.
|
||||
- [Validate human input](../how-tos/human_in_the_loop/add-human-in-the-loop.md#validate-human-input): Pause the graph to validate human input before proceeding with the next step.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 121 KiB |
@@ -9,13 +9,18 @@ search:
|
||||
|
||||
## Installation
|
||||
|
||||
The LangGraph CLI can be installed via pip:
|
||||
The LangGraph CLI can be installed via pip or [Homebrew](https://brew.sh/):
|
||||
|
||||
=== "pip"
|
||||
```bash
|
||||
pip install langgraph-cli
|
||||
```
|
||||
|
||||
=== "Homebrew"
|
||||
```bash
|
||||
brew install langgraph-cli
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
LangGraph CLI provides the following core functionality:
|
||||
|
||||
@@ -19,13 +19,14 @@ From the control plane UI, you can:
|
||||
- Update a deployment.
|
||||
- Update environment variables for a deployment.
|
||||
- View build and server logs of a deployment.
|
||||
- View deployment metrics such as CPU and memory usage.
|
||||
- Delete a deployment.
|
||||
|
||||
The Control Plane UI is embedded in [LangSmith](https://docs.smith.langchain.com/langgraph_cloud).
|
||||
|
||||
## Control Plane API
|
||||
|
||||
This section describes data model of the control plane API. The API is used to create, update, and delete deployments. However, they are not publicly accessible.
|
||||
This section describes the data model of the control plane API. The API is used to create, update, and delete deployments. See the [control plane API reference](../cloud/reference/api/api_ref_control_plane.md) for more details.
|
||||
|
||||
### Deployment
|
||||
|
||||
@@ -33,11 +34,7 @@ A deployment is an instance of a LangGraph Server. A single deployment can have
|
||||
|
||||
### Revision
|
||||
|
||||
A revision is an iteration of a deployment. When a new deployment is created, an initial revision is automatically created. To deploy code changes or update environment variables for a deployment, a new revision must be created.
|
||||
|
||||
### Environment Variable
|
||||
|
||||
Environment variables are set for a deployment. All environment variables are stored as secrets (i.e. saved in a secrets store).
|
||||
A revision is an iteration of a deployment. When a new deployment is created, an initial revision is automatically created. To deploy code changes or update secrets for a deployment, a new revision must be created.
|
||||
|
||||
## Control Plane Features
|
||||
|
||||
@@ -49,21 +46,40 @@ For simplicity, the control plane offers two deployment types with different res
|
||||
|
||||
| **Deployment Type** | **CPU/Memory** | **Scaling** | **Database** |
|
||||
|---------------------|-----------------|---------------------|----------------------------------------------------------------------------------|
|
||||
| Development | 1 CPU, 1 GB RAM | Up to 1 container | 10 GB disk, no backups |
|
||||
| Production | 2 CPU, 2 GB RAM | Up to 10 containers | Autoscaling disk, automatic backups, highly available (multi-zone configuration) |
|
||||
| Development | 1 CPU, 1 GB RAM | Up to 1 replica | 10 GB disk, no backups |
|
||||
| Production | 2 CPU, 2 GB RAM | Up to 10 replicas | Autoscaling disk, automatic backups, highly available (multi-zone configuration) |
|
||||
|
||||
CPU and memory resources are per container.
|
||||
CPU and memory resources are per replica.
|
||||
|
||||
!!! warning "Immutable Deployment Type"
|
||||
|
||||
Once a deployment is created, the deployment type cannot be changed.
|
||||
|
||||
!!! info "Resource Customization"
|
||||
For `Production` type deployments, resources can be manually increased on a case-by-case basis depending on use case and capacity constraints. Contact support@langchain.dev to request an increase in resources.
|
||||
!!! info "Self-Hosted Deployment"
|
||||
Resources for [Self-Hosted Data Plane](../concepts/langgraph_self_hosted_data_plane.md) and [Self-Hosted Control Plane](../concepts/langgraph_self_hosted_control_plane.md) deployments can be fully customized. Deployment types are only applicable for [Cloud SaaS](../concepts/langgraph_cloud.md) deployments.
|
||||
|
||||
For `Development` types deployments, database disk size can be manually increased on a case-by-case basis depending on use case and capacity constraints. For most use cases, [TTLs](../how-tos/ttl/configure_ttl.md) should be configured to manage disk usage. Contact support@langchain.dev to request an increase in resources.
|
||||
#### Production
|
||||
|
||||
Resources for [Self-Hosted Data Plane](../concepts/langgraph_self_hosted_data_plane.md) and [Self-Hosted Control Plane](../concepts/langgraph_self_hosted_control_plane.md) deployments can be fully customized.
|
||||
`Production` type deployments are suitable for "production" workloads. For example, select `Production` for customer-facing applications in the critical path.
|
||||
|
||||
Resources for `Production` type deployments can be manually increased on a case-by-case basis depending on use case and capacity constraints. Contact support@langchain.dev to request an increase in resources.
|
||||
|
||||
#### Development
|
||||
|
||||
`Development` type deployments are suitable development and testing. For example, select `Development` for internal testing environments. `Development` type deployments are not suitable for "production" workloads.
|
||||
|
||||
!!! danger "Preemptible Compute Infrastructure"
|
||||
`Development` type deployments (API server, queue server, and database) are provisioned on preemptible compute infrastructure. This means the compute infrastructure **may be terminated at any time without notice**. This may result in intermittent...
|
||||
|
||||
- Redis connection timeouts/errors
|
||||
- Postgres connection timeouts/errors
|
||||
- Failed or retrying background runs
|
||||
|
||||
This behavior is expected. Preemptible compute infrastructure **significantly reduces the cost to provision a `Development` type deployment**. By design, LangGraph Server is fault-tolerant. The implementation will automatically attempt to recover from Redis/Postgres connection errors and retry failed background runs.
|
||||
|
||||
`Production` type deployments are provisioned on durable compute infrastructure, not preemptible compute infrastructure.
|
||||
|
||||
Database disk size for `Development` type deployments can be manually increased on a case-by-case basis depending on use case and capacity constraints. For most use cases, [TTLs](../how-tos/ttl/configure_ttl.md) should be configured to manage disk usage. Contact support@langchain.dev to request an increase in resources.
|
||||
|
||||
### Database Provisioning
|
||||
|
||||
@@ -88,8 +104,26 @@ Infrastructure for deployments and revisions are provisioned and deployed asynch
|
||||
|
||||
The control plane and [LangGraph Data Plane](./langgraph_data_plane.md) "listener" application coordinate to achieve asynchronous deployments.
|
||||
|
||||
### Monitoring
|
||||
|
||||
After a deployment is ready, the control plane monitors the deployment and records various metrics, such as:
|
||||
|
||||
- CPU and memory usage of the deployment.
|
||||
- Number of container restarts.
|
||||
- Number of replicas (this will increase with [autoscaling](../concepts/langgraph_data_plane.md#autoscaling)).
|
||||
- [Postgres](../concepts/langgraph_data_plane.md#postgres) CPU, memory usage, and disk usage.
|
||||
- [LangGraph Server queue](../concepts/langgraph_server.md#persistence-and-task-queue) pending/active run count.
|
||||
- [LangGraph Server API](../concepts/langgraph_server.md) success response count, error response count, and latency.
|
||||
|
||||
These metrics are displayed as charts in the Control Plane UI.
|
||||
|
||||
### LangSmith Integration
|
||||
|
||||
A [LangSmith](https://docs.smith.langchain.com/) tracing project is automatically created for each deployment. The tracing project has the same name as the deployment. When creating a deployment, the `LANGCHAIN_TRACING` and `LANGSMITH_API_KEY`/`LANGCHAIN_API_KEY` environment variables do not need to be specified; they are set automatically by the control plane.
|
||||
A [LangSmith](https://docs.smith.langchain.com/) tracing project and LangSmith API key are automatically created for each deployment. The deployment uses the API key to automatically send traces to LangSmith.
|
||||
|
||||
When a deployment is deleted, the traces and the tracing project are not deleted.
|
||||
- The tracing project has the same name as the deployment.
|
||||
- The API key has the description `LangGraph Platform: <deployment_name>`.
|
||||
- The API key is never revealed and cannot be deleted manually.
|
||||
- When creating a deployment, the `LANGCHAIN_TRACING` and `LANGSMITH_API_KEY`/`LANGCHAIN_API_KEY` environment variables do not need to be specified; they are set automatically by the control plane.
|
||||
|
||||
When a deployment is deleted, the traces and the tracing project are not deleted. However, the API will be deleted when the deployment is deleted.
|
||||
|
||||
@@ -50,13 +50,22 @@ Runs in a LangGraph Server may be retried for specific failures (currently only
|
||||
|
||||
This section describes various features of the data plane.
|
||||
|
||||
### Data Region
|
||||
|
||||
!!! info "Only for Cloud SaaS"
|
||||
Data regions are only applicable for [Cloud SaaS](../concepts/langgraph_cloud.md) deployments.
|
||||
|
||||
Deployments can be created in 2 data regions: US and EU
|
||||
|
||||
The data region for a deployment is implied by the data region of the LangSmith organization where the deployment is created. Deployments and the underlying database for the deployments cannot be migrated between data regions.
|
||||
|
||||
### Autoscaling
|
||||
|
||||
[`Production` type](../concepts/langgraph_control_plane.md#deployment-types) deployments automatically scale up to 10 containers. Scaling is based on 3 metrics:
|
||||
|
||||
1. CPU utilization
|
||||
1. Memory utilization
|
||||
1. Number of pending (in progress) [runs](../cloud/concepts/runs.md)
|
||||
1. Number of pending (in progress) [runs](./assistants.md#execution)
|
||||
|
||||
For CPU utilization, the autoscaler targets 75% utilization. This means the autoscaler will scale the number of containers up or down to ensure that CPU utilization is at or near 75%. For memory utilization, the autoscaler targets 75% utilization as well.
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ Develop, deploy, scale, and manage agents with **LangGraph Platform** — the pu
|
||||
|
||||
LangGraph Platform makes it easy to get your agent running in production — whether it’s built with LangGraph or another framework — so you can focus on your app logic, not infrastructure. Deploy with one click to get a live endpoint, and use our robust APIs and built-in task queues to handle production scale.
|
||||
|
||||
- **[Streaming Support](../cloud/concepts/streaming.md)**: As agents grow more sophisticated, they often benefit from streaming both token outputs and intermediate states back to the user. Without this, users are left waiting for potentially long operations with no feedback. LangGraph Server provides multiple streaming modes optimized for various application needs.
|
||||
- **[Streaming Support](../cloud/how-tos/streaming.md)**: As agents grow more sophisticated, they often benefit from streaming both token outputs and intermediate states back to the user. Without this, users are left waiting for potentially long operations with no feedback. LangGraph Server provides multiple streaming modes optimized for various application needs.
|
||||
|
||||
- **[Background Runs](../cloud/how-tos/background_run.md)**: For agents that take longer to process (e.g., hours), maintaining an open connection can be impractical. The LangGraph Server supports launching agent runs in the background and provides both polling endpoints and webhooks to monitor run status effectively.
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user