Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed648f87bf |
@@ -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 powerful low-level orchestration framework for building, managing, and deploying long-running, stateful agents.
|
||||
|
||||
@@ -81,4 +80,4 @@ While LangGraph can be used standalone, it also integrates seamlessly with any L
|
||||
|
||||
## 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.
|
||||
@@ -39,7 +39,6 @@ REDIRECT_MAP = {
|
||||
"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",
|
||||
# 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",
|
||||
@@ -68,8 +67,6 @@ REDIRECT_MAP = {
|
||||
"cloud/concepts/api.md": "concepts/langgraph_server.md",
|
||||
"cloud/concepts/cloud.md": "concepts/langgraph_cloud.md",
|
||||
"cloud/faq/studio.md": "concepts/langgraph_studio.md#studio-faqs",
|
||||
"cloud/how-tos/human_in_the_loop_edit_state.md": "cloud/how-tos/add-human-in-the-loop.md",
|
||||
"cloud/how-tos/human_in_the_loop_user_input.md": "cloud/how-tos/add-human-in-the-loop.md",
|
||||
# cloud streaming redirects
|
||||
"cloud/how-tos/stream_values.md": "cloud/how-tos/streaming.md#stream-graph-state",
|
||||
"cloud/how-tos/stream_updates.md": "cloud/how-tos/streaming.md#stream-graph-state",
|
||||
@@ -97,9 +94,7 @@ REDIRECT_MAP = {
|
||||
"tutorials/introduction.ipynb": "concepts/why-langgraph.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",
|
||||
# assistant redirects
|
||||
"cloud/how-tos/assistant_versioning.md": "cloud/how-tos/configuration_cloud.md"
|
||||
"concepts/self_hosted.md": "concepts/langgraph_self_hosted_data_plane.md"
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import os
|
||||
import json
|
||||
import click
|
||||
import nbformat
|
||||
import re
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
NOTEBOOK_DIRS = ("docs/how-tos","docs/tutorials")
|
||||
@@ -89,10 +88,12 @@ def has_blocklisted_command(code: str, metadata: dict) -> bool:
|
||||
return True
|
||||
return False
|
||||
|
||||
MERMAID_PATTERN = re.compile(r'display\(Image\((\w+)\.get_graph\(\)\.draw_mermaid_png\(\)\)\)')
|
||||
|
||||
def remove_mermaid(code: str) -> str:
|
||||
return MERMAID_PATTERN.sub('print()', code)
|
||||
return code.replace(
|
||||
"display(Image(graph.get_graph().draw_mermaid_png()))",
|
||||
# replace with a dummy statement
|
||||
"print()"
|
||||
)
|
||||
|
||||
|
||||
def add_vcr_to_notebook(
|
||||
@@ -107,8 +108,6 @@ def add_vcr_to_notebook(
|
||||
continue
|
||||
|
||||
lines = cell.source.splitlines()
|
||||
# remove the special tag for hidden cells
|
||||
lines = [line for line in lines if not line.strip().startswith("# hide-cell")]
|
||||
# skip if empty cell
|
||||
if not lines:
|
||||
continue
|
||||
@@ -196,11 +195,6 @@ def remove_mermaid_from_notebook(notebook: nbformat.NotebookNode) -> nbformat.No
|
||||
continue
|
||||
|
||||
cell.source = remove_mermaid(cell.source)
|
||||
|
||||
# skip the cell entirely if it contains PYPPETEER
|
||||
if "PYPPETEER" in cell.source:
|
||||
cell.source = ""
|
||||
|
||||
return notebook
|
||||
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
eNqdVW1wU1UabmEFHGVgRBwFZoiZbp3B3uTefKchs7RJbUuTJk0aS1E3ntx7knub++X9SHOL9aOorCuLcx0Bv6ZTaWhoCQVsRVw+FnRcVt0iA/7Y7q44O86qCzviOs6wjgx48lFpB355fiT33vc973nO8zzvOQP5DJRkRuCrCwyvQAmQCnqR9YG8BB9Toaw8M8JBhRaoXDgU7RxWJWZ6Da0oolxvNgORMQki5AFjIgXOnCHMJA0UM3oWWVgqk0sIlDb97CYjB2UZpKBsrH9ok5EU0Eq8Yqw3RqAsCjxlYHhDqwJYBvAmY51REliIgrImK5Az9tfNnkEz1xNUGUrG/kfqjJxAQRZ9SIkKZjMRGMfwxTRZkSDgjPVJwMqwP09DQKG9vpijBVnRx+ei3w9IEqLpkCcFiuFT+kSqjxHrDBRMskCBdYY+WaHGEA4elhjSx9IQihjCnIEj5bn6ASCKLEOCYtzcIwt8oYIbUzQR3hgeK24AQ7Twij4ZQlAaWs1hDZHNGwiT027CD2QxWQEMzyL2MBYgVCNiKX5kdkAEZBoVwSpC6iPlyeOzcwRZ3x0EZCg6pySQSFrfDSTOYZuY/V1SeYXhoJ73hW9crhK8vpzVRBAm98E5hWWNJ/XdJeLfmTMZKpKGkQKqob+Jj8/ww0I+pdD6MGG17pFKnpDh5hE0TVHlgRxSBP71L/mKh3aF2makPF91d86P1NGPdapIIsJqCALNYMEtdgNO1Fsc9YTF0BzsLPgqy3TeVIaDnRLg5SSSomlG/DxJq3waUmO+mwp+rCg42k0RPvIiBrOiIEOsgkovbMAi5ebBWv0TZY9hgpQCPNNXWlYfLYqJmoXhJythURKKJdHiGCfrwzaHc7wSmeF5DO0Lxwgcw4ljWeTTjKBhqlj2OIaMlGFIiJVkG7Y58T9mMQkxwzIcg+gt/VYaGjnBiqNx+MYMRUhD1PujhB0vj+OzcyTIIcBFhNcrWdxoHL151s/VbO7SmIsJyQxnYRq2cPLhG+OVGrtwuZCdScYYSp+uQS/xpCWRpPBEMknaYNJGuUmQxF1WEpBOp4NwgMS7iB2GRFWKAouCpCCeSHSEKZo+XceBbLH3vFbCbnWgvXrQKUSyKgWjasIvFDchewyiBFkBUPvJJIbK0hAre1LP+7vbG4KtvrEoAukThDQDX/p79d3xOJmMJzivyxlIt1gkuSsjKfHHmFjEYetu6BL7iFggzYuhcLSFTHUSrYkOfwojnEgut9vutGCECTcR6Pxq64qxqbTUYyU6/f6WYEtDlHKGm/0tCda+EfRGKNN6SyAcCIiNYXd0fXscpuIdgq+5vc8dkNJUs7OJDHDxHq1PbqcbYyGptylhstjYNhMObSQe6tmQoF0dNt4aCnQEu9EWgUJ7zR4DMjGDSPdWWglDrYSVG8k+00geA1Uixmuae3h6DC3oSgjxrOYxRIsMQ/QPOBhlFOhtF3g4/TIiRs0wlDdtaaUjXfZGXom6ep0N7bFGNd7b2EbEOwMC0+fLapSwXo1beJ8Ym8WMy+3E8Ao5DtzmKpnzOvRfiOrQBmz2yYCFxPLdl+cFmWeSyZEo6ioo6WMkK6gUugckOOJ7AIs0dOuTbiuEdop04kk77iRBAmvqihyYqfbzOZIrXiJ5wCLjZUh9grZ6jfU2m9XoMXDA63LYcLx0Qz49UjQqn/qgenz1C4uqSmP+1sjJbefwpce+un/rJ1Njo2+tPHvb2Yxb/NeJ6l/Ry92na+4R1/55cKhp71WPdNbxn3l37Fz15dQm7ZI2uOzeGodkzn2z/OLJV7+/eu6rq59d3LKQu23lhdXxwfNP/v9vx/ep2pOH9/671rLn6oLXPimsDG62nmjJPPf2wg+WxiaM5kNQe+i/tY8OLQjk1G3nrtS2LZx85ZtDR/6hvJ7vfmLX5WdWDPzzhf3PL21cG/qc/tTxbc3lxBF3aMGiBtVweeev783dt+y+yLNPPb0dvtu9LrjjZPv5JXu+3u46U/Vex6KaO7cV1g4Pjb/vTO/Ykrl9zcZ3hon713W3FRLff3jpi1vdv1214sIb+fqjQx/dhT13/LuhedNf1yIDvHnK/7tTo8//b80byuCCU8vP9b7yxM7PpiRJe2p5tqfVctDy8eO9tY+MxhYfaFz2YHD9PmXJ1HsP93SRmyfnvRT78jdTV4KbTv/h0I+rf7jlzIpVvqh7yeILF098/NqW7OC1jUeUtffsqL4S+jRMDLx46cy6vQ9YP996+5/mvR9+m/Z8e/qLx1379v7+LdNHK74rbP+Q+GFxVdW1a/Ormo729zfPr6r6CVs9pnc=
|
||||
@@ -1 +0,0 @@
|
||||
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
|
||||
@@ -1 +1 @@
|
||||
eNqNVWtsFFUU5hUxVIkkUkJEHUZsVXp3d3Zm225BcPuwLaQP2w0UDMG7d+7uDp2dO8y9U7pFTKxIDAZ1fpg0hqjIdhc3DbSBhBBApMEoURKD4cei+PrjDzUaNMZ3vbPd7cOWxySTzNzz+s75zjm3P9ODLaoRY+6QZjBsQcT4D3X6MxbeaWPK9qYTmMWJmmpv6wwfti0t93CcMZPWeL3Q1DzQYHGLmBryIJLw9kjeBKYUxjBNRYiazP2wW0zA3u2MdGODijWSz69UiEUVsebp3aJFdCzWiDbFllghIsJBGIwfxLWVQnN5QoiQiLinYkIPUqpRxqNOU27ShFoSWSm0aggLjAgJjJmQJLZHaCK7BAQNoVmIY910z7hchcn1U53OCL4rDlk5FRJJwYAJzHW3cdRExTqXIR3aKgYyCABKDAMzoEPGCyXuycQxVHk1v5yzJBUnlDkjMyp0DCKETQawgYiqGTHneKxPMysEFUddLxVCH2VqFrl+80Q42W6MTQB1rQcf7wU8c83QefkA0xKY2Mx5t7UtvL2xeVNDa3rctTMMTVPXEHTNvTs4xKFCXoAlTTxTnHWzB5wPgzknQ0Ww3vYkZ90QfB4l6PENTw2tQ447beblp6cKTIi6uR9Q6CgnPW58dKoOoc5gC0RtndNcQgvFnUFoJSqVaVlatuEm6mTq2meGKwgnw8keSfIER6Y5pkkDOYNRqFM8MsHEhEnW7/PLwFcJfNLJaa4xs5IAER7BOeQ7Wiygjo0YizuH/XLlEQtTkw8KfiHNzZhN+1OcUfzJR5lCd7/TtnGyH5an6jm7ztmwzSmWZKEFJgUeOiD4fDUBqUYOCo0t4aG6QpjwrDyNhC1o0CjnqqHYPBkUt41urGbrZm2YXNlkxhaPr2sJjQHNMG3eC/mJBPkjJ6343Cf32C31LZzgNXJjF23KbsOGYuaccPMFvgCQ5HAx66258tmseV/PgDhYnY+2+tb6kxALNuW3Y3NjiOJs5oXlWESXUm5YignNSVwpORgM3sLvzRAJs1n+j9Dx1FfdRHMqlePawk21b4gnWwANNNU5w7+3+ySprl1BXQ0762NdgdoqTa3WukIbOk5RZmmIu3Qb2SQWAxQjfqOwpJOr4NeEu4Qel6WAXMnRrBE0A+l803bakXriQqVrBNPCOoHqqUmgxIpBQ+vLj4kLILdKDlbKAVWOAByJqkAJVleBYNAvgYjfX60q1VKVolYe7tGgk5U8khAjJKbjYygKEERxDMaH2cnUb2kNtTTXDXWBDhIhnJAw5MQZxMDpTmzx/eFkkU5sla9tC6frngQdoS3OiaCMsT/ir47igKJEq6pAw+aO4eJYT4xtyt35+fvx+bRbEiN24d8HX75zTv6Zz9+xMfWV0Mb50j0vjm0YoJu2ZsVTjy564MwjpUuc18rQtqo1x6NlkW2/nfu4tGR/ZuFPf/WeWXJ1weK+3J8fXFcuX3v9Wfvaj5eie/4xn0uOhb5fNvz26bmxkobR0vO136xdFkotvTv1zKHv9i5fcWHFjtHolYPKaP2RlsX7/audZufeLz7rHQj/ffDiuhVrLbCv9tX0Unr+vu7+gfs3/7Kp4YDVmv7woYE3wUvXvy65A85b2HSxGyxY/8TPZ8WGBY2BWIk99vmBTy+3X1m9q2meYUdX7vt9/7rSu676/zg3+u37Jft+fWvnla8uXe994725bpLz5ygnFnmf4t//AT+mUOg=
|
||||
eNqNVVtMHFUYXnoleKnGSL01nG4qJHVnd2Z2C+z6oLBQ2yIXYZtKUcnZmcPOwMyc6cxZ2uVmhPqk0M5TE/WpLLtmxQKxalP1SUiqVmyaNkqjyIsxhgRjfCs2eGbZ5VJu3aeZ8//f/33//51/tjfZjgxTxlrOsKwRZECB0BfT6k0a6FQUmeRsQkVEwmK8rrYhNBg15KnnJUJ0M+DxQF12Q41IBtZlwS1g1dPOeVRkmjCCzHgYi7E7ObmdThWeaSa4DWmmMwA4lve5gDObRU+aOp0GVhB9ckZNZDhpVMBUikbsI0neD44WqSCMw85uF1jOhaYpm4TS3wc4IoNyHF4EBRUYFZEb1MgCAgQDFSECYji6HxzBp4EANXAUSEjR7TMaF2HspdUk6wg6LUFSZAI1BjSoIpr/pt0OFpFiR4U0I+NlDjEm1jREGAUSOkVnd1JCUKSjPheXsEmssTXDG4GCgHTCIE3AoqxFrE8jHbLuAiJqsWukBLte2h0r1YaQzkBFbkeJRZQ1CnVdkQVoxz2tlHs4o5khMR2tDafs1hhqgUasL8uyOjx1Meq1Bli31+fmR88wdMKyplCzaB9UUkJPx79aGdCh0EbrMJl7ZCUWwZdW5mDTGqqGQm3DqpLQECRrCBpqse+zledGVCOyiqxksG4tXSa4TOd1c5zbP7aqsBnTBGuoBSomGlsa8hIkxbO8l2GLGZa7lJ2SgrQIkaxBvtj3sYFMne4A6kvQkiRq9sapI+j6tWTm1l6srcq6Oe3YG6+g7ljfnECiC/A8OEZvFa1/CPBcgPUHOA68Uh0aDmZoQuuaMRYyoGa2UEMqs+YnBSmqtSExFVzX9innclsG5VdkVSZMZmWpWfarFfexLDtVuGmmgVQ6NZsx7vX7/VvUpZNBxLps90eHx/B8KNMle3J9HlnTo/QKprc/oyphq6K6Dm6Zv6wtiyl8AMwGCrmTU0XroXGUrJE4VJpme2Hr/GWJGUzRg2A2lgjWg983vkWiA5tkrhzcYjbYNHtDPamM84wsWl/T52aWa2wMHe84Vl7devzVGkmr9tbprWYJGmyXoZXi3ByIYBxR0EjwMBOEgoSYhvQKWcmKxpqy6qPB4deZehzG9C6FIL1zGtZQogEZdDWtlKDgqEg/dgZKUHh9WaN12c8W86XFLB+GXlQi8j6m8kT9aHaZlpYlbn8p038479CVNejRRE5OwXu5jvRvu3iurGr85YffXfj+/L+hOyc95huP7ZmdftLiJ8d/u6TWFz09ytz7++bEzomGmfnpzvM7r+Y+usv/Bfr87dLrt7u6uj6Y++mvy1Xeu9/27Hgo0H9lqG684HHXgbmSmcrAa7V5+bsGfunruzH/3NgVZi4POm9ddM8mDs4+49pRWFmve36Ihv5p+6R5oC8y+d35ghsk3P5fS/9e76niP12tgZvj3O73n63qvTf/yPBIfkVMeaIDbtt9ZOz2R7k9Z2cih/NyB4zfm65d8Py6bV9/Y9Mfew7k/Hwrv3dPkRIsH9nXMF9S91Z3ecHCi5N3Pwz9eLVnu8OxsLDdIZY/lWrOcTj+B1mCCwM=
|
||||
@@ -1 +1 @@
|
||||
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
|
||||
@@ -1 +1 @@
|
||||
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
|
||||
@@ -1 +0,0 @@
|
||||
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
|
||||
@@ -1 +0,0 @@
|
||||
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
|
||||
@@ -1 +0,0 @@
|
||||
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
|
||||
@@ -0,0 +1 @@
|
||||
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
|
||||
@@ -0,0 +1 @@
|
||||
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
|
||||
@@ -0,0 +1 @@
|
||||
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
|
||||
@@ -1 +0,0 @@
|
||||
eNqNVX1sE2UYLxkYQlyiEf5QEzgbBon0be96t3YdYFy2AYPsg7WCfDnf3r1tj13vPe59b6GDiU6UEIx6MQrGj8DWtVgX2AIJjA9jIhBiQCVgTBNi0BiNGogxkohR53tduw+3AfdXe+/z+z2/3/M873Pd2Q5kEhXrM/pVnSITypT9IXZ31kTbLETo7kwS0QRW0i3N4UivZar5igSlBqn2+aCheqFOEyY2VNkr46SvQ/AlESEwjkg6ipVU/q0d7iTc3kZxO9KJu1rg/ZLHXQpxV2/a4TaxhtzVbosg0+1xy5iJ0Cl7sd5UKeIgRxLYpNxWRsDBKLYoJydUucDWtYVRYQVpLFrWoKUgIIIgIFjXEQUapEw9o6QoaTBf1DJZHt7Ld2UTCCrM9Leuh9MJTKg9OMnIUSjLyKAA6TJWVD1uH4t3qoaHU1DM4fVwnYQqOdnJVKiXnWtHyABQUzvQse2AUKjqGnMJqJpETLT9UVNzpG1lw7r6pswItT0ADUNTZejAfVuZ6P6id0BTBpp8nHMqBFjZdGqfqCmJ9bWkWHN0jvdKIS8/MD61BpnujFE4Pz3+wIByO+MBxcbbmRHwkfExmNh9jVBuDk+ghKacsPugmQxIE1yalu4YtbO1LZPTFQ/H0oleQfCGBicQk5Qu230xqBE0ONqJUUjOz/tFwAcAL5yYQI2omQIyZhnsQ/yRUgE1pMdpwu4VpOBhExGDzTN6OcNg1CLdadZRdOlitjiEPc1rxubh0XQd6659thHrHk7wc40wxbHUlZwgVYtSNV/FrWyM9NcW00Sm7NNgxIQ6ibFe1ZeGJysnLL0dKbnaKQcmv2jMMZtTpKlJlQJVNyw2C4WLAwqv7Iyfd578k/eMN1GS1cjJXcIsug8MQdQ+7vgFfCUQ/JGS6435xVOh2VxPkthXVci25N7xYxKLmMX3g5leonsqeHGHldSlpWlLMRo5pisthkKhe/BOqyi4Mc9NhfxfQ0esL7xL5PhWjkRzd42etkK5omigKvYZ9ruNF4TaFhGHhTXtJCAlOyuDIX8y1h4eItRUZUbpDLLBti8gSGaLn6bsvIdtc2cJLReFSjHA1CzlVF3W2O4NW9E67EglSznDRBqGytCYUGzGoa52Fq6JIyC/UAwFxEpFjAIUjSlAClUFQSjkF0DU769SpCohKCmB3g4V2jnBK3BxjOMaOirHgAzlBAIjl9nO1m1oqmlsqO1/FrTiKGYNiUDWOB3rKBNGJtsfdk7WsKWwtW2iTO0K0FqzwT4eEhGE/pAUUIJRKcr2Sv361oHStR69tmln5xc+Yy9lnJLo8fMz5i3YN9tVeMqUN2rWnOPn7h5evfOnuaeesS/X/3ajYdbaudv6zv2QvxBrO5XyfWX9vGv/ls0ty8ovBUV+6ez5D/YPtf96/vLVz06eOT184PrNgYZv/F3L37k1S7rYca5rzrvJPQefyJ4MesjzgwsPwg+WreBv9ID5H1/9pef7ut2tlRZ5LrivImHsndnw6Zlbr/d2X/3khYfKvvxn3Z0bb/9xePOC6y+mnwpz6OvTFbuGeuZtOXTslbWz1p69PWfxHsG99/eh/g3dj/8588dguUfVxI1NMx94c+eSikf+dl1orDFnv7r6tcc+/+69fM3BwVVL1Kej/D6x/ItT8zftv7J61bV/y6+t/PDKX8OeeXduSreX7SpzuYaHy1zvH15VZs1wuf4DgoVD8g==
|
||||
@@ -89,4 +89,4 @@ LangGraph Studio Web is a specialized UI that you can connect to LangGraph API s
|
||||
|
||||
## 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.
|
||||
Once your LangGraph app is running locally, you can deploy it using LangGraph Cloud or self-hosted options. Refer to the [deployment options guide](../tutorials/deployment.md) for detailed instructions on all supported deployment models.
|
||||
|
||||
@@ -106,7 +106,7 @@ for chunk in agent.stream(
|
||||
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.
|
||||
1. The [`interrupt` function][langgraph.types.interrupt] is used in conjunction with the [`Command`](../reference/types.md#langgraph.types.Command) object to resume the graph with a value provided by the human.
|
||||
|
||||
## Using with Agent Inbox
|
||||
|
||||
|
||||
@@ -294,7 +294,7 @@ agent.invoke(
|
||||
)
|
||||
```
|
||||
|
||||
For more details, see [how to update state from tools](../how-tos/tool-calling.ipynb#update).
|
||||
For more details, see [how to update state from tools](../how-tos/update-state-from-tools.ipynb).
|
||||
|
||||
## Long-term memory
|
||||
|
||||
@@ -302,7 +302,7 @@ Use long-term memory to store user-specific or application-specific data across
|
||||
|
||||
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.
|
||||
1. [Configure a store](../how-tos/cross-thread-persistence.ipynb) 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 }
|
||||
|
||||
@@ -23,182 +23,29 @@ Compatible models can be found in the [LangChain integrations directory](https:/
|
||||
|
||||
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
|
||||
)
|
||||
```
|
||||
```python
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
|
||||
agent = create_react_agent(
|
||||
# highlight-next-line
|
||||
model="anthropic:claude-3-7-sonnet-latest",
|
||||
# other parameters
|
||||
)
|
||||
```
|
||||
|
||||
## Using `init_chat_model`
|
||||
|
||||
The [`init_chat_model`](https://python.langchain.com/docs/how_to/chat_models_universal_init/) utility simplifies model initialization with configurable parameters:
|
||||
|
||||
=== "OpenAI"
|
||||
|
||||
```
|
||||
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
|
||||
)
|
||||
```
|
||||
```python
|
||||
from langchain.chat_models import init_chat_model
|
||||
|
||||
model = init_chat_model(
|
||||
"anthropic:claude-3-7-sonnet-latest",
|
||||
temperature=0,
|
||||
max_tokens=2048
|
||||
)
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -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](./deployment.md#launch-langgraph-server-locally) or deploy your agent on [LangGraph Cloud](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):
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@ There are many situations in which it is useful to run an assistant on a schedul
|
||||
For example, say that you're building an assistant that runs daily and sends an email summary
|
||||
of the day's news. You could use a cron job to run the assistant every day at 8:00 PM.
|
||||
|
||||
LangGraph Platform supports cron jobs, which run on a user-defined schedule. The user specifies a schedule, an assistant, and some input. After that, on the specified schedule, the server will:
|
||||
LangGraph Cloud supports cron jobs, which run on a user-defined schedule. The user specifies a schedule, an assistant, and some input. After that, on the specified schedule, the server will:
|
||||
|
||||
- Create a new thread with the specified assistant
|
||||
- Send the specified input to that thread
|
||||
|
||||
Note that this sends the same input to the thread every time. See the [how-to guide](../../cloud/how-tos/cron_jobs.md) for creating cron jobs.
|
||||
|
||||
The LangGraph Platform API provides several endpoints for creating and managing cron jobs. See the [API reference](../../cloud/reference/api/api_ref.html#tag/runscreate/POST/threads/{thread_id}/runs/crons) for more details.
|
||||
The LangGraph Cloud API provides several endpoints for creating and managing cron jobs. See the [API reference](../../cloud/reference/api/api_ref.html#tag/runscreate/POST/threads/{thread_id}/runs/crons) for more details.
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
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.
|
||||
The LangGraph Cloud 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,12 +1,11 @@
|
||||
# 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 contains the accumulated state of a sequence of [runs](./runs.md). If a run is executed on a thread, then 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.
|
||||
The state of a thread at a particular point in time is called a [checkpoint](../../concepts/persistence.md#checkpoints). Checkpoints 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).
|
||||
|
||||
* 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.
|
||||
The LangGraph Cloud 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.
|
||||
@@ -1,7 +1,7 @@
|
||||
# Webhooks
|
||||
|
||||
Webhooks enable event-driven communication from your LangGraph Platform application to external services. For example, you may want to issue an update to a separate service once an API call to LangGraph Platform has finished running.
|
||||
Webhooks enable event-driven communication from your LangGraph Cloud application to external services. For example, you may want to issue an update to a separate service once an API call to LangGraph Cloud has finished running.
|
||||
|
||||
Many LangGraph Platform endpoints accept a `webhook` parameter. If this parameter is specified by a an endpoint that can accept POST requests, LangGraph Platform will send a request at the completion of a run.
|
||||
Many LangGraph Cloud endpoints accept a `webhook` parameter. If this parameter is specified by a an endpoint that can accept POST requests, LangGraph Cloud will send a request at the completion of a run.
|
||||
|
||||
See the corresponding [how-to guide](../../cloud/how-tos/webhooks.md) for more detail.
|
||||
@@ -4,14 +4,14 @@ Before deploying, review the [conceptual guide for the Cloud SaaS](../../concept
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. LangGraph Platform applications are deployed from GitHub repositories. Configure and upload a LangGraph Platform application to a GitHub repository in order to deploy it to LangGraph Platform.
|
||||
1. [Verify that the LangGraph API runs locally](../../tutorials/langgraph-platform/local-server.md). If the API does not run successfully (i.e. `langgraph dev`), deploying to LangGraph Platform will fail as well.
|
||||
1. LangGraph Cloud applications are deployed from GitHub repositories. Configure and upload a LangGraph Cloud application to a GitHub repository in order to deploy it to LangGraph Cloud.
|
||||
1. [Verify that the LangGraph API runs locally](../../tutorials/langgraph-platform/local-server.md). If the API does not run successfully (i.e. `langgraph dev`), deploying to LangGraph Cloud will fail as well.
|
||||
|
||||
## Create New Deployment
|
||||
|
||||
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. In the left-hand navigation panel, select `LangGraph Platform`. The `LangGraph Platform` view contains a list of existing LangGraph Cloud deployments.
|
||||
1. In the top-right corner, select `+ New Deployment` to create a new deployment.
|
||||
1. In the `Create New Deployment` panel, fill out the required fields.
|
||||
1. `Deployment details`
|
||||
@@ -38,7 +38,7 @@ When [creating a new deployment](#create-new-deployment), a new revision is crea
|
||||
|
||||
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. In the left-hand navigation panel, select `LangGraph Platform`. The `LangGraph Platform` view contains a list of existing LangGraph Cloud deployments.
|
||||
1. Select an existing deployment to create a new revision for.
|
||||
1. In the `Deployment` view, in the top-right corner, select `+ New Revision`.
|
||||
1. In the `New Revision` modal, fill out the required fields.
|
||||
@@ -79,7 +79,7 @@ Starting from the `LangGraph Platform` view...
|
||||
|
||||
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. In the left-hand navigation panel, select `LangGraph Platform`. The `LangGraph Platform` view contains a list of existing LangGraph Cloud deployments.
|
||||
1. Select the menu icon (three dots) on the right-hand side of the row for the desired deployment and select `Delete`.
|
||||
1. A `Confirmation` modal will appear. Select `Delete`.
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 400 KiB |
|
After Width: | Height: | Size: 461 KiB |
|
After Width: | Height: | Size: 642 KiB |
|
After Width: | Height: | Size: 288 KiB |
|
After Width: | Height: | Size: 418 KiB |
|
After Width: | Height: | Size: 401 KiB |
|
After Width: | Height: | Size: 453 KiB |
|
Before Width: | Height: | Size: 84 KiB |
@@ -1,11 +1,11 @@
|
||||
# How to Set Up a LangGraph Application with requirements.txt
|
||||
|
||||
A LangGraph application must be configured with a [LangGraph configuration file](../reference/cli.md#configuration-file) in order to be deployed to LangGraph Platform (or to be self-hosted). This how-to guide discusses the basic steps to setup a LangGraph application for deployment using `requirements.txt` to specify project dependencies.
|
||||
A LangGraph application must be configured with a [LangGraph configuration file](../reference/cli.md#configuration-file) in order to be deployed to LangGraph Cloud (or to be self-hosted). This how-to guide discusses the basic steps to setup a LangGraph application for deployment using `requirements.txt` to specify project dependencies.
|
||||
|
||||
This walkthrough is based on [this repository](https://github.com/langchain-ai/langgraph-example), which you can play around with to learn more about how to setup your LangGraph application for deployment.
|
||||
|
||||
!!! tip "Setup with pyproject.toml"
|
||||
If you prefer using poetry for dependency management, check out [this how-to guide](./setup_pyproject.md) on using `pyproject.toml` for LangGraph Platform.
|
||||
If you prefer using poetry for dependency management, check out [this how-to guide](./setup_pyproject.md) on using `pyproject.toml` for LangGraph Cloud.
|
||||
|
||||
!!! tip "Setup with a Monorepo"
|
||||
If you are interested in deploying a graph located inside a monorepo, take a look at [this repository](https://github.com/langchain-ai/langgraph-example-monorepo) for an example of how to do so.
|
||||
@@ -130,7 +130,7 @@ graph = workflow.compile()
|
||||
```
|
||||
|
||||
!!! warning "Assign `CompiledGraph` to Variable"
|
||||
The build process for LangGraph Platform requires that the `CompiledGraph` object be assigned to a variable at the top-level of a Python module (alternatively, you can provide [a function that creates a graph](./graph_rebuild.md)).
|
||||
The build process for LangGraph Cloud requires that the `CompiledGraph` object be assigned to a variable at the top-level of a Python module (alternatively, you can provide [a function that creates a graph](./graph_rebuild.md)).
|
||||
|
||||
Example file directory:
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# How to Set Up a LangGraph.js Application
|
||||
|
||||
A [LangGraph.js](https://langchain-ai.github.io/langgraphjs/) application must be configured with a [LangGraph configuration file](../reference/cli.md#configuration-file) in order to be deployed to LangGraph Platform (or to be self-hosted). This how-to guide discusses the basic steps to setup a LangGraph.js application for deployment using `package.json` to specify project dependencies.
|
||||
A [LangGraph.js](https://langchain-ai.github.io/langgraphjs/) application must be configured with a [LangGraph configuration file](../reference/cli.md#configuration-file) in order to be deployed to LangGraph Cloud (or to be self-hosted). This how-to guide discusses the basic steps to setup a LangGraph.js application for deployment using `package.json` to specify project dependencies.
|
||||
|
||||
This walkthrough is based on [this repository](https://github.com/langchain-ai/langgraphjs-studio-starter), which you can play around with to learn more about how to setup your LangGraph application for deployment.
|
||||
|
||||
@@ -157,7 +157,7 @@ export const graph = workflow.compile();
|
||||
|
||||
!!! info "Assign `CompiledGraph` to Variable"
|
||||
|
||||
The build process for LangGraph Platform requires that the `CompiledGraph` object be assigned to a variable at the top-level of a JavaScript module (alternatively, you can provide [a function that creates a graph](./graph_rebuild.md)).
|
||||
The build process for LangGraph Cloud requires that the `CompiledGraph` object be assigned to a variable at the top-level of a JavaScript module (alternatively, you can provide [a function that creates a graph](./graph_rebuild.md)).
|
||||
|
||||
Example file directory:
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# How to Set Up a LangGraph Application with pyproject.toml
|
||||
|
||||
A LangGraph application must be configured with a [LangGraph configuration file](../reference/cli.md#configuration-file) in order to be deployed to LangGraph Platform (or to be self-hosted). This how-to guide discusses the basic steps to setup a LangGraph application for deployment using `pyproject.toml` to define your package's dependencies.
|
||||
A LangGraph application must be configured with a [LangGraph configuration file](../reference/cli.md#configuration-file) in order to be deployed to LangGraph Cloud (or to be self-hosted). This how-to guide discusses the basic steps to setup a LangGraph application for deployment using `pyproject.toml` to define your package's dependencies.
|
||||
|
||||
This walkthrough is based on [this repository](https://github.com/langchain-ai/langgraph-example-pyproject), which you can play around with to learn more about how to setup your LangGraph application for deployment.
|
||||
|
||||
@@ -59,7 +59,7 @@ Example `pyproject.toml` file:
|
||||
[tool.poetry]
|
||||
name = "my-agent"
|
||||
version = "0.0.1"
|
||||
description = "An excellent agent build for LangGraph Platform."
|
||||
description = "An excellent agent build for LangGraph cloud."
|
||||
authors = ["Polly the parrot <1223+polly@users.noreply.github.com>"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
@@ -138,7 +138,7 @@ graph = workflow.compile()
|
||||
```
|
||||
|
||||
!!! warning "Assign `CompiledGraph` to Variable"
|
||||
The build process for LangGraph Platform requires that the `CompiledGraph` object be assigned to a variable at the top-level of a Python module.
|
||||
The build process for LangGraph Cloud requires that the `CompiledGraph` object be assigned to a variable at the top-level of a Python module.
|
||||
|
||||
Example file directory:
|
||||
|
||||
|
||||
@@ -1,342 +0,0 @@
|
||||
# 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.
|
||||
|
||||
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:**
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
# highlight-next-line
|
||||
from langgraph_sdk.schema import Command
|
||||
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 interrupt is hit.
|
||||
result = await client.runs.wait(
|
||||
thread_id,
|
||||
assistant_id,
|
||||
input={"some_text": "original text"} # (1)!
|
||||
)
|
||||
|
||||
print(result['__interrupt__']) # (2)!
|
||||
# > [
|
||||
# > {
|
||||
# > 'value': {'text_to_revise': 'original text'},
|
||||
# > 'resumable': True,
|
||||
# > 'ns': ['human_node:fc722478-2f21-0578-c572-d9fc4dd07c3b'],
|
||||
# > 'when': 'during'
|
||||
# > }
|
||||
# > ]
|
||||
|
||||
|
||||
# Resume the graph
|
||||
print(await client.runs.wait(
|
||||
thread_id,
|
||||
assistant_id,
|
||||
# highlight-next-line
|
||||
command=Command(resume="Edited text") # (3)!
|
||||
))
|
||||
# > {'some_text': 'Edited text'}
|
||||
```
|
||||
|
||||
1. The graph is invoked with some initial state.
|
||||
2. When the graph hits the interrupt, it returns an interrupt object with the payload and metadata.
|
||||
3. The graph is resumed with a `Command(resume=...)`, injecting the human's input and continuing execution.
|
||||
|
||||
=== "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 interrupt is hit.
|
||||
const result = await client.runs.wait(
|
||||
threadID,
|
||||
assistantID,
|
||||
{ input: { "some_text": "original text" } } // (1)!
|
||||
);
|
||||
|
||||
console.log(result['__interrupt__']); // (2)!
|
||||
// > [
|
||||
// > {
|
||||
// > 'value': {'text_to_revise': 'original text'},
|
||||
// > 'resumable': True,
|
||||
// > 'ns': ['human_node:fc722478-2f21-0578-c572-d9fc4dd07c3b'],
|
||||
// > 'when': 'during'
|
||||
// > }
|
||||
// > ]
|
||||
|
||||
// Resume the graph
|
||||
console.log(await client.runs.wait(
|
||||
threadID,
|
||||
assistantID,
|
||||
// highlight-next-line
|
||||
{ command: { resume: "Edited text" }} // (3)!
|
||||
));
|
||||
// > {'some_text': 'Edited text'}
|
||||
```
|
||||
|
||||
1. The graph is invoked with some initial state.
|
||||
2. When the graph hits the interrupt, it returns an interrupt object with the payload and metadata.
|
||||
3. The graph is resumed with a `{ resume: ... }` command object, injecting the human's input and continuing execution.
|
||||
|
||||
=== "cURL"
|
||||
|
||||
Create a thread:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
Run the graph until the interrupt is hit.:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/wait \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": {\"some_text\": \"original text\"}
|
||||
}"
|
||||
```
|
||||
|
||||
Resume the graph:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/wait \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"command\": {
|
||||
\"resume\": \"Edited text\"
|
||||
}
|
||||
}"
|
||||
```
|
||||
|
||||
??? example "Extended example: using `interrupt`"
|
||||
|
||||
This is an example graph you can run in the LangGraph API server.
|
||||
See [LangGraph Platform quickstart](../quick_start.md) for more details.
|
||||
|
||||
```python
|
||||
from typing import TypedDict
|
||||
import uuid
|
||||
|
||||
from langgraph.checkpoint.memory import InMemorySaver
|
||||
from langgraph.constants import START
|
||||
from langgraph.graph import StateGraph
|
||||
# highlight-next-line
|
||||
from langgraph.types import interrupt, Command
|
||||
|
||||
class State(TypedDict):
|
||||
some_text: str
|
||||
|
||||
def human_node(state: State):
|
||||
# highlight-next-line
|
||||
value = interrupt( # (1)!
|
||||
{
|
||||
"text_to_revise": state["some_text"] # (2)!
|
||||
}
|
||||
)
|
||||
return {
|
||||
"some_text": value # (3)!
|
||||
}
|
||||
|
||||
|
||||
# Build the graph
|
||||
graph_builder = StateGraph(State)
|
||||
graph_builder.add_node("human_node", human_node)
|
||||
graph_builder.add_edge(START, "human_node")
|
||||
|
||||
graph = graph_builder.compile()
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
Once you have a running LangGraph API server, you can interact with it using
|
||||
[LangGraph SDK](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/)
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
# highlight-next-line
|
||||
from langgraph_sdk.schema import Command
|
||||
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 interrupt is hit.
|
||||
result = await client.runs.wait(
|
||||
thread_id,
|
||||
assistant_id,
|
||||
input={"some_text": "original text"} # (1)!
|
||||
)
|
||||
|
||||
print(result['__interrupt__']) # (2)!
|
||||
# > [
|
||||
# > {
|
||||
# > 'value': {'text_to_revise': 'original text'},
|
||||
# > 'resumable': True,
|
||||
# > 'ns': ['human_node:fc722478-2f21-0578-c572-d9fc4dd07c3b'],
|
||||
# > 'when': 'during'
|
||||
# > }
|
||||
# > ]
|
||||
|
||||
|
||||
# Resume the graph
|
||||
print(await client.runs.wait(
|
||||
thread_id,
|
||||
assistant_id,
|
||||
# highlight-next-line
|
||||
command=Command(resume="Edited text") # (3)!
|
||||
))
|
||||
# > {'some_text': 'Edited text'}
|
||||
```
|
||||
|
||||
1. The graph is invoked with some initial state.
|
||||
2. When the graph hits the interrupt, it returns an interrupt object with the payload and metadata.
|
||||
3. The graph is resumed with a `Command(resume=...)`, injecting the human's input and continuing execution.
|
||||
|
||||
=== "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 interrupt is hit.
|
||||
const result = await client.runs.wait(
|
||||
threadID,
|
||||
assistantID,
|
||||
{ input: { "some_text": "original text" } } // (1)!
|
||||
);
|
||||
|
||||
console.log(result['__interrupt__']); // (2)!
|
||||
// > [
|
||||
// > {
|
||||
// > 'value': {'text_to_revise': 'original text'},
|
||||
// > 'resumable': True,
|
||||
// > 'ns': ['human_node:fc722478-2f21-0578-c572-d9fc4dd07c3b'],
|
||||
// > 'when': 'during'
|
||||
// > }
|
||||
// > ]
|
||||
|
||||
// Resume the graph
|
||||
console.log(await client.runs.wait(
|
||||
threadID,
|
||||
assistantID,
|
||||
// highlight-next-line
|
||||
{ command: { resume: "Edited text" }} // (3)!
|
||||
));
|
||||
// > {'some_text': 'Edited text'}
|
||||
```
|
||||
|
||||
1. The graph is invoked with some initial state.
|
||||
2. When the graph hits the interrupt, it returns an interrupt object with the payload and metadata.
|
||||
3. The graph is resumed with a `{ resume: ... }` command object, injecting the human's input and continuing execution.
|
||||
|
||||
=== "cURL"
|
||||
|
||||
Create a thread:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
Run the graph until the interrupt is hit:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/wait \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": {\"some_text\": \"original text\"}
|
||||
}"
|
||||
```
|
||||
|
||||
Resume the graph:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/wait \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"command\": {
|
||||
\"resume\": \"Edited text\"
|
||||
}
|
||||
}"
|
||||
```
|
||||
|
||||
## 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.
|
||||
@@ -0,0 +1,152 @@
|
||||
# How to version Assistants
|
||||
|
||||
!!! info "Prerequisites"
|
||||
|
||||
- [Assistants Overview](../../concepts/assistants.md)
|
||||
- [How to create an Assistant](./configuration_cloud.md)
|
||||
|
||||
In this guide we will show you how to create, manage and use multiple versions of an assistant. If you have not already, please first see [this](./configuration_cloud.md) guide on creating an assistant. For this example, assume you have a graph with the following configuration schema:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
class Config(BaseModel):
|
||||
model_name: Literal["anthropic", "openai"] = "anthropic"
|
||||
system_prompt: str
|
||||
|
||||
builder = StateGraph(State, config_schema=Config)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
const ConfigAnnotation = Annotation.Root({
|
||||
modelName: Annotation<z.enum(["openai", "anthropic"])>({
|
||||
default: () => "anthropic",
|
||||
}),
|
||||
systemPrompt: Annotation<String>
|
||||
});
|
||||
|
||||
// the rest of your code
|
||||
|
||||
const builder = new StateGraph(StateAnnotation, ConfigAnnotation);
|
||||
```
|
||||
|
||||
And that you have the following assistant already created:
|
||||
|
||||
{
|
||||
"assistant_id": "62e209ca-9154-432a-b9e9-2d75c7a9219b",
|
||||
"graph_id": "agent",
|
||||
"name": "Open AI Assistant"
|
||||
"config": {
|
||||
"configurable": {
|
||||
"model_name": "openai",
|
||||
"system_prompt": "You are a helpful assistant."
|
||||
}
|
||||
},
|
||||
"metadata": {}
|
||||
"created_at": "2024-08-31T03:09:10.230718+00:00",
|
||||
"updated_at": "2024-08-31T03:09:10.230718+00:00",
|
||||
}
|
||||
|
||||
## Create a new version for your assistant
|
||||
|
||||
### LangGraph SDK
|
||||
|
||||
To edit the assistant, use the `update` method. This will create a new version of the assistant with the provided edits. See the [Python](../reference/sdk/python_sdk_ref.md#langgraph_sdk.client.AssistantsClient.update) and [JS](../reference/sdk/js_ts_sdk_ref.md#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 previously versions.
|
||||
|
||||
For example, to update your assistant's system prompt:
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
openai_assistant_v2 = await client.assistants.update(
|
||||
openai_assistant["assistant_id"],
|
||||
config={
|
||||
"configurable": {
|
||||
"model_name": "openai",
|
||||
"system_prompt": "You are an unhelpful assistant!",
|
||||
}
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
const openaiAssistantV2 = await client.assistants.update(
|
||||
openai_assistant["assistant_id"],
|
||||
{
|
||||
config: {
|
||||
configurable: {
|
||||
model_name: 'openai',
|
||||
system_prompt: 'You are an unhelpful assistant!',
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request PATCH \
|
||||
--url <DEPOLYMENT_URL>/assistants/<ASSISTANT_ID> \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"config": {"model_name": "openai", "system_prompt": "You are an unhelpful assistant!"}
|
||||
}'
|
||||
```
|
||||
|
||||
This will create a new version of the assistant with the updated parameters and set this as the active version of your assistant. If you now run your graph and pass in this assistant id, it will use this latest version.
|
||||
|
||||
### LangGraph Platform UI
|
||||
|
||||
You can also edit assistants from the LangGraph Platform UI.
|
||||
|
||||
Inside your deployment, select the "Assistants" tab. This will load a table of all of the assistants in your deployment, across all graphs.
|
||||
|
||||
To edit an existing assistant, select the "Edit" button for the specified assistant. This will open a form where you can edit the assistant's name, description, and configuration.
|
||||
|
||||
Additionally, if using LangGraph Studio, you can edit the assistants and create new versions via the "Manage Assistants" button.
|
||||
|
||||
## Use a previous assistant version
|
||||
|
||||
### LangGraph SDK
|
||||
|
||||
You can also change the active version of your assistant. To do so, use the `setLatest` method.
|
||||
|
||||
In the example above, to rollback to the first version of the assistant:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
await client.assistants.set_latest(openai_assistant['assistant_id'], 1)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
await client.assistants.setLatest(openaiAssistant['assistant_id'], 1);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/assistants/<ASSISTANT_ID>/latest \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"version": 1
|
||||
}'
|
||||
```
|
||||
|
||||
If you now run your graph and pass in this assistant id, it will use the first version of the assistant.
|
||||
|
||||
### LangGraph Platform UI
|
||||
|
||||
If using LangGraph Studio, to set the active version of your asssistant, click the "Manage Assistants" button and locate the assistant you would like to use. Select the assistant and the version, and then click the "Active" toggle. This will update the assistant to make the selected version active.
|
||||
|
||||
!!! warning "Deleting Assistants"
|
||||
Deleting as assistant will delete ALL of it's versions. There is currently no way to delete a single version, but by pointing your assistant to the correct version you can skip any versions that you don't wish to use.
|
||||
@@ -0,0 +1,203 @@
|
||||
# Check the Status of your Threads
|
||||
|
||||
## Setup
|
||||
|
||||
To start, we can setup our client with whatever URL you are hosting your graph from:
|
||||
|
||||
### 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 '{}'
|
||||
```
|
||||
|
||||
## Find idle threads
|
||||
|
||||
We can use the following commands to find threads that are idle, which means that all runs executed on the thread have finished running:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
print(await client.threads.search(status="idle",limit=1))
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
console.log(await client.threads.search({ status: "idle", limit: 1 }));
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/search \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{"status": "idle", "limit": 1}'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
[{'thread_id': 'cacf79bb-4248-4d01-aabc-938dbd60ed2c',
|
||||
'created_at': '2024-08-14T17:36:38.921660+00:00',
|
||||
'updated_at': '2024-08-14T17:36:38.921660+00:00',
|
||||
'metadata': {'graph_id': 'agent'},
|
||||
'status': 'idle',
|
||||
'config': {'configurable': {}}}]
|
||||
|
||||
|
||||
## Find interrupted threads
|
||||
|
||||
We can use the following commands to find threads that have been interrupted in the middle of a run, which could either mean an error occurred before the run finished or a human-in-the-loop breakpoint was reached and the run is waiting to continue:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
print(await client.threads.search(status="interrupted",limit=1))
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
console.log(await client.threads.search({ status: "interrupted", limit: 1 }));
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/search \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{"status": "interrupted", "limit": 1}'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
[{'thread_id': '0d282b22-bbd5-4d95-9c61-04dcc2e302a5',
|
||||
'created_at': '2024-08-14T17:41:50.235455+00:00',
|
||||
'updated_at': '2024-08-14T17:41:50.235455+00:00',
|
||||
'metadata': {'graph_id': 'agent'},
|
||||
'status': 'interrupted',
|
||||
'config': {'configurable': {}}}]
|
||||
|
||||
## Find busy threads
|
||||
|
||||
We can use the following commands to find threads that are busy, meaning they are currently handling the execution of a run:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
print(await client.threads.search(status="busy",limit=1))
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
console.log(await client.threads.search({ status: "busy", limit: 1 }));
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/search \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{"status": "busy", "limit": 1}'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
[{'thread_id': '0d282b22-bbd5-4d95-9c61-04dcc2e302a5',
|
||||
'created_at': '2024-08-14T17:41:50.235455+00:00',
|
||||
'updated_at': '2024-08-14T17:41:50.235455+00:00',
|
||||
'metadata': {'graph_id': 'agent'},
|
||||
'status': 'busy',
|
||||
'config': {'configurable': {}}}]
|
||||
|
||||
## Find specific threads
|
||||
|
||||
You may also want to check the status of specific threads, which you can do in a few ways:
|
||||
|
||||
### Find by ID
|
||||
|
||||
You can use the `get` function to find the status of a specific thread, as long as you have the ID saved
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
print((await client.threads.get(<THREAD_ID>))['status'])
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
console.log((await client.threads.get(<THREAD_ID>)).status);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request GET \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID> \
|
||||
--header 'Content-Type: application/json' | jq -r '.status'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
'idle'
|
||||
|
||||
### Find by metadata
|
||||
|
||||
The search endpoint for threads also allows you to filter on metadata, which can be helpful if you use metadata to tag threads in order to keep them organized:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
print((await client.threads.search(metadata={"foo":"bar"},limit=1))[0]['status'])
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
console.log((await client.threads.search({ metadata: { "foo": "bar" }, limit: 1 }))[0].status);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/search \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{"metadata": {"foo":"bar"}, "limit": 1}' | jq -r '.[0].status'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
'idle'
|
||||
@@ -1,36 +1,31 @@
|
||||
# Debug LangSmith traces
|
||||
# Testing local agents with remote traces
|
||||
|
||||
This guide explains how to open LangSmith traces in LangGraph Studio for interactive investigation and debugging.
|
||||
## Overview
|
||||
|
||||
## Open deployed threads
|
||||
A common workflow when debugging production-deployed agents is to test the same thread against a local version of the same agent, which may have modifications.
|
||||
|
||||
1. Open the LangSmith trace, selecting the root run.
|
||||
2. Click "Run in Studio".
|
||||
To support this, LangGraph Studio, in combination with LangSmith, allows you to clone remote threads traced in LangSmith into your locally running agent. This cloned thread can then be used to re-run specific nodes within Studio.
|
||||
|
||||
This will open LangGraph Studio connected to the associated LangGraph Platform deployment with the trace's parent thread selected.
|
||||
## Requirements
|
||||
|
||||
## Testing local agents with remote traces
|
||||
|
||||
This section explains how to test a local agent against remote traces from LangSmith. This enables you to use production traces as input for local testing, allowing you to debug and verify agent modifications in your development environment.
|
||||
|
||||
### Requirements
|
||||
|
||||
- A LangSmith traced thread
|
||||
- A locally running agent. See [here](../how-tos/studio/quick_start.md#local-development-server) for setup
|
||||
instructions.
|
||||
|
||||
!!! info "Local agent requirements"
|
||||
!!! info "Prerequisites"
|
||||
|
||||
- langgraph>=0.3.18
|
||||
- langgraph-api>=0.0.32
|
||||
- Contains the same set of nodes present in the remote trace
|
||||
|
||||
### Cloning Thread
|
||||
- A thread traced in LangSmith.
|
||||
- A locally running agent. See [here](../../how-tos/local-studio.md) for setup instructions.
|
||||
- Note that your local agent must be using the above specified `langgraph` and `langgraph-api` versions.
|
||||
- The nodes present in the remote trace must exist in at least one of the graphs in your local agent.
|
||||
|
||||
1. Open the LangSmith trace, selecting the root run.
|
||||
2. Click the dropdown next to "Run in Studio".
|
||||
3. Enter your local agent's URL.
|
||||
4. Select "Clone thread locally".
|
||||
5. If multiple graphs exist, select the target graph.
|
||||
## Cloning Thread
|
||||
|
||||
A new thread will be created in your local agent with the thread history inferred and copied from the remote thread, and you will be navigated to LangGraph Studio for your locally running application.
|
||||
First navigate to the LangSmith trace. Here you should see a button to "Run in Studio".
|
||||
|
||||
{width=1200}
|
||||
|
||||
This will prompt you to enter the url that your locally running agent is accessible at. Once provided, select "Clone thread locally". If you have multiple graphs in your agent, you will also be prompted to select a graph to clone this thread under.
|
||||
|
||||
Once selected, a will a new thread in your local agent will be created and the thread history will be reconstruced to reflect the original trace.
|
||||
|
||||
Alternatively, if your trace originates from an agent deployed on LangGraph Platform, you can "View original thread" to open Studio with the actual deployed thread.
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
# Manage assistants
|
||||
# How to create Assistants
|
||||
|
||||
In this guide we will show how to create, configure, and manage an [assistant](../../concepts/assistants.md).
|
||||
!!! info "Prerequisites"
|
||||
|
||||
- [Assistants Overview](../../concepts/assistants.md)
|
||||
- [Configuration](../../concepts/low_level.md#configuration)
|
||||
|
||||
In this guide we will show how to create and configure an assistant.
|
||||
|
||||
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`.
|
||||
|
||||
@@ -46,11 +51,11 @@ 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).
|
||||
|
||||
## Create an assistant
|
||||
## Creating an Assistant
|
||||
|
||||
### LangGraph SDK
|
||||
|
||||
To create an assistant, use the [LangGraph SDK](../../concepts/sdk.md) `create` method. See the [Python](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/#langgraph_sdk.client.AssistantsClient.create) and [JS](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/js_ts_sdk_ref/#create) SDK reference docs for more information.
|
||||
To create an assistant, use the [LangGraph SDK](../../concepts/sdk.md) `create` method. See the [Python](../reference/sdk/python_sdk_ref.md#langgraph_sdk.client.AssistantsClient.create) and [JS](../reference/sdk/js_ts_sdk_ref.md#create) SDK reference docs for more information.
|
||||
|
||||
This example uses the same configuration schema as above, and creates an assistant with `model_name` set to `openai`.
|
||||
|
||||
@@ -74,7 +79,7 @@ This example uses the same configuration schema as above, and creates an assista
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL> });
|
||||
const openAIAssistant = await client.assistants.create({
|
||||
let openAIAssistant = await client.assistants.create({
|
||||
graphId: 'agent',
|
||||
name: "Open AI Assistant",
|
||||
config: { "configurable": { "model_name": "openai" } },
|
||||
@@ -118,7 +123,7 @@ To create a new assistant, select the "+ New assistant" button. This will open a
|
||||
|
||||
To confirm, click "Create assistant". This will take you to [LangGraph Studio](../../concepts/langgraph_studio.md) where you can test the assistant. If you go back to the "Assistants" tab in the deployment, you will see the newly created assistant in the table.
|
||||
|
||||
## Use an assistant
|
||||
## Using an Assistant
|
||||
|
||||
### LangGraph SDK
|
||||
|
||||
@@ -145,7 +150,7 @@ We have now created an assistant called "Open AI Assistant" that has `model_name
|
||||
|
||||
```js
|
||||
const thread = await client.threads.create();
|
||||
const input = { "messages": [{ "role": "user", "content": "who made you?" }] };
|
||||
let input = { "messages": [{ "role": "user", "content": "who made you?" }] };
|
||||
|
||||
const streamResponse = client.runs.stream(
|
||||
thread["thread_id"],
|
||||
@@ -223,105 +228,3 @@ Output:
|
||||
### LangGraph Platform UI
|
||||
|
||||
Inside your deployment, select the "Assistants" tab. For the assistant you would like to use, click the "Studio" button. This will open LangGraph Studio with the selected assistant. When you submit an input (either in Graph or Chat mode), the selected assistant and its configuration will be used.
|
||||
|
||||
## Create a new version for your assistant
|
||||
|
||||
### LangGraph SDK
|
||||
|
||||
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 previously versions.
|
||||
|
||||
For example, to update your assistant's system prompt:
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
openai_assistant_v2 = await client.assistants.update(
|
||||
openai_assistant["assistant_id"],
|
||||
config={
|
||||
"configurable": {
|
||||
"model_name": "openai",
|
||||
"system_prompt": "You are an unhelpful assistant!",
|
||||
}
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
const openaiAssistantV2 = await client.assistants.update(
|
||||
openai_assistant["assistant_id"],
|
||||
{
|
||||
config: {
|
||||
configurable: {
|
||||
model_name: 'openai',
|
||||
system_prompt: 'You are an unhelpful assistant!',
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request PATCH \
|
||||
--url <DEPOLYMENT_URL>/assistants/<ASSISTANT_ID> \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"config": {"model_name": "openai", "system_prompt": "You are an unhelpful assistant!"}
|
||||
}'
|
||||
```
|
||||
|
||||
This will create a new version of the assistant with the updated parameters and set this as the active version of your assistant. If you now run your graph and pass in this assistant id, it will use this latest version.
|
||||
|
||||
### LangGraph Platform UI
|
||||
|
||||
You can also edit assistants from the LangGraph Platform UI.
|
||||
|
||||
Inside your deployment, select the "Assistants" tab. This will load a table of all of the assistants in your deployment, across all graphs.
|
||||
|
||||
To edit an existing assistant, select the "Edit" button for the specified assistant. This will open a form where you can edit the assistant's name, description, and configuration.
|
||||
|
||||
Additionally, if using LangGraph Studio, you can edit the assistants and create new versions via the "Manage Assistants" button.
|
||||
|
||||
## Use a previous assistant version
|
||||
|
||||
### LangGraph SDK
|
||||
|
||||
You can also change the active version of your assistant. To do so, use the `setLatest` method.
|
||||
|
||||
In the example above, to rollback to the first version of the assistant:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
await client.assistants.set_latest(openai_assistant['assistant_id'], 1)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
await client.assistants.setLatest(openaiAssistant['assistant_id'], 1);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/assistants/<ASSISTANT_ID>/latest \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"version": 1
|
||||
}'
|
||||
```
|
||||
|
||||
If you now run your graph and pass in this assistant id, it will use the first version of the assistant.
|
||||
|
||||
### LangGraph Platform UI
|
||||
|
||||
If using LangGraph Studio, to set the active version of your asssistant, click the "Manage Assistants" button and locate the assistant you would like to use. Select the assistant and the version, and then click the "Active" toggle. This will update the assistant to make the selected version active.
|
||||
|
||||
!!! warning "Deleting Assistants"
|
||||
Deleting as assistant will delete ALL of it's versions. There is currently no way to delete a single version, but by pointing your assistant to the correct version you can skip any versions that you don't wish to use.
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
# Copying Threads
|
||||
|
||||
You may wish to copy (i.e. "fork") an existing thread in order to keep the existing thread's history and create independent runs that do not affect the original thread. This guide shows how you can do that.
|
||||
|
||||
## Setup
|
||||
|
||||
This code assumes you already have a thread to copy.
|
||||
|
||||
For more information, see these guides on [Threads](../../cloud/concepts/threads.md) and [Streaming](../../concepts/streaming.md).
|
||||
|
||||
### 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>")
|
||||
assistant_id = "agent"
|
||||
thread = await client.threads.create()
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
|
||||
const client = new Client({ apiUrl: "<DEPLOYMENT_URL>" });
|
||||
const assistantId = "agent";
|
||||
const thread = await client.threads.create();
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"metadata": {}
|
||||
}'
|
||||
```
|
||||
|
||||
## Copying a thread
|
||||
|
||||
The code below assumes that a thread you'd like to copy already exists.
|
||||
|
||||
Copying a thread will create a new thread with the same history as the existing thread, and then allow you to continue executing runs.
|
||||
|
||||
### Create copy
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
copied_thread = await client.threads.copy(<THREAD_ID>)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
let copiedThread = await client.threads.copy(<THREAD_ID>);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST --url <DEPLOYMENT_URL>/threads/<THREAD_ID>/copy \
|
||||
--header 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
### Verify copy
|
||||
|
||||
We can verify that the history from the prior thread did indeed copy over correctly:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
def remove_thread_id(d):
|
||||
if 'metadata' in d and 'thread_id' in d['metadata']:
|
||||
del d['metadata']['thread_id']
|
||||
return d
|
||||
|
||||
original_thread_history = list(map(remove_thread_id,await client.threads.get_history(<THREAD_ID>)))
|
||||
copied_thread_history = list(map(remove_thread_id,await client.threads.get_history(copied_thread['thread_id'])))
|
||||
|
||||
# Compare the two histories
|
||||
assert original_thread_history == copied_thread_history
|
||||
# if we made it here the assertion passed!
|
||||
print("The histories are the same.")
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
function removeThreadId(d) {
|
||||
if (d.metadata && d.metadata.thread_id) {
|
||||
delete d.metadata.thread_id;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
// Assuming `client.threads.getHistory(threadId)` is an async function that returns a list of dicts
|
||||
async function compareThreadHistories(threadId, copiedThreadId) {
|
||||
const originalThreadHistory = (await client.threads.getHistory(threadId)).map(removeThreadId);
|
||||
const copiedThreadHistory = (await client.threads.getHistory(copiedThreadId)).map(removeThreadId);
|
||||
|
||||
// Compare the two histories
|
||||
console.assert(JSON.stringify(originalThreadHistory) === JSON.stringify(copiedThreadHistory));
|
||||
// if we made it here the assertion passed!
|
||||
console.log("The histories are the same.");
|
||||
}
|
||||
|
||||
// Example usage
|
||||
compareThreadHistories(<THREAD_ID>, copiedThread.thread_id);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
if diff <(
|
||||
curl --request GET --url <DEPLOYMENT_URL>/threads/<THREAD_ID>/history | jq -S 'map(del(.metadata.thread_id))'
|
||||
) <(
|
||||
curl --request GET --url <DEPLOYMENT_URL>/threads/<COPIED_THREAD_ID>/history | jq -S 'map(del(.metadata.thread_id))'
|
||||
) >/dev/null; then
|
||||
echo "The histories are the same."
|
||||
else
|
||||
echo "The histories are different."
|
||||
fi
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
The histories are the same.
|
||||
@@ -1,6 +1,6 @@
|
||||
# Use cron jobs
|
||||
# Cron Jobs
|
||||
|
||||
Sometimes you don't want to run your graph based on user interaction, but rather you would like to schedule your graph to run on a schedule - for example if you wish for your graph to compose and send out a weekly email of to-dos for your team. LangGraph Platform allows you to do this without having to write your own script by using the `Crons` client. To schedule a graph job, you need to pass a [cron expression](https://crontab.cronhub.io/) to inform the client when you want to run the graph. `Cron` jobs are run in the background and do not interfere with normal invocations of the graph.
|
||||
Sometimes you don't want to run your graph based on user interaction, but rather you would like to schedule your graph to run on a schedule - for example if you wish for your graph to compose and send out a weekly email of to-dos for your team. LangGraph Cloud allows you to do this without having to write your own script by using the `Crons` client. To schedule a graph job, you need to pass a [cron expression](https://crontab.cronhub.io/) to inform the client when you want to run the graph. `Cron` jobs are run in the background and do not interfere with normal invocations of the graph.
|
||||
|
||||
## Setup
|
||||
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
# Add node to dataset
|
||||
# Adding nodes as dataset examples in Studio
|
||||
|
||||
This guide shows how to add examples to [LangSmith datasets](https://docs.smith.langchain.com/evaluation/how_to_guides#dataset-management) from nodes in the thread log. This is useful to evaluate indivudal steps of the agent.
|
||||
In LangGraph Studio you can create dataset examples from the thread history in the right-hand pane. This can be especially useful when you want to evaluate intermediate steps of the agent.
|
||||
|
||||
1. Select a thread.
|
||||
2. Click on the `Add to Dataset` button.
|
||||
3. Select nodes whose input/output you want to add to a dataset.
|
||||
4. For each selected node, select the target dataset to create the example in. By default a dataset for the specific assistant and node will be selected. If this dataset does not yet exist, it will be created.
|
||||
5. Edit the example's input/output as needed before adding it to the dataset.
|
||||
6. Select "Add to dataset" at the bottom of the page to add all selected nodes to their respective datasets.
|
||||
1. Click on the `Add to Dataset` button to enter the dataset mode.
|
||||
1. Select nodes which you want to add to dataset.
|
||||
1. Select the target dataset to create the example in.
|
||||
|
||||
You can edit the example payload before sending it to the dataset, which is useful if you need to make changes to conform the example to the dataset schema.
|
||||
|
||||
Finally, you can customise the target dataset by clicking on the `Settings` button.
|
||||
|
||||
See [Evaluating intermediate steps](https://docs.smith.langchain.com/evaluation/how_to_guides/langgraph#evaluating-intermediate-steps) for more details on how to evaluate intermediate steps.
|
||||
|
||||
<video controls allowfullscreen="true" poster="../img/studio_datasets.jpg">
|
||||
<source src="https://langgraph-docs-assets.pages.dev/studio_datasets.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
@@ -1,86 +1,24 @@
|
||||
# Breakpoints
|
||||
# How to add static 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.
|
||||
!!! tip "Prerequisites"
|
||||
|
||||
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.
|
||||
This guide assumes familiarity with the following concepts:
|
||||
|
||||
## Set breakpoints
|
||||
* [Breakpoints](../../concepts/breakpoints.md)
|
||||
* [LangGraph Glossary](../../concepts/low_level.md)
|
||||
|
||||
|
||||
=== "Compile time"
|
||||
Human-in-the-loop (HIL) interactions are crucial for [agentic systems](../../concepts/agentic_concepts.md#human-in-the-loop). [Breakpoints](../../concepts/breakpoints.md) are a common HIL interaction pattern, allowing the graph to stop at specific steps and seek human approval before proceeding (e.g., for sensitive actions).
|
||||
|
||||
```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)!
|
||||
)
|
||||
```
|
||||
Breakpoints are built on top of LangGraph [checkpoints](../../concepts/persistence.md#checkpoints), which save the graph's state after each node execution. Checkpoints are saved in [threads](../../concepts/persistence.md#threads) that preserve graph state and can be accessed after a graph has finished execution. This allows for graph execution to pause at specific points, await human approval, and then resume execution from the last checkpoint.
|
||||
|
||||
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.
|
||||
## Setup
|
||||
|
||||
=== "Run time"
|
||||
### Code for your graph
|
||||
|
||||
=== "Python"
|
||||
In this how-to we use a simple ReAct style hosted graph (you can see the full code for defining it [here](../../how-tos/human_in_the_loop/breakpoints.ipynb)). The important thing is that there are two nodes (one named `agent` that calls the LLM, and one named `action` that calls the tool), and a routing function from `agent` that determines whether to call `action` next or just end the graph run (the `action` node always calls the `agent` node after execution).
|
||||
|
||||
```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.
|
||||
### SDK Initialization
|
||||
|
||||
|
||||
=== "Python"
|
||||
@@ -88,97 +26,130 @@ With breakpoints, you can inspect the graph's state and node inputs at any point
|
||||
```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"
|
||||
=== "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 assistantId = "agent";
|
||||
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)!
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
## Adding a breakpoint
|
||||
|
||||
We now want to add a breakpoint in our graph run, which we will do before a tool is called.
|
||||
We can do this by adding `interrupt_before=["action"]`, which tells us to interrupt before calling the action node.
|
||||
We can do this either when compiling the graph or when kicking off a run.
|
||||
Here we will do it when kicking of a run, if you would like to to do it at compile time you need to edit the python file where your graph is defined and add the `interrupt_before` parameter when you call `.compile`.
|
||||
|
||||
First let's access our hosted LangGraph instance through the SDK:
|
||||
|
||||
And, now let's compile it with a breakpoint before the tool node:
|
||||
|
||||
=== "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",
|
||||
interrupt_before=["action"],
|
||||
):
|
||||
print(f"Receiving new event of type: {chunk.event}...")
|
||||
print(chunk.data)
|
||||
print("\n\n")
|
||||
```
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
const input = { messages: [{ role: "human", content: "what's the weather in sf" }] };
|
||||
|
||||
const streamResponse = client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistantId,
|
||||
{
|
||||
input: input,
|
||||
streamMode: "updates",
|
||||
interruptBefore: ["action"]
|
||||
}
|
||||
);
|
||||
|
||||
// Resume the graph
|
||||
await client.runs.wait(
|
||||
threadID,
|
||||
assistantID,
|
||||
{ input: null } // (2)!
|
||||
);
|
||||
for await (const chunk of streamResponse) {
|
||||
console.log(`Receiving new event of type: ${chunk.event}...`);
|
||||
console.log(chunk.data);
|
||||
console.log("\n\n");
|
||||
}
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
--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\"}]},
|
||||
\"interrupt_before\": [\"action\"],
|
||||
\"stream_mode\": [
|
||||
\"messages\"
|
||||
]
|
||||
}" | \
|
||||
sed 's/\r$//' | \
|
||||
awk '
|
||||
/^event:/ {
|
||||
if (data_content != "") {
|
||||
print data_content "\n"
|
||||
}
|
||||
sub(/^event: /, "Receiving event of type: ", $0)
|
||||
printf "%s...\n", $0
|
||||
data_content = ""
|
||||
}
|
||||
/^data:/ {
|
||||
sub(/^data: /, "", $0)
|
||||
data_content = $0
|
||||
}
|
||||
END {
|
||||
if (data_content != "") {
|
||||
print data_content "\n"
|
||||
}
|
||||
}
|
||||
'
|
||||
```
|
||||
|
||||
Run the graph until the breakpoint:
|
||||
Output:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/wait \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": <INPUT>
|
||||
}"
|
||||
```
|
||||
Receiving new event of type: metadata...
|
||||
{'run_id': '3b77ef83-687a-4840-8858-0371f91a92c3'}
|
||||
|
||||
|
||||
|
||||
Receiving new event of type: data...
|
||||
{'agent': {'messages': [{'content': [{'id': 'toolu_01HwZqM1ptX6E15A5LAmyZTB', 'input': {'query': 'weather in san francisco'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}], 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'run-e5d17791-4d37-4ad2-815f-a0c4cba62585', 'example': False, 'tool_calls': [{'name': 'tavily_search_results_json', 'args': {'query': 'weather in san francisco'}, 'id': 'toolu_01HwZqM1ptX6E15A5LAmyZTB'}], 'invalid_tool_calls': []}]}}
|
||||
|
||||
|
||||
|
||||
Receiving new event of type: end...
|
||||
None
|
||||
|
||||
|
||||
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,277 @@
|
||||
# How to Edit State of a Deployed Graph
|
||||
|
||||
When creating LangGraph agents, it is often nice to add a human-in-the-loop component. This can be helpful when giving them access to tools. Often in these situations you may want to edit the graph state before continuing (for example, to edit what tool is being called, or how it is being called).
|
||||
|
||||
This can be in several ways, but the primary supported way is to add an "interrupt" before a node is executed. This interrupts execution at that node. You can then use update_state to update the state, and then resume from that spot to continue.
|
||||
|
||||
## 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/time-travel.ipynb) if you want to. 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 '{}'
|
||||
```
|
||||
|
||||
## Editing state
|
||||
|
||||
### Initial invocation
|
||||
|
||||
Now let's invoke our graph, making sure to interrupt before the `action` node.
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
input = { 'messages':[{ "role":"user", "content":"search for weather in SF" }] }
|
||||
|
||||
async for chunk in client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistant_id,
|
||||
input=input,
|
||||
stream_mode="updates",
|
||||
interrupt_before=["action"],
|
||||
):
|
||||
if chunk.data and chunk.event != "metadata":
|
||||
print(chunk.data)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
const input = { messages: [{ role: "human", content: "search for weather in SF" }] };
|
||||
|
||||
const streamResponse = client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistantId,
|
||||
{
|
||||
input: input,
|
||||
streamMode: "updates",
|
||||
interruptBefore: ["action"],
|
||||
}
|
||||
);
|
||||
|
||||
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\": \"search for weather in SF\"}]},
|
||||
\"interrupt_before\": [\"action\"],
|
||||
\"stream_mode\": [
|
||||
\"updates\"
|
||||
]
|
||||
}" | \
|
||||
sed 's/\r$//' | \
|
||||
awk '
|
||||
/^event:/ {
|
||||
if (data_content != "" && event_type != "metadata") {
|
||||
print data_content "\n"
|
||||
}
|
||||
sub(/^event: /, "", $0)
|
||||
event_type = $0
|
||||
data_content = ""
|
||||
}
|
||||
/^data:/ {
|
||||
sub(/^data: /, "", $0)
|
||||
data_content = $0
|
||||
}
|
||||
END {
|
||||
if (data_content != "" && event_type != "metadata") {
|
||||
print data_content "\n"
|
||||
}
|
||||
}
|
||||
'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
{'agent': {'messages': [{'content': [{'text': "Certainly! I'll search for the current weather in San Francisco for you using the search function. Here's how I'll do that:", 'type': 'text'}, {'id': 'toolu_01KEJMBFozSiZoS4mAcPZeqQ', 'input': {'query': 'current weather in San Francisco'}, 'name': 'search', 'type': 'tool_use'}], 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'run-6dbb0167-f8f6-4e2a-ab68-229b2d1fbb64', 'example': False, 'tool_calls': [{'name': 'search', 'args': {'query': 'current weather in San Francisco'}, 'id': 'toolu_01KEJMBFozSiZoS4mAcPZeqQ'}], 'invalid_tool_calls': [], 'usage_metadata': None}]}}
|
||||
|
||||
|
||||
### Edit the state
|
||||
|
||||
Now, let's assume we actually meant to search for the weather in Sidi Frej (another city with the initials SF). We can edit the state to properly reflect that:
|
||||
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
# First, lets get the current state
|
||||
current_state = await client.threads.get_state(thread['thread_id'])
|
||||
|
||||
# Let's now get the last message in the state
|
||||
# This is the one with the tool calls that we want to update
|
||||
last_message = current_state['values']['messages'][-1]
|
||||
|
||||
# Let's now update the args for that tool call
|
||||
last_message['tool_calls'][0]['args'] = {'query': 'current weather in Sidi Frej'}
|
||||
|
||||
# Let's now call `update_state` to pass in this message in the `messages` key
|
||||
# This will get treated as any other update to the state
|
||||
# It will get passed to the reducer function for the `messages` key
|
||||
# That reducer function will use the ID of the message to update it
|
||||
# It's important that it has the right ID! Otherwise it would get appended
|
||||
# as a new message
|
||||
await client.threads.update_state(thread['thread_id'], {"messages": last_message})
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
// First, let's get the current state
|
||||
const currentState = await client.threads.getState(thread["thread_id"]);
|
||||
|
||||
// Let's now get the last message in the state
|
||||
// This is the one with the tool calls that we want to update
|
||||
let lastMessage = currentState.values.messages.slice(-1)[0];
|
||||
|
||||
// Let's now update the args for that tool call
|
||||
lastMessage.tool_calls[0].args = { query: "current weather in Sidi Frej" };
|
||||
|
||||
// Let's now call `update_state` to pass in this message in the `messages` key
|
||||
// This will get treated as any other update to the state
|
||||
// It will get passed to the reducer function for the `messages` key
|
||||
// That reducer function will use the ID of the message to update it
|
||||
// It's important that it has the right ID! Otherwise it would get appended
|
||||
// as a new message
|
||||
await client.threads.updateState(thread["thread_id"], { values: { messages: lastMessage } });
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request GET --url <DEPLOYMENT_URL>/threads/<THREAD_ID>/state | \
|
||||
jq '.values.messages[-1] | (.tool_calls[0].args = {"query": "current weather in Sidi Frej"})' | \
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/state \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data @-
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
{'configurable': {'thread_id': '9c8f1a43-9dd8-4017-9271-2c53e57cf66a',
|
||||
'checkpoint_ns': '',
|
||||
'checkpoint_id': '1ef58e7e-3641-649f-8002-8b4305a64858'}}
|
||||
|
||||
|
||||
|
||||
### Resume invocation
|
||||
|
||||
Now we can resume our graph run but with the updated state:
|
||||
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
async for chunk in client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistant_id,
|
||||
input=None,
|
||||
stream_mode="updates",
|
||||
):
|
||||
if chunk.data and chunk.event != "metadata":
|
||||
print(chunk.data)
|
||||
```
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
const streamResponse = client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistantId,
|
||||
{
|
||||
input: null,
|
||||
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\",
|
||||
\"stream_mode\": [
|
||||
\"updates\"
|
||||
]
|
||||
}"| \
|
||||
sed 's/\r$//' | \
|
||||
awk '
|
||||
/^event:/ {
|
||||
if (data_content != "" && event_type != "metadata") {
|
||||
print data_content "\n"
|
||||
}
|
||||
sub(/^event: /, "", $0)
|
||||
event_type = $0
|
||||
data_content = ""
|
||||
}
|
||||
/^data:/ {
|
||||
sub(/^data: /, "", $0)
|
||||
data_content = $0
|
||||
}
|
||||
END {
|
||||
if (data_content != "" && event_type != "metadata") {
|
||||
print data_content "\n"
|
||||
}
|
||||
}
|
||||
'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
{'action': {'messages': [{'content': '["I looked up: current weather in Sidi Frej. Result: It\'s sunny in San Francisco, but you better look out if you\'re a Gemini 😈."]', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'tool', 'name': 'search', 'id': '1161b8d1-bee4-4188-9be8-698aecb69f10', 'tool_call_id': 'toolu_01KEJMBFozSiZoS4mAcPZeqQ'}]}}
|
||||
{'agent': {'messages': [{'content': [{'text': 'I apologize for the confusion in my search query. It seems the search function interpreted "SF" as "Sidi Frej" instead of "San Francisco" as we intended. Let me search again with the full city name to get the correct information:', 'type': 'text'}, {'id': 'toolu_0111rrwgfAcmurHZn55qjqTR', 'input': {'query': 'current weather in San Francisco'}, 'name': 'search', 'type': 'tool_use'}], 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'run-b8c25779-cfb4-46fc-a421-48553551242f', 'example': False, 'tool_calls': [{'name': 'search', 'args': {'query': 'current weather in San Francisco'}, 'id': 'toolu_0111rrwgfAcmurHZn55qjqTR'}], 'invalid_tool_calls': [], 'usage_metadata': None}]}}
|
||||
{'action': {'messages': [{'content': '["I looked up: current weather in San Francisco. Result: It\'s sunny in San Francisco, but you better look out if you\'re a Gemini 😈."]', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'tool', 'name': 'search', 'id': '6bc632ae-5ee6-4d01-9532-79c524a2d443', 'tool_call_id': 'toolu_0111rrwgfAcmurHZn55qjqTR'}]}}
|
||||
{'agent': {'messages': [{'content': "Now, based on the search results, I can provide you with information about the current weather in San Francisco:\n\nThe weather in San Francisco is currently sunny. \n\nIt's worth noting that the search result included an unusual comment about Gemini, which doesn't seem directly related to the weather. This might be due to the search engine including some astrological information or a joke in its results. However, for the purpose of weather information, we can focus on the fact that it's sunny in San Francisco right now.\n\nIs there anything else you'd like to know about the weather in San Francisco or any other location?", 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'run-227a042b-dd97-476e-af32-76a3703af5d8', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': None}]}}
|
||||
|
||||
|
||||
As you can see it now looks up the current weather in Sidi Frej (although our dummy search node still returns results for SF because we don't actually do a search in this example, we just return the same "It's sunny in San Francisco ..." result every time).
|
||||
@@ -86,7 +86,7 @@ First, we need to setup our client so that we can communicate with our hosted gr
|
||||
const thread = await client.threads.create();
|
||||
```
|
||||
|
||||
=== "cURL"
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
@@ -135,7 +135,7 @@ First, let's run the agent with an input that requires tool calls with approval:
|
||||
}
|
||||
```
|
||||
|
||||
=== "cURL"
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
@@ -194,7 +194,7 @@ To approve the tool call, we need to let `human_review_node` know what value to
|
||||
}
|
||||
```
|
||||
|
||||
=== "cURL"
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
@@ -257,7 +257,7 @@ Let's now say we want to edit the tool call. E.g. change some of the parameters
|
||||
}
|
||||
```
|
||||
|
||||
=== "cURL"
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
@@ -323,7 +323,7 @@ To do this, we will use `Command` with a different resume value of `{"action": "
|
||||
}
|
||||
```
|
||||
|
||||
=== "cURL"
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
@@ -395,7 +395,7 @@ For this example we will just add a single tool call representing the feedback (
|
||||
}
|
||||
```
|
||||
|
||||
=== "cURL"
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
@@ -462,7 +462,7 @@ To do this, we will use `Command` with a different resume value of `{"action": "
|
||||
}
|
||||
```
|
||||
|
||||
=== "cURL"
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
@@ -525,7 +525,7 @@ We can see that we now get to another interrupt - because it went back to the mo
|
||||
}
|
||||
```
|
||||
|
||||
=== "cURL"
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
|
||||
@@ -1,240 +1,386 @@
|
||||
# Time travel
|
||||
# How to Replay and Branch from Prior States
|
||||
|
||||
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.
|
||||
With LangGraph Cloud you have the ability to return to any of your prior states and either re-run the graph to reproduce issues noticed during testing, or branch out in a different way from what was originally done in the prior states. In this guide we will show a quick example of how to rerun past states and how to branch off from previous states as well.
|
||||
|
||||
## Use time travel
|
||||
## Setup
|
||||
|
||||
To use time-travel in LangGraph:
|
||||
The examples below are executed against a specific deployment on LangGraph Cloud. You will use
|
||||
the SDK in a similar way, but you will expect to see different results based on the graph you have deployed.
|
||||
|
||||
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`.
|
||||
Alternatively, set a [breakpoint](./human_in_the_loop_breakpoint.md) before the node(s) where you want execution to pause. You can then find the most recent checkpoint recorded up to that breakpoint.
|
||||
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`.
|
||||
### SDK initialization
|
||||
|
||||
## Example
|
||||
|
||||
??? example "Example graph"
|
||||
|
||||
```python
|
||||
from typing_extensions import TypedDict, NotRequired
|
||||
from langgraph.graph import StateGraph, START, END
|
||||
from langchain.chat_models import init_chat_model
|
||||
from langgraph.checkpoint.memory import InMemorySaver
|
||||
|
||||
class State(TypedDict):
|
||||
topic: NotRequired[str]
|
||||
joke: NotRequired[str]
|
||||
|
||||
llm = init_chat_model(
|
||||
"anthropic:claude-3-7-sonnet-latest",
|
||||
temperature=0,
|
||||
)
|
||||
|
||||
def generate_topic(state: State):
|
||||
"""LLM call to generate a topic for the joke"""
|
||||
msg = llm.invoke("Give me a funny topic for a joke")
|
||||
return {"topic": msg.content}
|
||||
|
||||
def write_joke(state: State):
|
||||
"""LLM call to write a joke based on the topic"""
|
||||
msg = llm.invoke(f"Write a short joke about {state['topic']}")
|
||||
return {"joke": msg.content}
|
||||
|
||||
# Build workflow
|
||||
builder = StateGraph(State)
|
||||
|
||||
# Add nodes
|
||||
builder.add_node("generate_topic", generate_topic)
|
||||
builder.add_node("write_joke", write_joke)
|
||||
|
||||
# Add edges to connect nodes
|
||||
builder.add_edge(START, "generate_topic")
|
||||
builder.add_edge("generate_topic", "write_joke")
|
||||
|
||||
# Compile
|
||||
graph = builder.compile()
|
||||
```
|
||||
|
||||
### 1. Run the graph
|
||||
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"
|
||||
|
||||
# create a thread
|
||||
thread = await client.threads.create()
|
||||
thread_id = thread["thread_id"]
|
||||
|
||||
# Run the graph
|
||||
result = await client.runs.wait(
|
||||
thread_id,
|
||||
assistant_id,
|
||||
input={}
|
||||
)
|
||||
```
|
||||
|
||||
=== "JavaScript"
|
||||
=== "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 assistantId = "agent";
|
||||
const thread = await client.threads.create();
|
||||
const threadID = thread["thread_id"];
|
||||
|
||||
// Run the graph
|
||||
const result = await client.runs.wait(
|
||||
threadID,
|
||||
assistantID,
|
||||
{ input: {}}
|
||||
);
|
||||
```
|
||||
|
||||
=== "cURL"
|
||||
|
||||
Create a thread:
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
Run the graph:
|
||||
## Replay a state
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/wait \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": {}
|
||||
}"
|
||||
```
|
||||
### Initial invocation
|
||||
|
||||
### 2. Identify a checkpoint
|
||||
Before replaying a state - we need to create states to replay from! In order to do this, let's invoke our graph with a simple message:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
# The states are returned in reverse chronological order.
|
||||
states = await client.threads.get_history(thread_id)
|
||||
selected_state = states[1]
|
||||
print(selected_state)
|
||||
```
|
||||
input = {"messages": [{"role": "user", "content": "Please search the weather in SF"}]}
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
// The states are returned in reverse chronological order.
|
||||
const states = await client.threads.getHistory(threadID);
|
||||
const selectedState = states[1];
|
||||
console.log(selectedState);
|
||||
```
|
||||
|
||||
=== "cURL"
|
||||
|
||||
```bash
|
||||
curl --request GET \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/history \
|
||||
--header 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
### 3. Update the state (optional)
|
||||
|
||||
`update_state` will create a new checkpoint. The new checkpoint will be associated with the same thread, but a new checkpoint ID.
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
new_config = await client.threads.update_state(
|
||||
thread_id,
|
||||
{"topic": "chickens"},
|
||||
# highlight-next-line
|
||||
checkpoint_id=selected_state["checkpoint_id"]
|
||||
)
|
||||
print(new_config)
|
||||
```
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
const newConfig = await client.threads.updateState(
|
||||
threadID,
|
||||
{
|
||||
values: { "topic": "chickens" },
|
||||
checkpointId: selectedState["checkpoint_id"]
|
||||
}
|
||||
);
|
||||
console.log(newConfig);
|
||||
```
|
||||
|
||||
=== "cURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/state \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"checkpoint_id\": <CHECKPOINT_ID>,
|
||||
\"values\": {\"topic\": \"chickens\"}
|
||||
}"
|
||||
```
|
||||
|
||||
### 4. Resume execution from the checkpoint
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
await client.runs.wait(
|
||||
thread_id,
|
||||
async for chunk in client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistant_id,
|
||||
# highlight-next-line
|
||||
input=None,
|
||||
# highlight-next-line
|
||||
checkpoint_id=new_config["checkpoint_id"]
|
||||
)
|
||||
input=input,
|
||||
stream_mode="updates",
|
||||
):
|
||||
if chunk.data and chunk.event != "metadata":
|
||||
print(chunk.data)
|
||||
```
|
||||
|
||||
=== "JavaScript"
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
await client.runs.wait(
|
||||
threadID,
|
||||
assistantID,
|
||||
const input = { "messages": [{ "role": "user", "content": "Please search the weather in SF" }] }
|
||||
|
||||
const streamResponse = client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistantId,
|
||||
{
|
||||
// highlight-next-line
|
||||
input: null,
|
||||
// highlight-next-line
|
||||
checkpointId: newConfig["checkpoint_id"]
|
||||
input: input,
|
||||
streamMode: "updates",
|
||||
}
|
||||
);
|
||||
for await (const chunk of streamResponse) {
|
||||
if (chunk.data && chunk.event !== "metadata") {
|
||||
console.log(chunk.data);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== "cURL"
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/wait \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"checkpoint_id\": <CHECKPOINT_ID>
|
||||
}"
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": {\"messages\": [{\"role\": \"human\", \"content\": \"Please search the weather in SF\"}]},
|
||||
\"stream_mode\": [
|
||||
\"updates\"
|
||||
]
|
||||
}" | \
|
||||
sed 's/\r$//' | \
|
||||
awk '
|
||||
/^event:/ {
|
||||
if (data_content != "" && event_type != "metadata") {
|
||||
print data_content "\n"
|
||||
}
|
||||
sub(/^event: /, "", $0)
|
||||
event_type = $0
|
||||
data_content = ""
|
||||
}
|
||||
/^data:/ {
|
||||
sub(/^data: /, "", $0)
|
||||
data_content = $0
|
||||
}
|
||||
END {
|
||||
if (data_content != "" && event_type != "metadata") {
|
||||
print data_content "\n"
|
||||
}
|
||||
}
|
||||
'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
{'agent': {'messages': [{'content': [{'text': "Certainly! I'll use the search function to look up the current weather in San Francisco for you. Let me do that now.", 'type': 'text'}, {'id': 'toolu_011vroKUtWU7SBdrngpgpFMn', 'input': {'query': 'current weather in San Francisco'}, 'name': 'search', 'type': 'tool_use'}], 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'run-ee639877-d97d-40f8-96dc-d0d1ae22d203', 'example': False, 'tool_calls': [{'name': 'search', 'args': {'query': 'current weather in San Francisco'}, 'id': 'toolu_011vroKUtWU7SBdrngpgpFMn'}], 'invalid_tool_calls': [], 'usage_metadata': None}]}}
|
||||
{'action': {'messages': [{'content': '["I looked up: current weather in San Francisco. Result: It\'s sunny in San Francisco, but you better look out if you\'re a Gemini 😈."]', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'tool', 'name': 'search', 'id': '7bad0e72-5ebe-4b08-9b8a-b99b0fe22fb7', 'tool_call_id': 'toolu_011vroKUtWU7SBdrngpgpFMn'}]}}
|
||||
{'agent': {'messages': [{'content': "Based on the search results, I can provide you with information about the current weather in San Francisco:\n\nThe weather in San Francisco is currently sunny. This is great news for outdoor activities and enjoying the city's beautiful sights.\n\nIt's worth noting that the search result included an unusual comment about Geminis, which isn't typically part of a weather report. This might be due to the search engine including some astrological information or a joke in its results. However, for the purpose of answering your question about the weather, we can focus on the fact that it's sunny in San Francisco.\n\nIf you need any more specific information about the weather in San Francisco, such as temperature, wind speed, or forecast for the coming days, please let me know, and I'd be happy to search for that information for you.", 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'run-dbac539a-33c8-4f0c-9e20-91f318371e7c', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': None}]}}
|
||||
|
||||
|
||||
Now let's get our list of states, and invoke from the third state (right before the tool get called):
|
||||
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
states = await client.threads.get_history(thread['thread_id'])
|
||||
|
||||
# We can confirm that this state is correct by checking the 'next' attribute and seeing that it is the tool call node
|
||||
state_to_replay = states[2]
|
||||
print(state_to_replay['next'])
|
||||
```
|
||||
|
||||
## Learn more
|
||||
=== "Javascript"
|
||||
|
||||
- [**LangGraph time travel guide**](../../how-tos/human_in_the_loop/time-travel.ipynb): learn more about using time travel in LangGraph.
|
||||
```js
|
||||
const states = await client.threads.getHistory(thread['thread_id']);
|
||||
|
||||
// We can confirm that this state is correct by checking the 'next' attribute and seeing that it is the tool call node
|
||||
const stateToReplay = states[2];
|
||||
console.log(stateToReplay['next']);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request GET --url <DEPLOYMENT_URL>/threads/<THREAD_ID>/history | jq -r '.[2].next'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
['action']
|
||||
|
||||
|
||||
|
||||
To rerun from a state, we need first issue an empty update to the thread state. Then we need to pass in the resulting `checkpoint_id` as follows:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
state_to_replay = states[2]
|
||||
updated_config = await client.threads.update_state(
|
||||
thread["thread_id"],
|
||||
{"messages": []},
|
||||
checkpoint_id=state_to_replay["checkpoint_id"]
|
||||
)
|
||||
async for chunk in client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistant_id, # graph_id
|
||||
input=None,
|
||||
stream_mode="updates",
|
||||
checkpoint_id=updated_config["checkpoint_id"]
|
||||
):
|
||||
if chunk.data and chunk.event != "metadata":
|
||||
print(chunk.data)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
const stateToReplay = states[2];
|
||||
const config = await client.threads.updateState(thread["thread_id"], { values: {"messages": [] }, checkpointId: stateToReplay["checkpoint_id"] });
|
||||
const streamResponse = client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistantId,
|
||||
{
|
||||
input: null,
|
||||
streamMode: "updates",
|
||||
checkpointId: config["checkpoint_id"]
|
||||
}
|
||||
);
|
||||
for await (const chunk of streamResponse) {
|
||||
if (chunk.data && chunk.event !== "metadata") {
|
||||
console.log(chunk.data);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request GET --url <DEPLOYMENT_URL>/threads/<THREAD_ID>/history | jq -c '
|
||||
.[2] as $state_to_replay |
|
||||
{
|
||||
values: { messages: .[2].values.messages[-1] },
|
||||
checkpoint_id: $state_to_replay.checkpoint_id
|
||||
}' | \
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/state \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data @- | jq .checkpoint_id | \
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"checkpoint_id\": \"$1\",
|
||||
\"stream_mode\": [
|
||||
\"updates\"
|
||||
]
|
||||
}" | \
|
||||
sed 's/\r$//' | \
|
||||
awk '
|
||||
/^event:/ {
|
||||
if (data_content != "" && event_type != "metadata") {
|
||||
print data_content "\n"
|
||||
}
|
||||
sub(/^event: /, "", $0)
|
||||
event_type = $0
|
||||
data_content = ""
|
||||
}
|
||||
/^data:/ {
|
||||
sub(/^data: /, "", $0)
|
||||
data_content = $0
|
||||
}
|
||||
END {
|
||||
if (data_content != "" && event_type != "metadata") {
|
||||
print data_content "\n"
|
||||
}
|
||||
}
|
||||
'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
{'action': {'messages': [{'content': '["I looked up: current weather in San Francisco. Result: It\'s sunny in San Francisco, but you better look out if you\'re a Gemini 😈."]', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'tool', 'name': 'search', 'id': 'eba650e5-400e-4938-8508-f878dcbcc532', 'tool_call_id': 'toolu_011vroKUtWU7SBdrngpgpFMn'}]}}
|
||||
{'agent': {'messages': [{'content': "Based on the search results, I can provide you with information about the current weather in San Francisco:\n\nThe weather in San Francisco is currently sunny. This is great news if you're planning any outdoor activities or simply want to enjoy a pleasant day in the city.\n\nIt's worth noting that the search result included an unusual comment about Geminis, which doesn't seem directly related to the weather. This appears to be a playful or humorous addition to the weather report, possibly from the source where this information was obtained.\n\nIs there anything else you'd like to know about the weather in San Francisco or any other information you need?", 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'run-bc6dca3f-a1e2-4f59-a69b-fe0515a348bb', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': None}]}}
|
||||
|
||||
|
||||
As we can see, the graph restarted from the tool node with the same input as our original graph run.
|
||||
|
||||
## Branch off from previous state
|
||||
|
||||
Using LangGraph's checkpointing, you can do more than just replay past states. You can branch off previous locations to let the agent explore alternate trajectories or to let a user "version control" changes in a workflow.
|
||||
|
||||
Let's show how to do this to edit the state at a particular point in time. Let's update the state to change the input to the tool
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
# Let's now get the last message in the state
|
||||
# This is the one with the tool calls that we want to update
|
||||
last_message = state_to_replay['values']['messages'][-1]
|
||||
|
||||
# Let's now update the args for that tool call
|
||||
last_message['tool_calls'][0]['args'] = {'query': 'current weather in SF'}
|
||||
|
||||
config = await client.threads.update_state(thread['thread_id'],{"messages":[last_message]},checkpoint_id=state_to_replay['checkpoint_id'])
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
// Let's now get the last message in the state
|
||||
// This is the one with the tool calls that we want to update
|
||||
let lastMessage = stateToReplay['values']['messages'][-1];
|
||||
|
||||
// Let's now update the args for that tool call
|
||||
lastMessage['tool_calls'][0]['args'] = { 'query': 'current weather in SF' };
|
||||
|
||||
const config = await client.threads.updateState(thread['thread_id'], { values: { "messages": [lastMessage] }, checkpointId: stateToReplay['checkpoint_id'] });
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl -s --request GET --url <DEPLOYMENT_URL>/threads/<THREAD_ID>/history | \
|
||||
jq -c '
|
||||
.[2] as $state_to_replay |
|
||||
.[2].values.messages[-1].tool_calls[0].args.query = "current weather in SF" |
|
||||
{
|
||||
values: { messages: .[2].values.messages[-1] },
|
||||
checkpoint_id: $state_to_replay.checkpoint_id
|
||||
}' | \
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/state \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data @-
|
||||
```
|
||||
|
||||
Now we can rerun our graph with this new config, starting from the `new_state`, which is a branch of our `state_to_replay`:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
async for chunk in client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistant_id,
|
||||
input=None,
|
||||
stream_mode="updates",
|
||||
checkpoint_id=config['checkpoint_id']
|
||||
):
|
||||
if chunk.data and chunk.event != "metadata":
|
||||
print(chunk.data)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
const streamResponse = client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistantId,
|
||||
{
|
||||
input: null,
|
||||
streamMode: "updates",
|
||||
checkpointId: config['checkpoint_id'],
|
||||
}
|
||||
);
|
||||
for await (const chunk of streamResponse) {
|
||||
if (chunk.data && chunk.event !== "metadata") {
|
||||
console.log(chunk.data);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl -s --request GET --url <DEPLOYMENT_URL>/threads/<THREAD_ID>/state | \
|
||||
jq -c '.checkpoint_id' | \
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"checkpoint_id\": \"$1\",
|
||||
\"stream_mode\": [
|
||||
\"updates\"
|
||||
]
|
||||
}" | \
|
||||
sed 's/\r$//' | \
|
||||
awk '
|
||||
/^event:/ {
|
||||
if (data_content != "" && event_type != "metadata") {
|
||||
print data_content "\n"
|
||||
}
|
||||
sub(/^event: /, "", $0)
|
||||
event_type = $0
|
||||
data_content = ""
|
||||
}
|
||||
/^data:/ {
|
||||
sub(/^data: /, "", $0)
|
||||
data_content = $0
|
||||
}
|
||||
END {
|
||||
if (data_content != "" && event_type != "metadata") {
|
||||
print data_content "\n"
|
||||
}
|
||||
}
|
||||
'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
|
||||
{'action': {'messages': [{'content': '["I looked up: current weather in SF. Result: It\'s sunny in San Francisco, but you better look out if you\'re a Gemini 😈."]', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'tool', 'name': 'search', 'id': '2baf9941-4fda-4081-9f87-d76795d289f1', 'tool_call_id': 'toolu_011vroKUtWU7SBdrngpgpFMn'}]}}
|
||||
{'agent': {'messages': [{'content': "Based on the search results, I can provide you with information about the current weather in San Francisco (SF):\n\nThe weather in San Francisco is currently sunny. This means it's a clear day with plenty of sunshine. \n\nIt's worth noting that the specific temperature wasn't provided in the search result, but sunny weather in San Francisco typically means comfortable temperatures. San Francisco is known for its mild climate, so even on sunny days, it's often not too hot.\n\nThe search result also included a playful reference to astrological signs, mentioning Gemini. However, this is likely just a joke or part of the search engine's presentation and not related to the actual weather conditions.\n\nIs there any specific information about the weather in San Francisco you'd like to know more about? I'd be happy to perform another search if you need details on temperature, wind conditions, or the forecast for the coming days.", 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'run-a83de52d-ed18-4402-9384-75c462485743', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': None}]}}
|
||||
|
||||
|
||||
As we can see, the search query changed from San Francisco to SF, just as we had hoped!
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
# How to wait for user input using `interrupt`
|
||||
|
||||
!!! tip "Prerequisites"
|
||||
|
||||
This guide assumes familiarity with the following concepts:
|
||||
|
||||
* [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#human-in-the-loop). Waiting for human input is a common HIL interaction pattern, allowing the agent to ask the user clarifying questions and await input before proceeding.
|
||||
|
||||
We can implement this 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.
|
||||
|
||||
## 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/wait-user-input.ipynb#agent) if you want to. 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 '{}'
|
||||
```
|
||||
|
||||
## Waiting for user input
|
||||
|
||||
### Initial invocation
|
||||
|
||||
Now, let's invoke our graph.
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
input = {
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Ask the user where they are, then look up the weather there",
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
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: "human",
|
||||
content: "Ask the user where they are, then look up the weather there" }
|
||||
]
|
||||
};
|
||||
|
||||
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\": \"Ask the user where they are, then look up the weather there\"}]},
|
||||
\"stream_mode\": [
|
||||
\"updates\"
|
||||
]
|
||||
}"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
{'agent': {'messages': [{'content': [{'text': "I'll help you ask the user about their location and then search for weather information.", 'type': 'text'}, {'id': 'toolu_012JeNEvyePZFWK39d52Wdwi', 'input': {'question': 'Where are you located?'}, 'name': 'AskHuman', 'type': 'tool_use'}], 'additional_kwargs': {}, 'response_metadata': {'id': 'msg_01UBEdS6UvuFMetdokNsykVG', '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': 438, 'output_tokens': 76}, 'model_name': 'claude-3-5-sonnet-20241022'}, 'type': 'ai', 'name': None, 'id': 'run-1b1210d8-39e0-4607-9f0e-0ea932d28d5c-0', 'example': False, 'tool_calls': [{'name': 'AskHuman', 'args': {'question': 'Where are you located?'}, 'id': 'toolu_012JeNEvyePZFWK39d52Wdwi', 'type': 'tool_call'}], 'invalid_tool_calls': [], 'usage_metadata': {'input_tokens': 438, 'output_tokens': 76, 'total_tokens': 514, 'input_token_details': {'cache_read': 0, 'cache_creation': 0}}}]}}
|
||||
{'__interrupt__': [{'value': 'Where are you located?', 'resumable': True, 'ns': ['ask_human:2d41f894-f297-211e-9bfe-1d162ecba54a'], 'when': 'during'}]}
|
||||
|
||||
You can see that our graph got interrupted inside the `ask_human` node, which is now waiting for a `location` to be provided.
|
||||
|
||||
### Providing human input
|
||||
|
||||
We can provide human input (`location`) by invoking the graph with a `Command(resume="<location>")`:
|
||||
|
||||
=== "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="san francisco"),
|
||||
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: "san francisco" },
|
||||
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\": \"san francisco\"
|
||||
},
|
||||
\"stream_mode\": [
|
||||
\"updates\"
|
||||
]
|
||||
}"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
{'ask_human': {'messages': [{'tool_call_id': 'toolu_012JeNEvyePZFWK39d52Wdwi', 'type': 'tool', 'content': 'san francisco'}]}}
|
||||
{'agent': {'messages': [{'content': [{'text': 'Let me search for the weather in San Francisco.', 'type': 'text'}, {'id': 'toolu_019f9Y7ST6rNeDQkDjFCHk6C', 'input': {'query': 'current weather in san francisco'}, 'name': 'search', 'type': 'tool_use'}], 'additional_kwargs': {}, 'response_metadata': {'id': 'msg_0152YFm7DtnzfZQuiMUzaSsw', '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': 527, 'output_tokens': 67}, 'model_name': 'claude-3-5-sonnet-20241022'}, 'type': 'ai', 'name': None, 'id': 'run-f509b5b2-eb30-4200-a8da-fa79ed68812a-0', 'example': False, 'tool_calls': [{'name': 'search', 'args': {'query': 'current weather in san francisco'}, 'id': 'toolu_019f9Y7ST6rNeDQkDjFCHk6C', 'type': 'tool_call'}], 'invalid_tool_calls': [], 'usage_metadata': {'input_tokens': 527, 'output_tokens': 67, 'total_tokens': 594, 'input_token_details': {'cache_read': 0, 'cache_creation': 0}}}]}}
|
||||
{'action': {'messages': [{'content': "I looked up: current weather in san francisco. Result: It's sunny in San Francisco, but you better look out if you're a Gemini 😈.", 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'tool', 'name': 'search', 'id': 'cbd0f623-cc12-48a2-8c18-3cbb943e46e0', 'tool_call_id': 'toolu_019f9Y7ST6rNeDQkDjFCHk6C', 'artifact': None, 'status': 'success'}]}}
|
||||
{'agent': {'messages': [{'content': "Based on the search results, it's currently sunny in San Francisco. Would you like any specific details about the weather forecast?", 'additional_kwargs': {}, 'response_metadata': {'id': 'msg_01FhzXj72CehBYkJGX69vsBc', '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': 639, 'output_tokens': 29}, 'model_name': 'claude-3-5-sonnet-20241022'}, 'type': 'ai', 'name': None, 'id': 'run-f48e818e-dd88-415e-9a0b-4a958498b553-0', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': {'input_tokens': 639, 'output_tokens': 29, 'total_tokens': 668, 'input_token_details': {'cache_read': 0, 'cache_creation': 0}}}]}}
|
||||
@@ -1,48 +1,19 @@
|
||||
# Run application
|
||||
# How to manage Assistants
|
||||
|
||||
!!!info "Prerequisites"
|
||||
- [Running agents](../../agents/run_agents.md#running-agents)
|
||||
!!! info "Prerequisites"
|
||||
|
||||
This guide shows how to submit a [run](../concepts/runs.md) to your application.
|
||||
- [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.
|
||||
|
||||
## Graph mode
|
||||
|
||||
### Specify input
|
||||
First define the input to your graph with in the "Input" section on the left side of the page, below the graph interface.
|
||||
To view your assistants, click the "Manage Assistants" button in the bottom left corner.
|
||||
|
||||
Studio will attempt to render a form for your input based on the graph's defined [state schema](../../concepts/low_level.md/#schema). To disable this, click the "View Raw" button, which will present you with a JSON editor.
|
||||
|
||||
Click the up/down arrows at the top of the "Input" section to toggle through and use previously submitted inputs.
|
||||
|
||||
### Run settings
|
||||
|
||||
#### Assistant
|
||||
|
||||
To specify the [assistant](../../concepts/assistants.md) that is used for the run click the settings button in the bottom left corner. If an assistant is currently selected the button will also list the assistant name. If no assistant is selected it will say "Manage Assistants".
|
||||
|
||||
Select the assistant to run and click the "Active" toggle at the top of the modal to activate it. [See here](./studio/manage_assistants.md) for more information on managing assistants.
|
||||
|
||||
#### Streaming
|
||||
Click the dropdown next to "Submit" and click the toggle to enable/disable streaming.
|
||||
|
||||
#### Breakpoints
|
||||
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).
|
||||
|
||||
### 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 cancel the ongoing run, click the "Cancel" button.
|
||||
This opens a modal for you to view all the assistants for the selected graph. Specify the assistant and its version you would like to mark as "Active", and this assistant will be used when submitting runs.
|
||||
|
||||
By default, the "Default configuration" option will be active. This option reflects the default configuration defined in your graph. Edits made to this configuration will be used to update the run-time configuration, but will not update or create a new assistant unless you click "Create new assistant".
|
||||
|
||||
## Chat mode
|
||||
Specify the input to your chat application in the bottom of the conversation panel. Click the "Send message" button to submit the input as a Human message and have the response streamed back.
|
||||
|
||||
To cancel the ongoing run, click the "Cancel" button. Click the "Show tool calls" toggle to hide/show tool calls in the conversation.
|
||||
|
||||
## Learn more
|
||||
|
||||
To run your application from a specific checkpoint in an existing thread, see [this guide](./threads_studio.md#edit-thread-history).
|
||||
Chat mode enables you to switch through the different assistants in your graph via the dropdown selector at the top of the page. To create, edit, or delete assistants, use Graph mode.
|
||||
@@ -1,28 +1,23 @@
|
||||
# Iterate on prompts
|
||||
# Prompt Engineering in LangGraph Studio
|
||||
|
||||
## Overview
|
||||
|
||||
LangGraph Studio supports two methods for modifying prompts in your graph: direct node editing and the LangSmith Playground interface.
|
||||
A central aspect of agent development is prompt engineering. LangGraph Studio makes it easy to iterate on the prompts used within your graph directly within the UI.
|
||||
|
||||
## Direct Node Editing
|
||||
## Setup
|
||||
|
||||
Studio allows you to edit prompts used inside individual nodes, directly from the graph interface.
|
||||
The first step is to define your [configuration](https://langchain-ai.github.io/langgraph/how-tos/configuration/) such that LangGraph Studio is aware of the prompts you want to iterate on and which nodes they are associated with.
|
||||
|
||||
!!! info "Prerequisites"
|
||||
### Reference
|
||||
|
||||
- [Assistants overview](../../concepts/assistants.md)
|
||||
When defining your configuration, you can use special metadata keys to instruct LangGraph Studio how to handle different fields. Here's a reference for the available configuration options:
|
||||
|
||||
### Graph Configuration
|
||||
#### `langgraph_nodes`
|
||||
|
||||
Define your [configuration](https://langchain-ai.github.io/langgraph/how-tos/configuration/) to specify prompt fields and their associated nodes using `langgraph_nodes` and `langgraph_type` keys.
|
||||
|
||||
#### Configuration Reference
|
||||
|
||||
##### `langgraph_nodes`
|
||||
|
||||
- **Description**: Specifies which nodes of the graph a configuration field is associated with.
|
||||
- **Description**: Specifies which graph nodes a configuration field is associated with.
|
||||
- **Value Type**: Array of strings, where each string is the name of a node in your graph.
|
||||
- **Usage Context**: Include in the `json_schema_extra` dictionary for Pydantic models or the `metadata["json_schema_extra"]` dictionary for dataclasses.
|
||||
- **Required**: No, but necessary if you want a field to be editable for specific nodes in the UI.
|
||||
- **Example**:
|
||||
```python
|
||||
system_prompt: str = Field(
|
||||
@@ -31,13 +26,14 @@ Define your [configuration](https://langchain-ai.github.io/langgraph/how-tos/con
|
||||
)
|
||||
```
|
||||
|
||||
##### `langgraph_type`
|
||||
#### `langgraph_type`
|
||||
|
||||
- **Description**: Specifies the type of configuration field, which determines how it's handled in the UI.
|
||||
- **Value Type**: String
|
||||
- **Supported Values**:
|
||||
- `"prompt"`: Indicates the field contains prompt text that should be treated specially in the UI.
|
||||
- **Usage Context**: Include in the `json_schema_extra` dictionary for Pydantic models or the `metadata["json_schema_extra"]` dictionary for dataclasses.
|
||||
- **Required**: No, but helpful for prompt fields to enable special handling.
|
||||
- **Example**:
|
||||
```python
|
||||
system_prompt: str = Field(
|
||||
@@ -49,7 +45,9 @@ Define your [configuration](https://langchain-ai.github.io/langgraph/how-tos/con
|
||||
)
|
||||
```
|
||||
|
||||
#### Example Configuration
|
||||
### Example
|
||||
|
||||
For example, if you have a node called `call_model` whose system prompt you want to iterate on, you can define a configuration like the following.
|
||||
|
||||
```python
|
||||
## Using Pydantic
|
||||
@@ -113,22 +111,30 @@ class Configuration:
|
||||
|
||||
```
|
||||
|
||||
### Editing prompts in UI
|
||||
## Iterating on prompts
|
||||
|
||||
1. Locate the gear icon on nodes with associated configuration fields
|
||||
2. Click to open the configuration modal
|
||||
3. Edit the values
|
||||
4. Save to update the current assistant version or create a new one
|
||||
### Node Configuration
|
||||
|
||||
## LangSmith Playground
|
||||
With this set up, running your graph and viewing in LangGraph Studio will result in the graph rendering like such.
|
||||
|
||||
The [LangSmith Playground](https://
|
||||
docs.smith.langchain.com/prompt_engineering/how_to_guides#playground) interface allows testing individual LLM calls without running the full graph:
|
||||
**Note the configuration icon in the top right corner of the `call_model` node**:
|
||||
|
||||
1. Select a thread
|
||||
2. Click "View LLM Runs" on a node. This lists all the LLM calls (if any) made inside the node.
|
||||
3. Select an LLM run to open in Playground
|
||||
4. Modify prompts and test different model and tool settings
|
||||
5. Copy updated prompts back to your graph
|
||||
{width=1200}
|
||||
|
||||
For advanced Playground features, click the expand button in the top right corner.
|
||||
Clicking this icon will open a modal where you can edit the configuration for all of the fields associated with the `call_model` node. From here, you can save your changes and apply them to the graph. Note that these values reflect the currently active assistant, and saving will update the assistant with the new values.
|
||||
|
||||
{width=1200}
|
||||
|
||||
### Playground
|
||||
|
||||
LangGraph Studio also supports prompt engineering through an integration with the LangSmith Playground. To do so:
|
||||
|
||||
1. Open an existing thread or create a new one.
|
||||
2. Within the thread log, any nodes that have made an LLM call will have a "View LLM Runs" button. Clicking this will open a popover with the LLM runs for that node.
|
||||
3. Select the LLM run you want to edit. This will open the LangSmith Playground with the selected LLM run.
|
||||
|
||||
{width=1200}
|
||||
|
||||
From here you can edit the prompt, test different model configurations and re-run just this LLM call without having to re-run the entire graph. When you are happy with your changes, you can copy the updated prompt back into your graph.
|
||||
|
||||
For more information on how to use the LangSmith Playground, see the [LangSmith Playground documentation](https://docs.smith.langchain.com/prompt_engineering/how_to_guides#playground).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# How to run multiple agents on the same thread
|
||||
|
||||
In LangGraph Platform, a thread is not explicitly associated with a particular agent.
|
||||
In LangGraph Cloud, a thread is not explicitly associated with a particular agent.
|
||||
This means that you can run multiple agents on the same thread, which allows a different agent to continue from an initial agent's progress.
|
||||
|
||||
In this example, we will create two agents and then call them both on the same thread.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Stateless Runs
|
||||
|
||||
Most of the time, you provide a `thread_id` to your client when you run your graph in order to keep track of prior runs through the persistent state implemented in LangGraph Platform. However, if you don't need to persist the runs you don't need to use the built in persistent state and can create stateless runs.
|
||||
Most of the time, you provide a `thread_id` to your client when you run your graph in order to keep track of prior runs through the persistent state implemented in LangGraph Cloud. However, if you don't need to persist the runs you don't need to use the built in persistent state and can create stateless runs.
|
||||
|
||||
## Setup
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# LangGraph Studio FAQs
|
||||
|
||||
## Why is my project failing to start?
|
||||
|
||||
A project may fail to start if the configuration file is defined incorrectly, or if required environment variables are missing. See [here](../../reference/cli.md#configuration-file) for how your configuration file should be defined.
|
||||
|
||||
## How does interrupt work?
|
||||
|
||||
When you select the `Interrupts` dropdown and select a node to interrupt the graph will pause execution before and after (unless the node goes straight to `END`) that node has run. This means that you will be able to both edit the state before the node is ran and the state after the node has ran. This is intended to allow developers more fine-grained control over the behavior of a node and make it easier to observe how the node is behaving. You will not be able to edit the state after the node has ran if the node is the final node in the graph.
|
||||
|
||||
For more information on interrupts and human in the loop, see [here](./human_in_the_loop.md).
|
||||
|
||||
## Why are extra edges showing up in my graph?
|
||||
|
||||
If you don't define your conditional edges carefully, you might notice extra edges appearing in your graph. This is because without proper definition, LangGraph Studio assumes the conditional edge could access all other nodes. In order for this to not be the case, you need to be explicit about how you define the nodes the conditional edge routes to. There are two ways you can do this:
|
||||
|
||||
### Solution 1: Include a path map
|
||||
|
||||
The first way to solve this is to add path maps to your conditional edges. A path map is just a dictionary or array that maps the possible outputs of your router function with the names of the nodes that each output corresponds to. The path map is passed as the third argument to the `add_conditional_edges` function like so:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
graph.add_conditional_edges("node_a", routing_function, {True: "node_b", False: "node_c"})
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```ts
|
||||
graph.addConditionalEdges("node_a", routingFunction, { true: "node_b", false: "node_c" });
|
||||
```
|
||||
|
||||
In this case, the routing function returns either True or False, which map to `node_b` and `node_c` respectively.
|
||||
|
||||
### Solution 2: Update the typing of the router (Python only)
|
||||
|
||||
Instead of passing a path map, you can also be explicit about the typing of your routing function by specifying the nodes it can map to using the `Literal` python definition. Here is an example of how to define a routing function in that way:
|
||||
|
||||
```python
|
||||
def routing_function(state: GraphState) -> Literal["node_b","node_c"]:
|
||||
if state['some_condition'] == True:
|
||||
return "node_b"
|
||||
else:
|
||||
return "node_c"
|
||||
```
|
||||
|
||||
|
||||
## Why is my graph taking so long to startup?
|
||||
|
||||
The LangGraph Studio interacts with a local LangGraph API server. To stay aligned with ongoing updates, the LangGraph API requires regular rebuilding. As a result, you may occasionally experience slight delays when starting up your project.
|
||||
@@ -1,19 +0,0 @@
|
||||
# Manage assistants
|
||||
|
||||
!!! info "Prerequisites"
|
||||
|
||||
- [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.
|
||||
|
||||
## Graph mode
|
||||
|
||||
To view your assistants, click the "Manage Assistants" button in the bottom left corner.
|
||||
|
||||
This opens a modal for you to view all the assistants for the selected graph. Specify the assistant and its version you would like to mark as "Active", and this assistant will be used when submitting runs.
|
||||
|
||||
By default, the "Default configuration" option will be active. This option reflects the default configuration defined in your graph. Edits made to this configuration will be used to update the run-time configuration, but will not update or create a new assistant unless you click "Create new assistant".
|
||||
|
||||
## Chat mode
|
||||
|
||||
Chat mode enables you to switch through the different assistants in your graph via the dropdown selector at the top of the page. To create, edit, or delete assistants, use Graph mode.
|
||||
@@ -7,21 +7,16 @@ LangGraph Studio supports connecting to two types of graphs:
|
||||
- Graphs deployed on [LangGraph Platform](../../../cloud/quick_start.md)
|
||||
- Graphs running locally via the [LangGraph Server](../../../tutorials/langgraph-platform/local-server.md).
|
||||
|
||||
LangGraph Studio is accessed from the LangSmith UI, within the LangGraph Platform Deployments tab.
|
||||
## Deployed Application
|
||||
|
||||
## Deployed application
|
||||
|
||||
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.
|
||||
For applications that are deployed 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.
|
||||
|
||||
## Local development server
|
||||
## Local Development Server
|
||||
|
||||
To test your locally running application using LangGraph Studio, ensure your application is set up following [this guide](https://langchain-ai.github.io/langgraph/cloud/deployment/setup/).
|
||||
|
||||
!!! info "LangSmith Tracing"
|
||||
For local development, if you do not wish to have data traced to LangSmith, set `LANGSMITH_TRACING=false` in your application's `.env` file. With tracing disabled, no data will leave your local server.
|
||||
|
||||
Next, install the [LangGraph CLI](../../../concepts/langgraph_cli.md):
|
||||
|
||||
```
|
||||
@@ -49,14 +44,15 @@ If successful, you will see the following logs:
|
||||
>
|
||||
> - LangGraph Studio Web UI: https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024
|
||||
|
||||
Once running, you will automatically be directed to LangGraph Studio.
|
||||
Once running, you will automatically be directed to LangGraph Studio.
|
||||
|
||||
For an already running server, access Studio by either:
|
||||
|
||||
1. Directly navigate to the following URL: `https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024`.
|
||||
2. Within LangSmith, navigate to the LangGraph Platform Deployments tab, click the "LangGraph Studio" button, enter `http://127.0.0.1:2024` and click "Connect".
|
||||
If your server is already running, to access Studio, either:
|
||||
|
||||
If running your server at a different host or port, simply update the `baseUrl` to match.
|
||||
1. Directly navigate to the following URL: `https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024`.
|
||||
2. Within LangSmith, navigate to the LangGraph Platform Deployments tab, click the "LangGraph Studio" button, enter `http://127.0.0.1:2024` and click "Connect".
|
||||
|
||||
If running your server at a different host or port, simply update the `baseUrl` to match.
|
||||
|
||||
### (Optional) Attach a debugger
|
||||
|
||||
@@ -73,8 +69,8 @@ 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",
|
||||
@@ -84,22 +80,23 @@ Add this configuration to `launch.json`:
|
||||
"port": 5678
|
||||
}
|
||||
}
|
||||
`
|
||||
Specify the port number you chose in the previous step.
|
||||
```
|
||||
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
|
||||
|
||||
For issues getting started, please see this [troubleshooting guide](../../../troubleshooting/studio.md).
|
||||
|
||||
## Next steps
|
||||
|
||||
See the following guides for more information on how to use Studio:
|
||||
See the following how-tos for more information on how to use Studio:
|
||||
|
||||
- [Run application](../invoke_studio.md)
|
||||
- [Manage assistants](./manage_assistants.md)
|
||||
- [Manage threads](../threads_studio.md)
|
||||
- [Iterate on prompts](../iterate_graph_studio.md)
|
||||
- [Debug LangSmith traces](../clone_traces_studio.md)
|
||||
- [Add node to dataset](../datasets_studio.md)
|
||||
- [How to manage Assistants](../invoke_studio.md)
|
||||
- [How to manage Threads](../threads_studio.md)
|
||||
- [How to create datasets](../datasets_studio.md)
|
||||
- [How to prompt engineer](../iterate_graph_studio.md)
|
||||
- [How to locally debug remote traces](../clone_traces_studio.md)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Manage threads
|
||||
# How to manage Threads
|
||||
|
||||
!!! info "Prerequisites"
|
||||
|
||||
@@ -6,36 +6,25 @@
|
||||
|
||||
Studio allows you to view threads from the server and edit their state.
|
||||
|
||||
## View threads
|
||||
## View Threads
|
||||
|
||||
### Graph mode
|
||||
|
||||
1. In the top of the right-hand pane, select the dropdown menu to view existing threads.
|
||||
1. In the top of the right-hand pane, select the `New Thread` dropdown menu to view existing threads.
|
||||
1. Select the desired thread, and the thread history will populate in the right-hand side of the page.
|
||||
1. To create a new thread, click `+ New Thread` and [submit a run](../how-tos/invoke_studio.md#graph-mode).
|
||||
|
||||
To view more granular information in the thread, drag the slider at the top of the page to the right. To view less information, drag the slider to the left. Additionally, collapse or expand individual turns, nodes, and keys of the state.
|
||||
|
||||
Switch between `Pretty` and `JSON` mode for different rendering formats.
|
||||
1. To create a new thread, select `+ New Thread`.
|
||||
|
||||
### Chat mode
|
||||
|
||||
1. View all threads in the right-hand pane of the page.
|
||||
2. Select the desired thread and the thread history will populate in the center panel.
|
||||
3. To create a new thread, click the plus button and [submit a run](../how-tos/invoke_studio.md#chat-mode).
|
||||
2. Click the plus button to create a new thread.
|
||||
|
||||
## Edit thread history
|
||||
## Edit Thread State
|
||||
|
||||
### Graph mode
|
||||
|
||||
To edit the state of the thread, select "edit node state" next to the desired node. Edit the node's output as desired and click "fork" to confirm. This will create a new forked run from the checkpoint of the selected node.
|
||||
|
||||
If you instead want to re-run the thread from a given checkpoint without editing the state, click the "Re-run from here". This will again create a new forked run from the selected checkpoint. This is useful for re-running with changes that are not specific to the state, such as the selected assistant.
|
||||
To edit the state of the thread, select "edit node state" next to the desired node. This enables you to edit the node's output and create a new fork of the thread history. For more information about time travel, [see here](../../concepts/time-travel.md).
|
||||
|
||||
### Chat mode
|
||||
|
||||
To edit a human message in the thread, click the edit button below the human message. Edit the message as desired and submit. This will create a new fork of the conversation history. To re-generate an AI message, click the retry icon below the AI message.
|
||||
|
||||
## Learn more
|
||||
|
||||
For more information about time travel, [see here](../../concepts/time-travel.md).
|
||||
|
||||
@@ -158,7 +158,7 @@ export default function HomePage() {
|
||||
}
|
||||
```
|
||||
|
||||
Under the hood, the `useStream()` hook will use the `streamMode: "messages-tuple"` to receive a stream of messages (i.e. individual LLM tokens) from any LangChain chat model invocations inside your graph nodes. Learn more about messages streaming in the [streaming](../how-tos/streaming.md#messages) guide.
|
||||
Under the hood, the `useStream()` hook will use the `streamMode: "messages-tuple"` to receive a stream of messages (i.e. individual LLM tokens) from any LangChain chat model invocations inside your graph nodes. Learn more about messages streaming in the [How to stream messages from your graph](./stream_messages.md) guide.
|
||||
|
||||
### Interrupts
|
||||
|
||||
@@ -476,7 +476,7 @@ The `useStream()` hook provides several callback options to help you respond to
|
||||
- `onError`: Called when an error occurs.
|
||||
- `onFinish`: Called when the stream is finished.
|
||||
- `onUpdateEvent`: Called when an update event is received.
|
||||
- `onCustomEvent`: Called when a custom event is received. See the [streaming](../../how-tos/streaming.md#stream-custom-data) guide to learn how to stream custom events.
|
||||
- `onCustomEvent`: Called when a custom event is received. See [Custom events](../../how-tos/streaming.ipynb#custom) to learn how to stream custom events.
|
||||
- `onMetadataEvent`: Called when a metadata event is received, which contains the Run ID and Thread ID.
|
||||
|
||||
## Learn More
|
||||
|
||||
@@ -1,491 +0,0 @@
|
||||
# Use threads
|
||||
|
||||
!!! info "Prerequisites"
|
||||
|
||||
- [Threads Overview](../concepts/threads.md)
|
||||
|
||||
In this guide, we will show how to create, view, and inspect threads.
|
||||
|
||||
## Create a thread
|
||||
|
||||
To run your graph and the state persisted, you must first create a thread.
|
||||
|
||||
### Empty thread
|
||||
|
||||
To create a new thread, use the [LangGraph SDK](../../concepts/sdk.md) `create` method. See the [Python](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/#langgraph_sdk.client.ThreadsClient.create) and [JS](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/js_ts_sdk_ref/#create_3) SDK reference docs for more information.
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
|
||||
client = get_client(url=<DEPLOYMENT_URL>)
|
||||
thread = await client.threads.create()
|
||||
|
||||
print(thread)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL> });
|
||||
const thread = await client.threads.create();
|
||||
|
||||
console.log(thread);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
{
|
||||
"thread_id": "123e4567-e89b-12d3-a456-426614174000",
|
||||
"created_at": "2025-05-12T14:04:08.268Z",
|
||||
"updated_at": "2025-05-12T14:04:08.268Z",
|
||||
"metadata": {},
|
||||
"status": "idle",
|
||||
"values": {}
|
||||
}
|
||||
|
||||
### Copy thread
|
||||
|
||||
Alternatively, if you already have a thread in your application whose state you wish to copy, you can use the `copy` method. This will create an independent thread whose history is identical to the original thread at the time of the operation. See the [Python](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/#langgraph_sdk.client.ThreadsClient.copy) and [JS](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/js_ts_sdk_ref/#copy) SDK reference docs for more information.
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
copied_thread = await client.threads.copy(<THREAD_ID>)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
const copiedThread = await client.threads.copy(<THREAD_ID>);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST --url <DEPLOYMENT_URL>/threads/<THREAD_ID>/copy \
|
||||
--header 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
### Prepopulated State
|
||||
|
||||
Finally, you can create a thread with an arbitrary pre-defined state by providing a list of `supersteps` into the `create` method. The `supersteps` describe a list of a sequence of state updates. For example:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
|
||||
client = get_client(url=<DEPLOYMENT_URL>)
|
||||
thread = await client.threads.create(
|
||||
graph_id="agent",
|
||||
supersteps=[
|
||||
{
|
||||
updates: [
|
||||
{
|
||||
values: {},
|
||||
as_node: '__input__',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
updates: [
|
||||
{
|
||||
values: {
|
||||
messages: [
|
||||
{
|
||||
type: 'human',
|
||||
content: 'hello',
|
||||
},
|
||||
],
|
||||
},
|
||||
as_node: '__start__',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
updates: [
|
||||
{
|
||||
values: {
|
||||
messages: [
|
||||
{
|
||||
content: 'Hello! How can I assist you today?',
|
||||
type: 'ai',
|
||||
},
|
||||
],
|
||||
},
|
||||
as_node: 'call_model',
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
print(thread)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL> });
|
||||
const thread = await client.threads.create({
|
||||
graphId: 'agent',
|
||||
supersteps: [
|
||||
{
|
||||
updates: [
|
||||
{
|
||||
values: {},
|
||||
asNode: '__input__',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
updates: [
|
||||
{
|
||||
values: {
|
||||
messages: [
|
||||
{
|
||||
type: 'human',
|
||||
content: 'hello',
|
||||
},
|
||||
],
|
||||
},
|
||||
asNode: '__start__',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
updates: [
|
||||
{
|
||||
values: {
|
||||
messages: [
|
||||
{
|
||||
content: 'Hello! How can I assist you today?',
|
||||
type: 'ai',
|
||||
},
|
||||
],
|
||||
},
|
||||
asNode: 'call_model',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
console.log(thread);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{"metadata":{"graph_id":"agent"},"supersteps":[{"updates":[{"values":{},"as_node":"__input__"}]},{"updates":[{"values":{"messages":[{"type":"human","content":"hello"}]},"as_node":"__start__"}]},{"updates":[{"values":{"messages":[{"content":"Hello\u0021 How can I assist you today?","type":"ai"}]},"as_node":"call_model"}]}]}'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
{
|
||||
"thread_id": "f15d70a1-27d4-4793-a897-de5609920b7d",
|
||||
"created_at": "2025-05-12T15:37:08.935038+00:00",
|
||||
"updated_at": "2025-05-12T15:37:08.935046+00:00",
|
||||
"metadata": {"graph_id": "agent"},
|
||||
"status": "idle",
|
||||
"config": {},
|
||||
"values": {
|
||||
"messages": [
|
||||
{
|
||||
"content": "hello",
|
||||
"additional_kwargs": {},
|
||||
"response_metadata": {},
|
||||
"type": "human",
|
||||
"name": null,
|
||||
"id": "8701f3be-959c-4b7c-852f-c2160699b4ab",
|
||||
"example": false
|
||||
},
|
||||
{
|
||||
"content": "Hello! How can I assist you today?",
|
||||
"additional_kwargs": {},
|
||||
"response_metadata": {},
|
||||
"type": "ai",
|
||||
"name": null,
|
||||
"id": "4d8ea561-7ca1-409a-99f7-6b67af3e1aa3",
|
||||
"example": false,
|
||||
"tool_calls": [],
|
||||
"invalid_tool_calls": [],
|
||||
"usage_metadata": null
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
## List threads
|
||||
|
||||
### LangGraph SDK
|
||||
|
||||
To list threads, use the [LangGraph SDK](../../concepts/sdk.md) `search` method. This will list the threads in the application that match the provided filters. See the [Python](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/#langgraph_sdk.client.ThreadsClient.search) and [JS](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/js_ts_sdk_ref/#search_2) SDK reference docs for more information.
|
||||
|
||||
#### Filter by thread status
|
||||
|
||||
Use the `status` field to filter threads based on their status. Supported values are `idle`, `busy`, `interrupted`, and `error`. See [here](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/?h=thread+status#langgraph_sdk.auth.types.ThreadStatus) for information on each status. For example, to view `idle` threads:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
print(await client.threads.search(status="idle",limit=1))
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
console.log(await client.threads.search({ status: "idle", limit: 1 }));
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/search \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{"status": "idle", "limit": 1}'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
[
|
||||
{
|
||||
'thread_id': 'cacf79bb-4248-4d01-aabc-938dbd60ed2c',
|
||||
'created_at': '2024-08-14T17:36:38.921660+00:00',
|
||||
'updated_at': '2024-08-14T17:36:38.921660+00:00',
|
||||
'metadata': {'graph_id': 'agent'},
|
||||
'status': 'idle',
|
||||
'config': {'configurable': {}}
|
||||
}
|
||||
]
|
||||
|
||||
#### Filter by metadata
|
||||
|
||||
The `search` method allows you to filter on metadata:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
print((await client.threads.search(metadata={"graph_id":"agent"},limit=1)))
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
console.log((await client.threads.search({ metadata: { "graph_id": "agent" }, limit: 1 })));
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/search \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{"metadata": {"graph_id":"agent"}, "limit": 1}'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
[
|
||||
{
|
||||
'thread_id': 'cacf79bb-4248-4d01-aabc-938dbd60ed2c',
|
||||
'created_at': '2024-08-14T17:36:38.921660+00:00',
|
||||
'updated_at': '2024-08-14T17:36:38.921660+00:00',
|
||||
'metadata': {'graph_id': 'agent'},
|
||||
'status': 'idle',
|
||||
'config': {'configurable': {}}
|
||||
}
|
||||
]
|
||||
|
||||
#### Sorting
|
||||
|
||||
The SDK also supports sorting threads by `thread_id`, `status`, `created_at`, and `updated_at` using the `sort_by` and `sort_order` params.
|
||||
|
||||
### LangGraph Platform UI
|
||||
|
||||
You can also view threads in a deployment via the LangGraph Platform UI.
|
||||
|
||||
Inside your deployment, select the "Threads" tab. This will load a table of all of the threads in your deployment.
|
||||
|
||||
To filter by thread status, select a status in the top bar. To sort by a supported property, click on the arrow icon for the desired column.
|
||||
|
||||
## Inspect threads
|
||||
|
||||
### LangGraph SDK
|
||||
|
||||
#### Get Thread
|
||||
|
||||
To view a specific thread given its `thread_id`, use the `get` method:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
print((await client.threads.get(<THREAD_ID>)))
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
console.log((await client.threads.get(<THREAD_ID>)));
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request GET \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID> \
|
||||
--header 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
{
|
||||
'thread_id': 'cacf79bb-4248-4d01-aabc-938dbd60ed2c',
|
||||
'created_at': '2024-08-14T17:36:38.921660+00:00',
|
||||
'updated_at': '2024-08-14T17:36:38.921660+00:00',
|
||||
'metadata': {'graph_id': 'agent'},
|
||||
'status': 'idle',
|
||||
'config': {'configurable': {}}
|
||||
}
|
||||
|
||||
#### Inspect Thread State
|
||||
|
||||
To view the current state of a given thread, use the `get_state` method:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
print((await client.threads.get_state(<THREAD_ID>)))
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
console.log((await client.threads.getState(<THREAD_ID>)));
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request GET \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/state \
|
||||
--header 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
{
|
||||
"values": {
|
||||
"messages": [
|
||||
{
|
||||
"content": "hello",
|
||||
"additional_kwargs": {},
|
||||
"response_metadata": {},
|
||||
"type": "human",
|
||||
"name": null,
|
||||
"id": "8701f3be-959c-4b7c-852f-c2160699b4ab",
|
||||
"example": false
|
||||
},
|
||||
{
|
||||
"content": "Hello! How can I assist you today?",
|
||||
"additional_kwargs": {},
|
||||
"response_metadata": {},
|
||||
"type": "ai",
|
||||
"name": null,
|
||||
"id": "4d8ea561-7ca1-409a-99f7-6b67af3e1aa3",
|
||||
"example": false,
|
||||
"tool_calls": [],
|
||||
"invalid_tool_calls": [],
|
||||
"usage_metadata": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"next": [],
|
||||
"tasks": [],
|
||||
"metadata": {
|
||||
"thread_id": "f15d70a1-27d4-4793-a897-de5609920b7d",
|
||||
"checkpoint_id": "1f02f46f-7308-616c-8000-1b158a9a6955",
|
||||
"graph_id": "agent_with_quite_a_long_name",
|
||||
"source": "update",
|
||||
"step": 1,
|
||||
"writes": {
|
||||
"call_model": {
|
||||
"messages": [
|
||||
{
|
||||
"content": "Hello! How can I assist you today?",
|
||||
"type": "ai"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"parents": {}
|
||||
},
|
||||
"created_at": "2025-05-12T15:37:09.008055+00:00",
|
||||
"checkpoint": {
|
||||
"checkpoint_id": "1f02f46f-733f-6b58-8001-ea90dcabb1bd",
|
||||
"thread_id": "f15d70a1-27d4-4793-a897-de5609920b7d",
|
||||
"checkpoint_ns": ""
|
||||
},
|
||||
"parent_checkpoint": {
|
||||
"checkpoint_id": "1f02f46f-7308-616c-8000-1b158a9a6955",
|
||||
"thread_id": "f15d70a1-27d4-4793-a897-de5609920b7d",
|
||||
"checkpoint_ns": ""
|
||||
},
|
||||
"checkpoint_id": "1f02f46f-733f-6b58-8001-ea90dcabb1bd",
|
||||
"parent_checkpoint_id": "1f02f46f-7308-616c-8000-1b158a9a6955"
|
||||
}
|
||||
|
||||
Optionally, to view the state of a thread at a given checkpoint, simply pass in the checkpoint id (or the entire checkpoint object):
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
thread_state = await client.threads.get_state(
|
||||
thread_id=<THREAD_ID>
|
||||
checkpoint_id=<CHECKPOINT_ID>
|
||||
)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
const threadState = await client.threads.getState(<THREAD_ID>, <CHECKPOINT_ID>);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request GET \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/state/<CHECKPOINT_ID> \
|
||||
--header 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
#### Inspect Full Thread History
|
||||
|
||||
To view a thread's history, use the `get_history` method. This returns a list of every state the thread experienced. For more information see the [Python](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/?h=thread+status#langgraph_sdk.client.ThreadsClient.get_history) and [JS](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/js_ts_sdk_ref/#gethistory) reference docs.
|
||||
|
||||
### LangGraph Platform UI
|
||||
|
||||
You can also view threads in a deployment via the LangGraph Platform UI.
|
||||
|
||||
Inside your deployment, select the "Threads" tab. This will load a table of all of the threads in your deployment.
|
||||
|
||||
Select a thread to inspect its current state. To view it's full history and for further debugging, open the thread in [LangGraph Studio](../../concepts//langgraph_studio.md).
|
||||
@@ -1,10 +1,10 @@
|
||||
# Use webhooks
|
||||
# Using Webhooks
|
||||
|
||||
When working with LangGraph Platform, you may want to use webhooks to receive updates after an API call completes. Webhooks are useful for triggering actions in your service once a run has finished processing. To implement this, you need to expose an endpoint that can accept `POST` requests and pass this endpoint as a `webhook` parameter in your API request.
|
||||
When working with LangGraph Cloud, you may want to use webhooks to receive updates after an API call completes. Webhooks are useful for triggering actions in your service once a run has finished processing. To implement this, you need to expose an endpoint that can accept `POST` requests and pass this endpoint as a `webhook` parameter in your API request.
|
||||
|
||||
Currently, the SDK does not provide built-in support for defining webhook endpoints, but you can specify them manually using API requests.
|
||||
|
||||
## Supported endpoints
|
||||
## Supported Endpoints
|
||||
|
||||
The following API endpoints accept a `webhook` parameter:
|
||||
|
||||
@@ -20,7 +20,7 @@ The following API endpoints accept a `webhook` parameter:
|
||||
|
||||
In this guide, we’ll show how to trigger a webhook after streaming a run.
|
||||
|
||||
## Set up your assistant and thread
|
||||
## Setting Up Your Assistant and Thread
|
||||
|
||||
Before making API calls, set up your assistant and thread.
|
||||
|
||||
@@ -56,8 +56,7 @@ curl --request POST \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
### Example Response
|
||||
```json
|
||||
{
|
||||
"thread_id": "9dde5490-2b67-47c8-aa14-4bfec88af217",
|
||||
@@ -70,9 +69,9 @@ Example response:
|
||||
}
|
||||
```
|
||||
|
||||
## Use a webhook with a graph run
|
||||
## Using a Webhook with a Graph Run
|
||||
|
||||
To use a webhook, specify the `webhook` parameter in your API request. When the run completes, LangGraph Platform sends a `POST` request to the specified webhook URL.
|
||||
To use a webhook, specify the `webhook` parameter in your API request. When the run completes, LangGraph Cloud sends a `POST` request to the specified webhook URL.
|
||||
|
||||
For example, if your server listens for webhook events at `https://my-server.app/my-webhook-endpoint`, include this in your request:
|
||||
|
||||
@@ -120,11 +119,11 @@ curl --request POST \
|
||||
}'
|
||||
```
|
||||
|
||||
## Webhook payload
|
||||
## 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 Cloud 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.
|
||||
|
||||
## Secure webhooks
|
||||
## Securing Webhooks
|
||||
|
||||
To ensure only authorized requests hit your webhook endpoint, consider adding a security token as a query parameter:
|
||||
|
||||
@@ -134,11 +133,15 @@ https://my-server.app/my-webhook-endpoint?token=YOUR_SECRET_TOKEN
|
||||
|
||||
Your server should extract and validate this token before processing requests.
|
||||
|
||||
## Test webhooks
|
||||
## Testing Webhooks
|
||||
|
||||
You can test your webhook using online services like:
|
||||
|
||||
- **[Beeceptor](https://beeceptor.com/)** – Quickly create a test endpoint and inspect incoming webhook payloads.
|
||||
- **[Webhook.site](https://webhook.site/)** – View, debug, and log incoming webhook requests in real time.
|
||||
|
||||
These tools help you verify that LangGraph Platform is correctly triggering and sending webhooks to your service.
|
||||
These tools help you verify that LangGraph Cloud is correctly triggering and sending webhooks to your service.
|
||||
|
||||
---
|
||||
|
||||
By following these steps, you can integrate webhooks into your LangGraph Cloud workflow, automating actions based on completed runs.
|
||||
|
||||
@@ -9,11 +9,16 @@ Before you begin, ensure you have the following:
|
||||
- A [GitHub account](https://github.com/)
|
||||
- A [LangSmith account](https://smith.langchain.com/) – free to sign up
|
||||
|
||||
This quickstart uses the [pre-built Python ReAct agent template](https://github.com/langchain-ai/react-agent), which requires the following:
|
||||
|
||||
- An API key for [Anthropic](https://console.anthropic.com/)
|
||||
- An API key for [Tavily](https://app.tavily.com/)
|
||||
|
||||
## 1. Create a repository on GitHub
|
||||
|
||||
To deploy an application to **LangGraph Platform**, your application code must reside in a GitHub repository. Both public and private repositories are supported. For this quickstart, use the [`new-langgraph-project` template](https://github.com/langchain-ai/react-agent) for your application:
|
||||
To deploy a LangGraph application to **LangGraph Cloud**, your application code must reside in a GitHub repository. Both public and private repositories are supported. For this quickstart, use the [pre-built Python ReAct agent template](https://github.com/langchain-ai/react-agent) for your application:
|
||||
|
||||
1. Go to the [`new-langgraph-project` repository](https://github.com/langchain-ai/new-langgraph-project) or [`new-langgraphjs-project` template](https://github.com/langchain-ai/new-langgraphjs-project).
|
||||
1. Go to the [ReAct Agent](https://github.com/langchain-ai/react-agent) repository.
|
||||
1. Click the `Fork` button in the top right corner to fork the repository to your GitHub account.
|
||||
1. Click **Create fork**.
|
||||
|
||||
@@ -21,9 +26,14 @@ To deploy an application to **LangGraph Platform**, your application code must r
|
||||
|
||||
1. Log in to [LangSmith](https://smith.langchain.com/).
|
||||
1. In the left sidebar, select **LangGraph Platform**.
|
||||
1. Click the **+ New Deployment** button. A pane will open where you can fill in the required fields.
|
||||
1. Click the **+ New Deployment** button. A modal will open where you can fill in the required fields.
|
||||
1. If you are a first time user or adding a private repository that has not been previously connected, click the **Import from GitHub** button and follow the instructions to connect your GitHub account.
|
||||
1. Select your New LangGraph Project repository.
|
||||
1. Select your ReAct Agent repository.
|
||||
1. In the **Environment Variables** section, set the following secrets:
|
||||
|
||||
- **ANTHROPIC_API_KEY**: Get an API key from [Anthropic](https://console.anthropic.com/).
|
||||
- **TAVILY_API_KEY**: Get an API key on the [Tavily website](https://app.tavily.com/).
|
||||
|
||||
1. Click **Submit** to deploy.
|
||||
|
||||
This may take about 15 minutes to complete. You can check the status in the **Deployment details** view.
|
||||
@@ -38,7 +48,7 @@ Once your application is deployed:
|
||||
LangGraph Studio will open to display your graph.
|
||||
|
||||
<figure markdown="1">
|
||||
[{: style="max-height:400px"}](deployment/img/langgraph_studio.png)
|
||||
[{: style="max-height:400px"}](deployment/img/09_langgraph_studio.png)
|
||||
<figcaption>
|
||||
Sample graph run in LangGraph Studio.
|
||||
</figcaption>
|
||||
@@ -115,7 +125,7 @@ You can now test the API:
|
||||
print("\n\n")
|
||||
```
|
||||
|
||||
=== "JavaScript SDK"
|
||||
=== "Javascript SDK"
|
||||
|
||||
1. Install the LangGraph JS SDK
|
||||
|
||||
@@ -171,7 +181,7 @@ You can now test the API:
|
||||
```
|
||||
|
||||
|
||||
## Next steps
|
||||
## Next Steps
|
||||
|
||||
Congratulations! You have deployed an application using LangGraph Platform.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>LangGraph Platform API Reference</title>
|
||||
<title>LangGraph Cloud API Reference</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# 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 Cloud API reference is available with each deployment at the `/docs` URL path (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 Cloud, authentication is required. Pass the `X-Api-Key` header with each request to the LangGraph Cloud API. The value of the header should be set to a valid LangSmith API key for the organization where the API is deployed.
|
||||
|
||||
Example `curl` command:
|
||||
```shell
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>LangGraph Platform API Reference</title>
|
||||
<title>LangGraph Cloud API Reference</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# LangGraph CLI
|
||||
|
||||
The LangGraph command line interface includes commands to build and run a LangGraph Platform API server locally in [Docker](https://www.docker.com/). For development and testing, you can use the CLI to deploy a local API server.
|
||||
The LangGraph command line interface includes commands to build and run a LangGraph Cloud API server locally in [Docker](https://www.docker.com/). For development and testing, you can use the CLI to deploy a local API server.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -39,7 +39,7 @@ The LangGraph CLI requires a JSON configuration file that follows this [schema](
|
||||
|
||||
| Key | Description |
|
||||
| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| <span style="white-space: nowrap;">`dependencies`</span> | **Required**. Array of dependencies for LangGraph Platform API server. Dependencies can be one of the following: <ul><li>A single period (`"."`), which will look for local Python packages.</li><li>The directory path where `pyproject.toml`, `setup.py` or `requirements.txt` is located.</br></br>For example, if `requirements.txt` is located in the root of the project directory, specify `"./"`. If it's located in a subdirectory called `local_package`, specify `"./local_package"`. Do not specify the string `"requirements.txt"` itself.</li><li>A Python package name.</li></ul> |
|
||||
| <span style="white-space: nowrap;">`dependencies`</span> | **Required**. Array of dependencies for LangGraph Cloud API server. Dependencies can be one of the following: <ul><li>A single period (`"."`), which will look for local Python packages.</li><li>The directory path where `pyproject.toml`, `setup.py` or `requirements.txt` is located.</br></br>For example, if `requirements.txt` is located in the root of the project directory, specify `"./"`. If it's located in a subdirectory called `local_package`, specify `"./local_package"`. Do not specify the string `"requirements.txt"` itself.</li><li>A Python package name.</li></ul> |
|
||||
| <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 creates an instance of `langgraph.graph.state.StateGraph` / `langgraph.graph.state.CompiledStateGraph`.</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`) |
|
||||
@@ -336,7 +336,7 @@ The LangGraph CLI requires a JSON configuration file that follows this [schema](
|
||||
|
||||
=== "Python"
|
||||
|
||||
Build LangGraph Platform API server Docker image.
|
||||
Build LangGraph Cloud API server Docker image.
|
||||
|
||||
**Usage**
|
||||
|
||||
@@ -350,13 +350,13 @@ The LangGraph CLI requires a JSON configuration file that follows this [schema](
|
||||
| -------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `--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. |
|
||||
| `--pull / --no-pull` | `--pull` | Build with latest remote Docker image. Use `--no-pull` for running the LangGraph Cloud API server with locally built images. |
|
||||
| `-c, --config FILE` | `langgraph.json` | Path to configuration file declaring dependencies, graphs and environment variables. |
|
||||
| `--help` | | Display command documentation. |
|
||||
|
||||
=== "JS"
|
||||
|
||||
Build LangGraph Platform API server Docker image.
|
||||
Build LangGraph Cloud API server Docker image.
|
||||
|
||||
**Usage**
|
||||
|
||||
@@ -379,7 +379,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 Cloud closed beta. Requires a license key for production use.
|
||||
|
||||
**Usage**
|
||||
|
||||
@@ -406,7 +406,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 Cloud closed beta. Requires a license key for production use.
|
||||
|
||||
**Usage**
|
||||
|
||||
@@ -432,7 +432,7 @@ The LangGraph CLI requires a JSON configuration file that follows this [schema](
|
||||
|
||||
=== "Python"
|
||||
|
||||
Generate a Dockerfile for building a LangGraph Platform API server Docker image.
|
||||
Generate a Dockerfile for building a LangGraph Cloud API server Docker image.
|
||||
|
||||
**Usage**
|
||||
|
||||
@@ -482,7 +482,7 @@ The LangGraph CLI requires a JSON configuration file that follows this [schema](
|
||||
|
||||
=== "JS"
|
||||
|
||||
Generate a Dockerfile for building a LangGraph Platform API server Docker image.
|
||||
Generate a Dockerfile for building a LangGraph Cloud API server Docker image.
|
||||
|
||||
**Usage**
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ See <a href="https://docs.smith.langchain.com/how_to_guides/tracing/sample_trace
|
||||
|
||||
Type of authentication for the LangGraph Server deployment. Valid values: `langsmith`, `noop`.
|
||||
|
||||
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`.
|
||||
For deployments to LangGraph Cloud, 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`.
|
||||
|
||||
## `LANGSMITH_RUNS_ENDPOINTS`
|
||||
|
||||
@@ -118,7 +118,7 @@ Defaults to `''`.
|
||||
## `REDIS_CLUSTER`
|
||||
|
||||
!!! 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 Cluster mode is only available in Self-Hosted Deployment models, LangGraph Cloud SaaS will provision a redis instance for you by default.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ While a router allows an LLM to make a single decision, more complex agent archi
|
||||
|
||||
This architecture allows for more complex and flexible agent behaviors, going beyond simple routing to enable dynamic problem-solving with multiple steps. Unlike the original [paper](https://arxiv.org/abs/2210.03629), today's agents rely on LLMs' [tool calling](#tool-calling) capabilities and operate on a list of [messages](./low_level.md#why-use-messages).
|
||||
|
||||
In LangGraph, you can use the prebuilt [agent](../agents/agents.md#2-create-an-agent) to get started with tool-calling agents.
|
||||
In LangGraph, you can use the prebuilt [agent](../agent/overview.md) to get started with tool-calling agents.
|
||||
|
||||
### Tool calling
|
||||
|
||||
@@ -75,7 +75,7 @@ Effective [memory management](../how-tos/memory.ipynb) enhances an agent's abili
|
||||
|
||||
### Planning
|
||||
|
||||
In a tool-calling [agent](../agents/overview.md#what-is-an-agent), an LLM is called repeatedly in a while-loop. At each step the agent decides which tools to call, and what the inputs to those tools should be. Those tools are then executed, and the outputs are fed back into the LLM as observations. The while-loop terminates when the agent decides it has enough information to solve the user request and it is not worth calling any more tools.
|
||||
In a tool-calling [agent](../agent/overview.md), an LLM is called repeatedly in a while-loop. At each step the agent decides which tools to call, and what the inputs to those tools should be. Those tools are then executed, and the outputs are fed back into the LLM as observations. The while-loop terminates when the agent decides it has enough information to solve the user request and it is not worth calling any more tools.
|
||||
|
||||
## Custom agent architectures
|
||||
|
||||
|
||||
@@ -11,19 +11,15 @@ Imagine a general-purpose writing agent built on a common graph architecture. Wh
|
||||
|
||||

|
||||
|
||||
## Configuring assistants
|
||||
## Configuring Assistants
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
* The LangGraph Cloud API provides several endpoints for creating and managing assistants their versions. See the [API reference](../cloud/reference/api/api_ref.html#tag/assistants) for more details.
|
||||
Once you've created an assistant, subsequent edits to that assistant will create new versions. See [this how-to](../cloud/how-tos/assistant_versioning.md) for more details on how to manage assistant versions.
|
||||
|
||||
@@ -22,14 +22,14 @@ In LangGraph Platform, authentication is handled by your [`@auth.authenticate`](
|
||||
|
||||
LangGraph Platform provides different security defaults:
|
||||
|
||||
### LangGraph Platform
|
||||
### LangGraph Cloud
|
||||
|
||||
- Uses LangSmith API keys by default
|
||||
- Requires valid API key in `x-api-key` header
|
||||
- Can be customized with your auth handler
|
||||
|
||||
!!! note "Custom auth"
|
||||
Custom auth **is supported** for all plans in LangGraph Platform.
|
||||
Custom auth **is supported** for all plans in LangGraph Cloud.
|
||||
|
||||
### Self-Hosted
|
||||
|
||||
|
||||
@@ -534,11 +534,11 @@
|
||||
"locked": false,
|
||||
"fontSize": 28,
|
||||
"fontFamily": 1,
|
||||
"text": "LangGraph Platform Deployment",
|
||||
"text": "LangGraph Cloud Deployment",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "top",
|
||||
"containerId": null,
|
||||
"originalText": "LangGraph Platform Deployment",
|
||||
"originalText": "LangGraph Cloud Deployment",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
|
||||
@@ -5,17 +5,18 @@ search:
|
||||
|
||||
# LangGraph Platform
|
||||
|
||||
Develop, deploy, scale, and manage agents with **LangGraph Platform** — the purpose-built platform for long-running, agentic workflows.
|
||||
|
||||
!!! tip "Get started with LangGraph Platform"
|
||||
|
||||
Check out the [LangGraph Platform quickstart](../tutorials/langgraph-platform/local-server.md) for instructions on how to use LangGraph Platform run a LangGraph application locally.
|
||||
|
||||
## Why use LangGraph Platform?
|
||||
**LangGraph Platform** is a commercial solution for deploying agentic applications to production, built on the open-source [LangGraph framework](../index.md).
|
||||
|
||||
<div align="center"><iframe width="560" height="315" src="https://www.youtube.com/embed/pfAQxBS5z88?si=XGS6Chydn6lhSO1S" title="What is LangGraph Platform?" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe></div>
|
||||
|
||||
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.
|
||||
!!! tip "Get started with LangGraph Platform"
|
||||
|
||||
Check out the [LangGraph Platform quickstart](../cloud/quick_start.md) for instructions on how to set up and use LangGraph Platform to do a cloud deployment.
|
||||
|
||||
|
||||
## Why use LangGraph Platform?
|
||||
|
||||
LangGraph Platform handles common issues that arise when deploying LLM applications to production, allowing you to focus on agent logic instead of managing server infrastructure.
|
||||
|
||||
- **[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.
|
||||
|
||||
@@ -31,6 +32,8 @@ LangGraph Platform makes it easy to get your agent running in production — wh
|
||||
|
||||
- **[Human-in-the-loop support](../cloud/how-tos/human_in_the_loop_breakpoint.md)**: In many applications, users require a way to intervene in agent processes. LangGraph Server provides specialized endpoints for human-in-the-loop scenarios, simplifying the integration of manual oversight into agent workflows.
|
||||
|
||||
- **[LangGraph Studio](./langgraph_studio.md)**: Enables visualization, interaction, and debugging of agentic systems that implement the LangGraph Server API protocol. Studio also integrates with LangSmith to enable tracing, evaluation, and prompt engineering.
|
||||
By using LangGraph Platform, you gain access to a robust, scalable deployment solution that mitigates these challenges, saving you the effort of implementing and maintaining them manually. This allows you to focus more on building effective agent behavior and less on solving deployment infrastructure issues.
|
||||
|
||||
- **[Deployment](./deployment_options.md)**: There are four ways to deploy on LangGraph Platform: [Cloud Saas](../concepts/langgraph_cloud.md), [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).
|
||||
## Deployment
|
||||
|
||||
There are several ways to deploy on LangGraph Platform. For more information, see [Deployment options](./deployment_options.md).
|
||||
|
||||
@@ -17,7 +17,7 @@ There are two versions of the self-hosted deployment: [Self-Hosted Data Plane](.
|
||||
|
||||
## Self-Hosted Data Plane
|
||||
|
||||
The [Self-Hosted Data Plane](../cloud/deployment/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. When using the Self-Hosted Data Plane version, you authenticate with a [LangSmith](https://smith.langchain.com/) API key.
|
||||
The [Self-Hosted Data Plane](./self_hosted.md.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. When using the Self-Hosted Data Plane version, you authenticate with a [LangSmith](https://smith.langchain.com/) API key.
|
||||
|
||||
| | [Control plane](../concepts/langgraph_control_plane.md) | [Data plane](../concepts/langgraph_data_plane.md) |
|
||||
|-------------------|-------------------|------------|
|
||||
|
||||
@@ -24,7 +24,7 @@ Feature Differences:
|
||||
|
||||
| | Lite | Enterprise |
|
||||
|-------|------------|------------|
|
||||
| [Cron Jobs](../cloud/concepts/cron_jobs.md) |❌|✅|
|
||||
| [Cron Jobs](../clouds/concepts/cron-jobs.md) |❌|✅|
|
||||
| [Custom Authentication](../concepts/auth.md) |❌|✅|
|
||||
| [Deployment options](../concepts/deployment_options.md) | Standalone container | Cloud Saas, Self-Hosted Data Plane, Self-Hosted Control Plane, Standalone container
|
||||
|
||||
@@ -58,11 +58,11 @@ LangGraph Server leverages a database for [persistence](persistence.md) and a ta
|
||||
|
||||
Currently, only [Postgres](https://www.postgresql.org/) is supported as a database for LangGraph Server and [Redis](https://redis.io/) as the task queue.
|
||||
|
||||
If you're deploying using [LangGraph Platform](./langgraph_cloud.md), these components are managed for you. If you're deploying LangGraph Server on your own infrastructure, you'll need to set up and manage these components yourself.
|
||||
If you're deploying using [LangGraph Cloud](./langgraph_cloud.md), these components are managed for you. If you're deploying LangGraph Server on your own infrastructure, you'll need to set up and manage these components yourself.
|
||||
|
||||
Please review the [deployment options](./deployment_options.md) guide for more information on how these components are set up and managed.
|
||||
|
||||
## Learn more
|
||||
|
||||
* LangGraph [Application Structure](./application_structure.md) guide explains how to structure your LangGraph application for deployment.
|
||||
* The [LangGraph Platform API Reference](../cloud/reference/api/api_ref.html) provides detailed information on the API endpoints and data models.
|
||||
* The [LangGraph Cloud API Reference](../cloud/reference/api/api_ref.html) provides detailed information on the API endpoints and data models.
|
||||
|
||||
@@ -17,28 +17,23 @@ LangGraph Studio is a specialized agent IDE that enables visualization, interact
|
||||
|
||||
## Features
|
||||
|
||||
Key features of LangGraph Studio:
|
||||
The key features of LangGraph Studio are:
|
||||
|
||||
- Visualize your graph architecture
|
||||
- [Run and interact with your agent](../cloud/how-tos/invoke_studio.md)
|
||||
- [Manage assistants](../cloud/how-tos/studio/manage_assistants.md.md)
|
||||
- [Manage threads](../cloud/how-tos/threads_studio.md)
|
||||
- [Iterate on prompts](../cloud/how-tos/iterate_graph_studio.md)
|
||||
- Manage [long term memory](memory.md)
|
||||
- Run and interact with your agent in a GUI
|
||||
- Create and manage [assistants](assistants.md)
|
||||
- View and manage [threads](../cloud/concepts/threads.md)
|
||||
- View and manage [long term memory](memory.md)
|
||||
- Debug agent state via [time travel](time-travel.md)
|
||||
|
||||
|
||||
LangGraph Studio works for graphs that are deployed on [LangGraph Platform](../cloud/quick_start.md) or for graphs that are running locally via the [LangGraph Server](../tutorials/langgraph-platform/local-server.md).
|
||||
|
||||
Studio supports two modes:
|
||||
LangGraph Studio supports two modes:
|
||||
|
||||
### Graph mode
|
||||
1. Graph
|
||||
2. Chat
|
||||
|
||||
Graph mode exposes the full feature-set of Studio and is useful when you would like as many details about the execution of your agent, including the nodes traversed, intermediate states, and LangSmith integrations (such as adding to datasets an playground).
|
||||
|
||||
### Chat mode
|
||||
|
||||
Chat mode is a simpler UI for iterating on and testing chat-specific agents. It is useful for business users and those who want to test overall agent behavior. Chat mode is only supported for graph's whose state includes or extends [`MessagesState`](https://langchain-ai.github.io/langgraph/how-tos/graph-api/#messagesstate).
|
||||
|
||||
## Learn more
|
||||
|
||||
- See this guide on how to [get started](../cloud/how-tos/studio/quick_start.md) with LangGraph Studio.
|
||||
Chat mode is a simpler UI for iterating on and testing chat-specific agents. It is useful for business users and those who want to test overall agent behavior.
|
||||
@@ -45,9 +45,9 @@ The first thing you do when you define a graph is define the `State` of the grap
|
||||
|
||||
### Schema
|
||||
|
||||
The main documented way to specify the schema of a graph is by using `TypedDict`. However, we also support [using a Pydantic BaseModel](../how-tos/graph-api.ipynb#use-pydantic-models-for-graph-state) as your graph state to add **default values** and additional data validation.
|
||||
The main documented way to specify the schema of a graph is by using `TypedDict`. However, we also support [using a Pydantic BaseModel](../how-tos/state-model.ipynb) as your graph state to add **default values** and additional data validation.
|
||||
|
||||
By default, the graph will have the same input and output schemas. If you want to change this, you can also specify explicit input and output schemas directly. This is useful when you have a lot of keys, and some are explicitly for input and others for output. See the [guide here](../how-tos/graph-api.ipynb#define-input-and-output-schemas) for how to use.
|
||||
By default, the graph will have the same input and output schemas. If you want to change this, you can also specify explicit input and output schemas directly. This is useful when you have a lot of keys, and some are explicitly for input and others for output. See the [notebook here](../how-tos/input_output_schema.ipynb) for how to use.
|
||||
|
||||
#### Multiple schemas
|
||||
|
||||
@@ -56,9 +56,9 @@ Typically, all graph nodes communicate with a single schema. This means that the
|
||||
- Internal nodes can pass information that is not required in the graph's input / output.
|
||||
- We may also want to use different input / output schemas for the graph. The output might, for example, only contain a single relevant output key.
|
||||
|
||||
It is possible to have nodes write to private state channels inside the graph for internal node communication. We can simply define a private schema, `PrivateState`. See [this guide](../how-tos/graph-api.ipynb#pass-private-state-between-nodes) for more detail.
|
||||
It is possible to have nodes write to private state channels inside the graph for internal node communication. We can simply define a private schema, `PrivateState`. See [this notebook](../how-tos/pass_private_state.ipynb) for more detail.
|
||||
|
||||
It is also possible to define explicit input and output schemas for a graph. In these cases, we define an "internal" schema that contains _all_ keys relevant to graph operations. But, we also define `input` and `output` schemas that are sub-sets of the "internal" schema to constrain the input and output of the graph. See [this guide](../how-tos/graph-api.ipynb#define-input-and-output-schemas) for more detail.
|
||||
It is also possible to define explicit input and output schemas for a graph. In these cases, we define an "internal" schema that contains _all_ keys relevant to graph operations. But, we also define `input` and `output` schemas that are sub-sets of the "internal" schema to constrain the input and output of the graph. See [this notebook](../how-tos/input_output_schema.ipynb) for more detail.
|
||||
|
||||
Let's look at an example:
|
||||
|
||||
@@ -352,7 +352,7 @@ def my_node(state: State) -> Command[Literal["my_other_node"]]:
|
||||
|
||||
When returning `Command` in your node functions, you must add return type annotations with the list of node names the node is routing to, e.g. `Command[Literal["my_other_node"]]`. This is necessary for the graph rendering and tells LangGraph that `my_node` can navigate to `my_other_node`.
|
||||
|
||||
Check out this [how-to guide](../how-tos/graph-api.ipynb#combine-control-flow-and-state-updates-with-command) for an end-to-end example of how to use `Command`.
|
||||
Check out this [how-to guide](../how-tos/command.ipynb) for an end-to-end example of how to use `Command`.
|
||||
|
||||
### When should I use Command instead of conditional edges?
|
||||
|
||||
@@ -379,7 +379,7 @@ def my_node(state: State) -> Command[Literal["other_subgraph"]]:
|
||||
|
||||
!!! important "State updates with `Command.PARENT`"
|
||||
|
||||
When you send updates from a subgraph node to a parent graph node for a key that's shared by both parent and subgraph [state schemas](#schema), you **must** define a [reducer](#reducers) for the key you're updating in the parent graph state. See this [example](../how-tos/graph-api.ipynb#navigate-to-a-node-in-a-parent-graph).
|
||||
When you send updates from a subgraph node to a parent graph node for a key that's shared by both parent and subgraph [state schemas](#schema), you **must** define a [reducer](#reducers) for the key you're updating in the parent graph state. See this [example](../how-tos/command.ipynb#navigating-to-a-node-in-a-parent-graph).
|
||||
|
||||
This is particularly useful when implementing [multi-agent handoffs](./multi_agent.md#handoffs).
|
||||
|
||||
@@ -435,7 +435,7 @@ def node_a(state, config):
|
||||
...
|
||||
```
|
||||
|
||||
See [this guide](../how-tos/graph-api.ipynb#add-runtime-configuration) for a full breakdown on configuration.
|
||||
See [this guide](../how-tos/configuration.ipynb) for a full breakdown on configuration.
|
||||
|
||||
### Recursion Limit
|
||||
|
||||
@@ -449,4 +449,4 @@ Read [this how-to](https://langchain-ai.github.io/langgraph/how-tos/recursion-li
|
||||
|
||||
## Visualization
|
||||
|
||||
It's often nice to be able to visualize graphs, especially as they get more complex. LangGraph comes with several built-in ways to visualize graphs. See [this how-to guide](../how-tos/graph-api.ipynb#visualize-your-graph) for more info.
|
||||
It's often nice to be able to visualize graphs, especially as they get more complex. LangGraph comes with several built-in ways to visualize graphs. See [this how-to guide](../how-tos/visualization.ipynb) for more info.
|
||||
|
||||
@@ -165,7 +165,7 @@ network = builder.compile()
|
||||
|
||||
### Supervisor
|
||||
|
||||
In this architecture, we define agents as nodes and add a supervisor node (LLM) that decides which agent nodes should be called next. We use [`Command`](./low_level.md#command) to route execution to the appropriate agent node based on supervisor's decision. This architecture also lends itself well to running multiple agents in parallel or using [map-reduce](../how-tos/graph-api.ipynb#map-reduce-and-the-send-api) pattern.
|
||||
In this architecture, we define agents as nodes and add a supervisor node (LLM) that decides which agent nodes should be called next. We use [`Command`](./low_level.md#command) to route execution to the appropriate agent node based on supervisor's decision. This architecture also lends itself well to running multiple agents in parallel or using [map-reduce](../how-tos/map-reduce.ipynb) pattern.
|
||||
|
||||
```python
|
||||
from typing import Literal
|
||||
|
||||
@@ -29,7 +29,7 @@ Checkpoint is a snapshot of the graph state saved at each super-step and is repr
|
||||
- `metadata`: Metadata associated with this checkpoint.
|
||||
- `values`: Values of the state channels at this point in time.
|
||||
- `next` A tuple of the node names to execute next in the graph.
|
||||
- `tasks`: A tuple of `PregelTask` objects that contain information about next tasks to be executed. If the step was previously attempted, it will include error information. If a graph was interrupted [dynamically](../how-tos/human_in_the_loop/breakpoints.ipynb#dynamic-breakpoints) from within a node, tasks will contain additional data associated with interrupts.
|
||||
- `tasks`: A tuple of `PregelTask` objects that contain information about next tasks to be executed. If the step was previously attempted, it will include error information. If a graph was interrupted [dynamically](../how-tos/human_in_the_loop/dynamic_breakpoints.ipynb) from within a node, tasks will contain additional data associated with interrupts.
|
||||
|
||||
Let's see what checkpoints are saved when a simple graph is invoked as follows:
|
||||
|
||||
@@ -428,7 +428,7 @@ for update in graph.stream(
|
||||
print(update)
|
||||
```
|
||||
|
||||
When we use the LangGraph Platform, either locally (e.g., in LangGraph Studio) or with LangGraph Platform, the base store is available to use by default and does not need to be specified during graph compilation. To enable semantic search, however, you **do** need to configure the indexing settings in your `langgraph.json` file. For example:
|
||||
When we use the LangGraph Platform, either locally (e.g., in LangGraph Studio) or with LangGraph Cloud, the base store is available to use by default and does not need to be specified during graph compilation. To enable semantic search, however, you **do** need to configure the indexing settings in your `langgraph.json` file. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -451,7 +451,7 @@ Under the hood, checkpointing is powered by checkpointer objects that conform to
|
||||
|
||||
* `langgraph-checkpoint`: The base interface for checkpointer savers ([BaseCheckpointSaver][langgraph.checkpoint.base.BaseCheckpointSaver]) and serialization/deserialization interface ([SerializerProtocol][langgraph.checkpoint.serde.base.SerializerProtocol]). Includes in-memory checkpointer implementation ([InMemorySaver][langgraph.checkpoint.memory.InMemorySaver]) for experimentation. LangGraph comes with `langgraph-checkpoint` included.
|
||||
* `langgraph-checkpoint-sqlite`: An implementation of LangGraph checkpointer that uses SQLite database ([SqliteSaver][langgraph.checkpoint.sqlite.SqliteSaver] / [AsyncSqliteSaver][langgraph.checkpoint.sqlite.aio.AsyncSqliteSaver]). Ideal for experimentation and local workflows. Needs to be installed separately.
|
||||
* `langgraph-checkpoint-postgres`: An advanced checkpointer that uses Postgres database ([PostgresSaver][langgraph.checkpoint.postgres.PostgresSaver] / [AsyncPostgresSaver][langgraph.checkpoint.postgres.aio.AsyncPostgresSaver]), used in LangGraph Platform. Ideal for using in production. Needs to be installed separately.
|
||||
* `langgraph-checkpoint-postgres`: An advanced checkpointer that uses Postgres database ([PostgresSaver][langgraph.checkpoint.postgres.PostgresSaver] / [AsyncPostgresSaver][langgraph.checkpoint.postgres.aio.AsyncPostgresSaver]), used in LangGraph Cloud. Ideal for using in production. Needs to be installed separately.
|
||||
|
||||
|
||||
### Checkpointer interface
|
||||
@@ -481,7 +481,7 @@ First, checkpointers facilitate [human-in-the-loop workflows](agentic_concepts.m
|
||||
|
||||
### Memory
|
||||
|
||||
Second, checkpointers allow for ["memory"](agentic_concepts.md#memory) between interactions. In the case of repeated human interactions (like conversations) any follow up messages can be sent to that thread, which will retain its memory of previous ones. See [this how-to guide](../how-tos/memory.ipynb) for an end-to-end example on how to add and manage conversation memory using checkpointers.
|
||||
Second, checkpointers allow for ["memory"](agentic_concepts.md#memory) between interactions. In the case of repeated human interactions (like conversations) any follow up messages can be sent to that thread, which will retain its memory of previous ones. See [this how-to guide](../how-tos/memory/manage-conversation-history.ipynb) for an end-to-end example on how to add and manage conversation memory using checkpointers.
|
||||
|
||||
### Time Travel
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ search:
|
||||
|
||||
|
||||
## Overview
|
||||
LangGraph Platform is a solution for deploying agentic applications in production.
|
||||
LangGraph Platform is a commercial solution for deploying agentic applications in production.
|
||||
There are three different plans for using it.
|
||||
|
||||
- **Developer**: All [LangSmith](https://smith.langchain.com/) users have access to this plan. You can sign up for this plan simply by creating a LangSmith account. This gives you access to the [Standalone Container (Lite)](./deployment_options.md) deployment option.
|
||||
@@ -33,7 +33,7 @@ There are three different plans for using it.
|
||||
| Publish/subscribe API for state | -- | Coming Soon! | Coming Soon! |
|
||||
| Scheduling prioritization | -- | Coming Soon! | Coming Soon! |
|
||||
|
||||
For pricing information, see [LangGraph Platform Pricing](https://www.langchain.com/langgraph-platform-pricing).
|
||||
Please see the [LangGraph Platform Pricing](https://www.langchain.com/langgraph-platform-pricing) for information on pricing.
|
||||
|
||||
## Related
|
||||
|
||||
|
||||
@@ -162,50 +162,7 @@ Use an MCP-compliant client to connect to the LangGraph server. The following ex
|
||||
|
||||
=== "Python"
|
||||
|
||||
|
||||
Install the adapter with:
|
||||
|
||||
```bash
|
||||
pip install langchain-mcp-adapters
|
||||
```
|
||||
|
||||
Here is an example of how to connect to a remote MCP endpoint and use an agent as a tool:
|
||||
|
||||
```python
|
||||
# Create server parameters for stdio connection
|
||||
from mcp import ClientSession
|
||||
from mcp.client.streamable_http import streamablehttp_client
|
||||
import asyncio
|
||||
|
||||
from langchain_mcp_adapters.tools import load_mcp_tools
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
|
||||
server_params = {
|
||||
"url": "https://mcp-finance-agent.xxx.us.langgraph.app/mcp",
|
||||
"headers": {
|
||||
"X-Api-Key":"lsv2_pt_your_api_key"
|
||||
}
|
||||
}
|
||||
|
||||
async def main():
|
||||
async with streamablehttp_client(**server_params) as (read, write, _):
|
||||
async with ClientSession(read, write) as session:
|
||||
# Initialize the connection
|
||||
await session.initialize()
|
||||
|
||||
# Load the remote graph as if it was a tool
|
||||
tools = await load_mcp_tools(session)
|
||||
|
||||
# Create and run a react agent with the tools
|
||||
agent = create_react_agent("openai:gpt-4.1", tools)
|
||||
|
||||
# Invoke the agent with a message
|
||||
agent_response = await agent.ainvoke({"messages": "What can the finance agent do for me?"})
|
||||
print(agent_response)
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
No official MCP client is available for Python yet.
|
||||
|
||||
|
||||
## Session behavior
|
||||
|
||||
@@ -97,4 +97,4 @@ After configuring the app properly and adding your API keys, you can start the a
|
||||
See the following guides for more information on how to deploy your app:
|
||||
|
||||
- **[Launch Local LangGraph Server](../tutorials/langgraph-platform/local-server.md)**: This quick start guide shows how to start a LangGraph Server locally for the **ReAct Agent** template. The steps are similar for other templates.
|
||||
- **[Deploy to LangGraph Platform](../cloud/quick_start.md)**: Deploy your LangGraph app using LangGraph Platform.
|
||||
- **[Deploy to LangGraph Cloud](../cloud/quick_start.md)**: Deploy your LangGraph app using LangGraph Cloud.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Add custom authentication
|
||||
# How to add custom authentication
|
||||
|
||||
!!! tip "Prerequisites"
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
|
||||
???+ note "Support by deployment type"
|
||||
|
||||
Custom auth is supported for all deployments in the **managed LangGraph Platform**, as well as **Enterprise** self-hosted plans. It is not supported for **Lite** self-hosted plans.
|
||||
Custom auth is supported for all deployments in the **managed LangGraph Cloud**, as well as **Enterprise** self-hosted plans. It is not supported for **Lite** self-hosted plans.
|
||||
|
||||
This guide shows how to add custom authentication to your LangGraph Platform application. This guide applies to both LangGraph Platform and self-hosted deployments. It does not apply to isolated usage of the LangGraph open source library in your own custom server.
|
||||
This guide shows how to add custom authentication to your LangGraph Platform application. This guide applies to both LangGraph Cloud and self-hosted deployments. It does not apply to isolated usage of the LangGraph open source library in your own custom server.
|
||||
|
||||
## 1. Implement authentication
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Document API authentication in OpenAPI
|
||||
# How to document API authentication in OpenAPI
|
||||
|
||||
This guide shows how to customize the OpenAPI security schema for your LangGraph Platform API documentation. A well-documented security schema helps API consumers understand how to authenticate with your API and even enables automatic client generation. See the [Authentication & Access Control conceptual guide](../../concepts/auth.md) for more details about LangGraph's authentication system.
|
||||
|
||||
@@ -11,9 +11,9 @@ This guide applies to all LangGraph Platform deployments (Cloud and self-hosted)
|
||||
|
||||
The default security scheme varies by deployment type:
|
||||
|
||||
=== "LangGraph Platform"
|
||||
=== "LangGraph Cloud"
|
||||
|
||||
By default, LangGraph Platform requires a LangSmith API key in the `x-api-key` header:
|
||||
By default, LangGraph Cloud requires a LangSmith API key in the `x-api-key` header:
|
||||
|
||||
```yaml
|
||||
components:
|
||||
|
||||
@@ -2677,55 +2677,6 @@
|
||||
"</details>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "5a2d23ae-ea3f-478b-8db6-791cd29cfb6c",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Async\n",
|
||||
"\n",
|
||||
"Using the [async](https://docs.python.org/3/library/asyncio.html) programming paradigm can produce significant performance improvements when running [IO-bound](https://en.wikipedia.org/wiki/I/O_bound) code concurrently (e.g., making concurrent API requests to a chat model provider).\n",
|
||||
"\n",
|
||||
"To convert a `sync` implementation of the graph to an `async` implementation, you will need to:\n",
|
||||
"\n",
|
||||
"1. Update `nodes` use `async def` instead of `def`.\n",
|
||||
"2. Update the code inside to use `await` appropriately.\n",
|
||||
"3. Invoke the graph with `.ainvoke` or `.astream` as desired.\n",
|
||||
"\n",
|
||||
"Because many LangChain objects implement the [Runnable Protocol](https://python.langchain.com/docs/expression_language/interface/) which has `async` variants of all the `sync` methods it's typically fairly quick to upgrade a `sync` graph to an `async` graph.\n",
|
||||
"\n",
|
||||
"See example below. To demonstrate async invocations of underlying LLMs, we will include a chat model:\n",
|
||||
"\n",
|
||||
"{!snippets/chat_model_tabs.md!}\n",
|
||||
"\n",
|
||||
"```python\n",
|
||||
"from langchain.chat_models import init_chat_model\n",
|
||||
"from langgraph.graph import MessagesState, StateGraph\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# highlight-next-line\n",
|
||||
"async def node(state: MessagesState): # (1)!\n",
|
||||
" # highlight-next-line\n",
|
||||
" new_message = await llm.ainvoke(state[\"messages\"]) # (2)!\n",
|
||||
" return {\"messages\": [new_message]}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"builder = StateGraph(MessagesState).add_node(node).set_entry_point(\"node\")\n",
|
||||
"graph = builder.compile()\n",
|
||||
"\n",
|
||||
"input_message = {\"role\": \"user\", \"content\": \"Hello\"}\n",
|
||||
"# highlight-next-line\n",
|
||||
"result = await graph.ainvoke({\"messages\": [input_message]}) # (3)!\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"1. Declare nodes to be async functions.\n",
|
||||
"2. Use async invocations when available within the node.\n",
|
||||
"3. Use async invocations on the graph object itself.\n",
|
||||
"\n",
|
||||
"!!! tip \"Async streaming\"\n",
|
||||
" See the [streaming guide](../../how-tos/streaming) for examples of streaming with async."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "d33ecddc-6818-41a3-9d0d-b1b1cbcd286d",
|
||||
@@ -3268,7 +3219,13 @@
|
||||
"from IPython.display import Image, display\n",
|
||||
"from langchain_core.runnables.graph import CurveStyle, MermaidDrawMethod, NodeStyles\n",
|
||||
"\n",
|
||||
"display(Image(app.get_graph().draw_mermaid_png()))"
|
||||
"display(\n",
|
||||
" Image(\n",
|
||||
" app.get_graph().draw_mermaid_png(\n",
|
||||
" draw_method=MermaidDrawMethod.API,\n",
|
||||
" )\n",
|
||||
" )\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -75,7 +75,7 @@ You should see your startup message printed when the server starts, and your cle
|
||||
|
||||
## Deploying
|
||||
|
||||
You can deploy your app as-is to LangGraph Platform or to your self-hosted platform.
|
||||
You can deploy your app as-is to LangGraph Cloud or to your self-hosted platform.
|
||||
|
||||
## Next steps
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ Now any request to your server will include the custom header `X-Custom-Header`
|
||||
|
||||
## Deploying
|
||||
|
||||
You can deploy this app as-is to LangGraph Platform or to your self-hosted platform.
|
||||
You can deploy this app as-is to LangGraph Cloud or to your self-hosted platform.
|
||||
|
||||
## Next steps
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ If you navigate to `localhost:2024/hello` in your browser (`2024` is the default
|
||||
|
||||
## Deploying
|
||||
|
||||
You can deploy this app as-is to LangGraph Platform or to your self-hosted platform.
|
||||
You can deploy this app as-is to LangGraph Cloud or to your self-hosted platform.
|
||||
|
||||
## Next steps
|
||||
|
||||
|
||||