Compare commits

...
202 Commits
Author SHA1 Message Date
Nuno Campos 0139e11ae5 0.4.7 2025-05-23 16:51:48 -07:00
Nuno CamposandGitHub bfbe55ab64 Fix stream mode not respected in subgraphs (#4806) 2025-05-23 16:47:01 -07:00
Nuno Campos 36478eb745 Fix stream mode not respected in subgraphs
- The default applied for subgraphs should be applied only when stream mode arg not passed in
2025-05-23 16:40:51 -07:00
Nuno CamposandGitHub 8fb91569b9 Add tests for stream_events when using imperative api (#4805) 2025-05-23 16:40:38 -07:00
Nuno Campos 9bf6728354 Try to make test less flaky 2025-05-23 16:32:51 -07:00
Nuno Campos 126a8f5bc6 Lock 2025-05-23 16:22:12 -07:00
Nuno Campos 9170f636d0 Lock 2025-05-23 16:14:15 -07:00
Nuno Campos 8207d3fefb Add tests for stream_events when using imperative api 2025-05-23 16:11:12 -07:00
Nuno Campos ade3f372a5 0.4.6 2025-05-23 15:24:07 -07:00
Nuno CamposandGitHub 3a55d1137b Fix exception handling for imperative tasks (#4802) 2025-05-23 15:23:15 -07:00
Nuno Campos bece43dc67 Fix 2025-05-23 15:06:18 -07:00
Nuno Campos 913b8d5e95 Lint 2025-05-23 14:32:45 -07:00
Andrew NguonlyandGitHub 29f6ea7f61 docs: Add warning about immutable deployment types (#4804)
* Add more details about database for deployment types. Clarify that deployment type cannot be changed.

* Add note about Development type disk capacity.
2025-05-23 14:04:46 -07:00
Nuno Campos 3c0d9346c2 Add sync test 2025-05-23 13:51:13 -07:00
Nuno Campos 0ebb78d9b2 Fix 2025-05-23 13:50:11 -07:00
Nuno Campos 70153ceba2 Fix exception handling for imperative tasks
- These exceptions should not be re-raised at end of tick, given they're handled explicitly by the developer in their entrypoint
2025-05-23 12:26:27 -07:00
Sydney RunkleandGitHub a9c87ed8b6 prebuilt: release 0.2.1 (#4801)
lockfile and version updates
2025-05-23 18:07:24 +00:00
Sydney RunkleandGitHub 837fe59e24 prebuilt: support provider builtin tools in create_react_agent (#4800) 2025-05-23 13:58:53 -04:00
Andrew NguonlyandGitHub 596a26461c docs: Add note about Enterprise plan in banner for all self-hosted deployment options. (#4797)
Add note about Enterprise plan in banner for all self-hosted deployment options.
2025-05-22 18:38:06 -07:00
Sydney RunkleandGitHub aff3be03dc prebuilt: release 0.2.0 (#4793) 2025-05-22 17:54:58 +00:00
David DuongandGitHub 1000b81eca feat(langgraph): push_messages should directly write to the state (#4791) 2025-05-22 19:41:51 +02:00
Tat Dat Duong 10c6ed7320 Code review 2025-05-22 19:35:44 +02:00
Tat Dat Duong 28e0e7f3ae feat(langgraph): push_messages should directly write to the state 2025-05-22 19:35:15 +02:00
Sydney RunkleandGitHub 87fa661ac0 temp: remove ToolInterruptNode code (#4792)
remove tool node specific code for post_model_hook release
2025-05-22 13:28:59 -04:00
David DuongandGitHub 3189b8fcda feat(sdk-js): Add rejoining of streams (#4744) 2025-05-22 19:19:07 +02:00
Tat Dat Duong 74b18acfc2 Bump to 0.0.78 2025-05-22 19:08:39 +02:00
Tat Dat Duong 1ff5e2966b Add joinStream method 2025-05-22 19:07:01 +02:00
Tat Dat Duong 102b1f63f3 Fix assumption on sessionStorage 2025-05-22 19:01:14 +02:00
Tat Dat Duong 74dbccd408 Rename to reconnectOnMount 2025-05-22 18:42:38 +02:00
Tat Dat Duong f5a2410ddc Make useStream more hackable 2025-05-22 18:38:09 +02:00
Sydney RunkleandGitHub 4cade428d8 docs: adding interactive graph widget (#4785) 2025-05-22 12:23:43 -04:00
Tat Dat Duong 5823a659fc use sessionStorage instead 2025-05-22 18:11:38 +02:00
Tat Dat Duong f2c32727e8 Fix invalid states 2025-05-22 18:09:16 +02:00
Tat Dat Duong 9d71d941fd resumable 2025-05-22 17:45:52 +02:00
Tat Dat Duong 0db618ae75 Further cleanup 2025-05-22 17:45:52 +02:00
Tat Dat Duong 49746ab3a3 Code cleanup 2025-05-22 17:45:51 +02:00
Tat Dat Duong 5c7ef9a4fc Fix race condition 2025-05-22 17:45:51 +02:00
Tat Dat Duong 62e688bb37 Add rejoining of streams 2025-05-22 17:45:51 +02:00
Sydney RunkleandGitHub 42eff39cd0 docs: fix top nav (#4783)
fix top nav
2025-05-21 19:30:13 -04:00
David DuongandGitHub 39172f2ff9 feat(graph): add push_message method to push manually to messages / message-tuple stream (#4722) 2025-05-22 01:01:48 +02:00
Nuno CamposandGitHub 0202c83e73 Apply same condition for stream_mode=values in suppress interrupt (#4782) 2025-05-21 15:55:55 -07:00
Tat Dat Duong ab924c72fa Add message state test 2025-05-22 00:49:23 +02:00
Nuno Campos 1833889316 Apply same condition for stream_mode=values in suppress interrupt 2025-05-21 15:23:27 -07:00
Tat Dat Duong 7a6bdb3441 Remove redundant cast 2025-05-22 00:23:26 +02:00
David DuongandGitHub ea7f45dba7 feat(sdk-py): resumable streams (#4765) 2025-05-22 00:19:43 +02:00
Tat Dat Duong d2875cc576 feat(graph): add push_message method to push manually to messages / message-tuple stream 2025-05-22 00:18:12 +02:00
Tat Dat Duong e77197a00f Here as well 2025-05-22 00:13:22 +02:00
Tat Dat Duong 30e6ea7aed Last uv.lock? 2025-05-22 00:13:10 +02:00
Tat Dat Duong 461afe0aa6 Again? 2025-05-22 00:12:40 +02:00
Tat Dat Duong 92f8dde61e uv.lock? 2025-05-22 00:07:56 +02:00
Tat Dat Duong f4f7a79907 Bump to 0.1.70 2025-05-22 00:03:48 +02:00
Tat Dat Duong 9c1267af1a Import re 2025-05-22 00:03:41 +02:00
Tat Dat Duong e0a4420b8e feat(sdk-py): resumable streams 2025-05-22 00:03:41 +02:00
Andrew NguonlyandGitHub 94a8067c12 docs: Remove outdated docs regarding graph construction (#4779)
Remove outdated docs regarding graph construction.
2025-05-21 12:21:32 -07:00
Lauren Hirata SinghandGitHub 957db60d89 docs: fix (#4778) 2025-05-21 14:48:36 -04:00
Lauren Hirata Singh a17efb1120 fix 2025-05-21 14:47:55 -04:00
Eugene YurtsevandGitHub d4b428de62 docs: improved llms.txt (#4777)
Improved llms.txt
2025-05-21 14:40:01 -04:00
Eugene YurtsevandGitHub fd0b70eb05 docs: improve llms.txt to generate title and description based on content (#4775)
Update llms.txt based on content
2025-05-21 14:39:13 -04:00
Lauren Hirata SinghandGitHub b8daf323ac docs: fix redirect (#4776) 2025-05-21 14:35:04 -04:00
Lauren Hirata Singh 2d3a3de1ce docs: fix redirect 2025-05-21 14:34:02 -04:00
Nuno CamposandGitHub 96e2f68b2d Only emit stream values chunks when the output channels have changed (#4774) 2025-05-21 13:09:42 -04:00
Eugene YurtsevandGitHub 364508ff20 docs: Add script to generate llms-txt links from yaml (#4771)
A first pass at the script to fix the immediate issue. Will follow up with a few additional improvements.
2025-05-21 11:54:23 -04:00
Eugene YurtsevandGitHub fa1af5c364 docs: replace llms.txt with updated links (#4772)
This is a stop gap solution to fix broken links.

This PR will be followed up with a better llms.txt file
2025-05-21 11:53:26 -04:00
le-codeur-rapideandGitHub 8ec29b4df7 docs: fix example pregel reducer (#4740) 2025-05-21 10:38:29 -04:00
Sydney RunkleandGitHub 06607e08ea prebuilts hitl: fix branching logic + add structural snapshot tests (#4767) 2025-05-21 09:29:59 -04:00
Lauren Hirata SinghandGitHub 0a8f54ca4a docs: add redirect (#4762) 2025-05-20 15:06:34 -04:00
Lauren Hirata Singh 9d41d439cc docs: add redirect 2025-05-20 14:52:02 -04:00
Leeroy BrunandGitHub ae5c01fbab docs: typo in npx command to install LangGraph CLI (#4761) 2025-05-20 17:08:02 +00:00
Sydney RunkleandGitHub bd52b1faa1 docs: deferred nodes (#4759) 2025-05-20 13:07:11 -04:00
Stefano LottiniandGitHub 4a04ca7268 [docs] Revise pip-install packages for PostgresSaver in "how-to/Persistence" notebook (#4752)
revise pip-install packages for PostgresSaver usage in how-to page
2025-05-20 15:46:39 +00:00
Yazan JianandGitHub 23b868da00 docs: fix async usage example from await .invoke() to `await .ainvo… (#4758)
docs: fix async usage example from `await .invoke()` to `await .ainvoke()`
2025-05-20 15:45:47 +00:00
David DuongandGitHub 51bfd16460 feat(sdk-js): add stream_resumable flag, that marks the stream as resumable (#4757) 2025-05-20 14:16:39 +02:00
Tat Dat Duong 325d9e3134 Bump to 0.0.77 2025-05-20 14:14:48 +02:00
Tat Dat Duong b052ebf984 feat(sdk-js): add stream_resumable flag, that marks the stream as resumable 2025-05-20 14:13:47 +02:00
Sydney RunkleandGitHub 1e938a692f docs: node caching (#4749) 2025-05-19 15:49:31 -04:00
Nuno CamposandGitHub 46a9d3159d Remove local_write utility (#4751)
- The validation isn't worth the cost of having to pass list of nodes to task config
2025-05-19 15:49:17 -04:00
Nuno CamposandGitHub d825e39df9 Print output for cached @task functions (#4750) 2025-05-19 11:42:16 -07:00
Nuno Campos 53a1e7c9de Print output for cached @task functions 2025-05-19 11:35:51 -07:00
Sydney RunkleandGitHub 7bd8616b1e feature: Implement post_model_hook and HumanInterruptNode (#4583) 2025-05-19 14:07:42 -04:00
William FHandGitHub 95f92069a7 sqlite: Add test for search with list filters (#4747) 2025-05-18 23:50:27 -07:00
William FHandGitHub 6b28319796 sqlite: update list_namespaces with max_depth (#4746)
sqlite: update on conflict
2025-05-18 23:27:06 -07:00
Didier DurandandGitHub 2ddf61201c docs: Fixing some typos (#4741) 2025-05-18 22:36:37 -07:00
Didier DurandandGitHub 9453ee08dc docs: Fix a few spelling mistakes (#4742)
Fixing some typos
2025-05-18 22:35:54 -07:00
William Fu-Hinthorn 15bafc54c8 Update lockfile 2025-05-17 21:55:46 -07:00
William FHandGitHub 725dd40fa7 Release sqlite store (#4737) 2025-05-17 21:49:03 -07:00
William FHandGitHub c2a1b3af07 docs: Clean up adjective use in readme (#4734) 2025-05-17 21:43:14 -07:00
William FHandGitHub 025b634d98 SqliteStore (#3608) 2025-05-17 21:40:19 -07:00
Brace SproulandGitHub 8edb3e7b65 release(sdk-js): 0.0.76 (#4733) 2025-05-16 14:59:50 -07:00
bracesproul c21cf9fc1d release(sdk-js): 0.0.76 2025-05-16 14:56:56 -07:00
David DuongandGitHub cb95393c67 fix: Allow users to config whether or not stream subgraphs (#4732) 2025-05-16 23:51:25 +02:00
bracesproul bb1edb4415 cr 2025-05-16 14:49:57 -07:00
bracesproul 6f1db4c60a fix: Allow users to config whether or not stream subgraphs 2025-05-16 14:49:28 -07:00
Vadym BardaandGitHub 119a03bb00 ci: fix benchmark command (#4729) 2025-05-16 12:53:46 -07:00
Vadym BardaandGitHub 09138048bc docs: document tuples in streaming guides (#4690) 2025-05-16 14:59:51 -04:00
David DuongandGitHub adc89440a6 feat(sdk-js): expose ID for SSE events, update joinStream (#4547) 2025-05-16 20:53:39 +02:00
Tat Dat Duong c65919b3b2 Remove onResponse 2025-05-16 11:52:07 -07:00
Tat Dat Duong 0fa2b6c600 Bump to 0.0.75 2025-05-16 11:50:35 -07:00
Tat Dat Duong 9b2071b103 Fix tests 2025-05-16 11:50:09 -07:00
Tat Dat Duong b3f13ee904 Add onRunCreated callback 2025-05-16 11:43:40 -07:00
Tat Dat Duong bf239a06e1 Add callback for response object to get headers 2025-05-16 11:43:39 -07:00
Tat Dat Duong cc25539018 feat(sdk-js): expose ID for SSE events, update joinStream 2025-05-16 11:43:39 -07:00
Lauren Hirata SinghandGitHub 654096625a docs: add redirect (#4727) 2025-05-16 11:28:45 -07:00
David DuongandGitHub 3ea1141d55 feat(sdk-js): switch from jest to vitest, add useStream FE tests (#4726) 2025-05-16 19:55:30 +02:00
Tat Dat Duong e9d1f5508a package.json 2025-05-16 10:53:06 -07:00
Tat Dat Duong c6157d90dd feat(sdk-js): switch from jest to vitest, add useStream FE tests 2025-05-16 10:48:11 -07:00
Lauren Hirata Singh f37a228b58 add redirect 2025-05-16 10:28:51 -07:00
Sydney RunkleandGitHub d34299ac39 ci: fix issue with cache in integration tests (#4725) 2025-05-16 16:49:29 +00:00
Sydney RunkleandGitHub 228a08b966 ci: migrate to uv! (#4698)
* Migrate to `uv`
* Format `pyproject.toml` files properly
* Remove upper bounds on dependencies, and bounds on dev dependencies
(we should be using latest)
* Move to hatch for packaing

In the future we should:
* Set up dependabot / automate lockfile updates and tests
* Add tests for min compatible versions (I'll do this right after merge)
* Use dynamic versioning
* Bump `pydantic` to v2.11.4 in the lockfile, we have some tests failing
2025-05-15 17:39:14 -07:00
Lauren Hirata SinghandGitHub 217795eb72 docs: fix broken img (#4716) 2025-05-15 13:37:40 -07:00
Lauren Hirata Singh e873df678b fix broken img 2025-05-15 13:33:36 -07:00
Hussein AkbarzadehandGitHub 2b603a6ab0 Update sql-agent.ipynb (#4702)
On line 586, the check_query function should use
check_query_system_prompt. Instead, small mistake it is using the
generate_query_system_prompt prompt which is obviously incorrect.
2025-05-15 20:26:36 +00:00
Lauren Hirata SinghandGitHub f60a06441b docs: fix nit (#4714) 2025-05-15 12:50:32 -07:00
Lauren Hirata Singh db5e956dc6 docs: fix nit 2025-05-15 12:48:49 -07:00
Yagnesh M. BhadiyadraandGitHub cacae7bd1f fix(docs): Correctify the Pass or Fail condition in the Graph API example. (#4712) 2025-05-15 19:37:36 +00:00
Vadym BardaandGitHub 60df867872 docs: update MCP example (#4713) 2025-05-15 14:59:35 -04:00
Vadym BardaandGitHub 6fb2a93212 langgraph: release 0.4.5 (#4709) 2025-05-15 13:33:02 -04:00
Vadym BardaandGitHub 3f8944c1fc checkpoint: release 2.0.26 (#4708) 2025-05-15 13:28:10 -04:00
Nuno CamposandGitHub e79f3ceedc Improve how we match cached writes for async imperative tasks (#4691)
- remove match_cached_writes from PregelRunner args (now called by
PregelLoop internally)
- this will be helpful when implementing distributed runner classes
2025-05-15 10:21:27 -07:00
Vadym BardaandGitHub a64414c87c langgraph: release 0.4.4 (#4704) 2025-05-15 11:20:59 -04:00
Nuno CamposandGitHub c9d85a22ec langgraph: fix drawing graph with __root__ channel (#4695) 2025-05-15 07:55:45 -07:00
PabloandGitHub 983243333c Missing space in langgraph_server.md (#4697)
Missing space
2025-05-15 08:39:01 -04:00
vbarda 3a1c02ff33 update for consistency 2025-05-15 08:38:25 -04:00
vbarda 5ab2aa79bb lint again 2025-05-14 22:11:09 -04:00
vbarda c33e64daa6 use list 2025-05-14 22:03:40 -04:00
vbarda 4ffae6065f lint + update 2025-05-14 22:01:54 -04:00
vbarda 54ddde9d4c update 2025-05-14 21:30:22 -04:00
vbarda 60c41ce69e update 2025-05-14 21:14:49 -04:00
vbarda efd33d860f update 2025-05-14 21:03:47 -04:00
vbarda 254a38560e langgraph: fix drawing graph with __root__ channel 2025-05-14 20:42:30 -04:00
Nuno CamposandGitHub 3487f4eba5 langgraph: fix graph drawing for self-loops (#4688)
Fixes #4685
2025-05-14 11:44:44 -07:00
Nuno Campos 3bdb7d09be Lint 2025-05-14 11:30:49 -07:00
Nuno Campos 3acf63a918 Lint 2025-05-14 11:29:26 -07:00
Nuno Campos f51e5e2bd7 Improve how we match cached writes for async imperative tasks
- remove match_cached_writes from PregelRunner args (now called by PregelLoop internally)
- this will be helpful when implementing distributed runner classes
2025-05-14 11:25:49 -07:00
lc-arjunandGitHub c1a4d77bd4 fix: update langgraph cli to include js and update template app description (#4687) 2025-05-14 07:04:04 -07:00
6e3eb6370e Update docs/docs/tutorials/langgraph-platform/local-server.md
Co-authored-by: William FH <13333726+hinthornw@users.noreply.github.com>
2025-05-14 06:56:36 -07:00
Arjun Natarajan ab0fa9dc77 use npx to install cli instead 2025-05-14 09:49:42 -04:00
vbarda b20130d4e4 langgraph: fix graph drawing for self-loops 2025-05-14 09:46:32 -04:00
Arjun Natarajan 5f821cf584 fix: update langgraph cli to include js and update template app description 2025-05-14 09:41:37 -04:00
lc-arjunandGitHub c7691081d1 fix(docs): template app names (#4682) 2025-05-13 15:25:39 -07:00
Arjun Natarajan 2ff2972b67 oops js is js not python 2025-05-13 18:21:20 -04:00
Arjun Natarajan 873873e640 fix(docs): template app names 2025-05-13 18:17:42 -04:00
lc-arjunandGitHub fba0d6f96c chore(docs): reformat troubleshooting section and remove faqs page (#4681) 2025-05-13 15:04:19 -07:00
Arjun Natarajan 8bc509578b fix formatting 2025-05-13 17:59:51 -04:00
Arjun Natarajan 72d085d9f8 reformat troubleshooting section and remove faqs page 2025-05-13 17:54:51 -04:00
ccurmeandGitHub 940c2b0e74 docs: add chat model tabs to models guide (#4679) 2025-05-13 17:50:26 -04:00
lc-arjunandGitHub e705ea1961 feat(docs): studio nits (#4680) 2025-05-13 13:53:02 -07:00
Arjun Natarajan 18e8d334ed nits to threads 2025-05-13 16:48:07 -04:00
Arjun Natarajan 392606f5f7 feat(docs): studio nits 2025-05-13 16:29:10 -04:00
lc-arjunandGitHub cff2be48c9 feat(docs): further improvements for studio guides docs (#4677) 2025-05-13 12:59:47 -07:00
Arjun Natarajan c63cc173b2 added page for running application 2025-05-13 15:54:46 -04:00
Lauren Hirata SinghandGitHub 2b8c295898 docs: Simplify LGP quickstarts (#4678)
- Change quickstarts to use `new-langgraph-project`, which doesn't
require API keys for Anthropic, Tavily, etc.
2025-05-13 11:47:48 -07:00
Arjun Natarajan 03c5547e34 update dataset page 2025-05-13 14:46:26 -04:00
Lauren Hirata Singh a7faae6b54 nit 2025-05-13 11:40:30 -07:00
Lauren Hirata Singh 1d3f19763d add js sample code back 2025-05-13 11:36:45 -07:00
Lauren Hirata Singh 16d5ce364c nit 2025-05-13 11:24:19 -07:00
Lauren Hirata Singh ce235d0eb1 edits based on feedback 2025-05-13 11:20:19 -07:00
Arjun Natarajan 2e17857ca7 verb tense 2025-05-13 13:57:18 -04:00
Arjun Natarajan 304a59a1c9 feat(docs): further improvements for studio guides docs 2025-05-13 13:52:14 -04:00
Lauren Hirata Singh 063a0e027b docs: Simplify LGP quickstarts 2025-05-13 10:51:48 -07:00
ccurmeandGitHub 4534d174f4 docs: move async guide into graph-api (#4676) 2025-05-13 13:30:23 -04:00
Lauren Hirata SinghandGitHub 8d9a99bb99 docs: address feedback (#4674) 2025-05-13 10:08:42 -07:00
Lauren Hirata Singh ee528f7d49 nit 2025-05-13 10:04:33 -07:00
Lauren Hirata Singh 9d3ea6fd7e docs: address feedback 2025-05-13 09:51:50 -07:00
Vadym BardaandGitHub d256469f37 docs: fix warnings in links (#4672) 2025-05-13 11:38:54 -04:00
William Fu-Hinthorn 138f0eb003 fix: (docs) Rm unused api keys 2025-05-13 08:07:13 -07:00
Lauren Hirata SinghandGitHub 1acad37bee docs: LGP nits (#4670) 2025-05-12 20:59:59 -07:00
Lauren Hirata Singh 0a7a7fb71f docs: LGP nits 2025-05-12 20:57:29 -07:00
lc-arjunandGitHub b6f3e25ef7 fix: use absolute links for sdk reference (#4669) 2025-05-12 19:44:36 -07:00
Arjun Natarajan 70a10b0b57 fix: use absolute links for sdk reference 2025-05-12 22:39:05 -04:00
William FHandGitHub bed5f80e2c Optionally use loop-safe asgi transport (#4668) 2025-05-12 19:31:44 -07:00
Vadym BardaandGitHub 8057efc80a docs: update cassettes & notebook runner processing (#4667) 2025-05-12 21:54:42 -04:00
ce4caa3ac8 docs: mcp adapters example for deployed graph (#4666)
Example to connect to the MCP server of a deployed graph in langgraph
platform (langchain-mcp-adapters).
The graph can be used as a MCP tool.


![image](https://github.com/user-attachments/assets/fb5c36d4-0f04-4814-b0b7-bf7fcf6f8381)

---------

Co-authored-by: Vadym Barda <vadym@langchain.dev>
2025-05-12 17:55:38 -07:00
Vadym BardaandGitHub ad315bb5e0 docs: update HITL platform docs (#4664) 2025-05-12 17:56:27 -04:00
Lauren Hirata SinghandGitHub 5b9c9057a2 docs: General clean up (#4665)
- Change LG Cloud > Platform
- Misc. cleanup
2025-05-12 17:41:49 -04:00
Lauren Hirata SinghandGitHub 280d612b2d Merge branch 'main' into clean 2025-05-12 17:26:17 -04:00
Lauren Hirata Singh ceee65c93c Fix headings 2025-05-12 14:25:34 -07:00
William Fu-Hinthorn a16176c5bf fix: (docs) Update openapi.json 2025-05-12 14:23:20 -07:00
lc-arjunandGitHub 5537adc16a feat(docs): threads (#4662) 2025-05-12 14:19:48 -07:00
Ido SalomonandGitHub 9fce01f884 [Proposal] add gitmcp badge for simple LLM-accessible documentation (#4502)
[gitmcp.io](https://gitmcp.io/) provides easy access to the latest
langgraph docs through a remote, free, open-source MCP. It's supported
by all major clients (including the new claude.ai web client). It
enables two access points -
- Empower AI agents (e.g., Cursor) with live documentation context to
prevent hallucinations.
- Chat with the documentation in the browser via the embedded chat

This PR proposes adding a new badge to help users make the most of
Langgraph's documentation with GitMCP. The badge is
[customizable](https://github.com/idosal/git-mcp?tab=readme-ov-file#-gitmcp-badge).
The count is a live count of users accessing Langgraph's documentation
through GitMCP.

Example:

[![GitMCP](https://img.shields.io/endpoint?url=https://gitmcp.io/badge/langchain-ai/langgraph)](https://gitmcp.io/langchain-ai/langgraph)

You can see a demo with Langgraph's GitHub pages at gitmcp.io.

Please let us know what you think :)
2025-05-12 17:08:47 -04:00
Arjun Natarajan e1ea8c9538 pr feedback 2025-05-12 16:55:03 -04:00
Lauren Hirata Singh 7ea406648f Simplify assistants 2025-05-12 13:37:25 -07:00
Lauren Hirata Singh f5423783f7 Change LG Cloud > Platform 2025-05-12 12:44:50 -07:00
Arjun Natarajan 04a42c014b rm accidental change 2025-05-12 15:22:26 -04:00
Arjun Natarajan 1e405274ba feat(docs): threads 2025-05-12 15:18:41 -04:00
Vadym BardaandGitHub ae5b4e834e docs: replace state_modifier w/ prompt in func how-tos (#4659) 2025-05-12 13:46:50 +00:00
André MenezesandGitHub 3d394740ce Fix AsyncPostgresStore._cursor row type (#4623)
`AsyncPostgresStore._cursor` is expected to yield
`AsyncCursor[DictRow]`. In the `else` branch the `row_factory` was not
defined, meaning that it would yield `AsyncCursor[TupleRow]` (default).
This resulted in an error in
[langgraph/store/postgres/aio.py#L249](https://github.com/langchain-ai/langgraph/blob/main/libs/checkpoint-postgres/langgraph/store/postgres/aio.py#L249)
(`TypeError: tuple indices must be integers or slices, not str`).
2025-05-12 13:34:56 +00:00
Emmanuel FerdmanandGitHub 5ab6f77982 Fix invalid channels error message (#4357)
# PR Summary
This small PR resolves the error formatting in
`libs/langgraph/langgraph/pregel/algo.py` so the `proc.channels` will be
evaluated in the message.

Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
2025-05-12 09:29:49 -04:00
Hamza KyamanywaandGitHub fe11a71d28 docs: update link in agentic concepts tutorial for map-reduce implementation (#4650)
## PR for:
- Fix broken link in agentic concepts tutorial for map-reduce
implementation
2025-05-12 07:46:30 -04:00
Sydney RunkleandGitHub 347fbf1625 docs: remove unused annotation comments in readmes (#4656) 2025-05-12 07:45:22 -04:00
Sydney RunkleandGitHub 0b5529108f docs: fix broken API reference link (#4657) 2025-05-12 07:45:07 -04:00
Lauren Hirata SinghandGitHub daf20b610b docs: General language cleanup (#4649)
- README updates based on feedback
- Auth tutorial cleanup
2025-05-12 06:11:08 +00:00
William FHandGitHub f7c4f96e61 Ref index (#4644) 2025-05-11 16:46:03 -07:00
Andrew NguonlyandGitHub 4bdbebb3e4 docs: Update beta labels for deployment options (#4643)
@lnhsingh, let me know if you're ok with this copy and messaging
placement.
2025-05-11 10:33:38 -07:00
ce5d3c9c5a docs: Remove self-hosted lite deployment option (#4645)
### Summary
Self-Hosted Lite is a server version, not a deployment option.

---------

Co-authored-by: Lauren Hirata Singh <lauren@langchain.dev>
2025-05-11 10:29:01 -07:00
Eugene YurtsevandGitHub dda4b02095 docs: remove customer support bot example (#4641)
Hide example temporarily until we bring it up to date
2025-05-09 21:19:35 -04:00
lc-arjunandGitHub 0c57f2ab7e fix(docs): update footer color (#4646) 2025-05-09 18:09:44 -07:00
Arjun Natarajan 6f6ae8edce update footer color 2025-05-09 21:05:43 -04:00
lc-arjunandGitHub ee11294351 feat(docs): assistants and threads and faqs (#4642) 2025-05-09 15:35:11 -07:00
Arjun Natarajan 6d3dae6fd1 assistants and threads and faqs 2025-05-09 18:11:44 -04:00
Eugene YurtsevandGitHub 200c09b662 docs: re-enable strict mode (#4638)
- Turn on strict mode to fail docs build if there are any internal
broken anchor links.
- Fix broken anchor links.
- Delete storm tutorial that has fake markdown inside it (we'll figure
out how to deal with it later)
2025-05-09 17:25:11 -04:00
Eugene YurtsevandGitHub af80d6b339 docs: re-org side bar (#4640) 2025-05-09 17:21:15 -04:00
Eugene YurtsevandGitHub 220f9aaaab docs: fix formatting in overview for getting started tutorial (#4639) 2025-05-09 16:48:13 -04:00
309 changed files with 30933 additions and 32424 deletions
-88
View File
@@ -1,88 +0,0 @@
# An action for setting up poetry install with caching.
# Using a custom action since the default action does not
# take poetry install groups into account.
# Action code from:
# https://github.com/actions/setup-python/issues/505#issuecomment-1273013236
name: poetry-install-with-caching
description: Poetry install with support for caching of dependency groups.
inputs:
python-version:
description: Python version, supporting MAJOR.MINOR only
required: true
poetry-version:
description: Poetry version
required: true
cache-key:
description: Cache key to use for manual handling of caching
required: true
runs:
using: composite
steps:
- uses: actions/setup-python@v5
name: Setup python ${{ inputs.python-version }}
id: setup-python
with:
python-version: ${{ inputs.python-version }}
- uses: actions/cache@v3
id: cache-bin-poetry
name: Cache Poetry binary - Python ${{ inputs.python-version }}
env:
SEGMENT_DOWNLOAD_TIMEOUT_MIN: "1"
with:
path: |
/opt/pipx/venvs/poetry
# This step caches the poetry installation, so make sure it's keyed on the poetry version as well.
key: bin-poetry-${{ runner.os }}-${{ runner.arch }}-py-${{ inputs.python-version }}-${{ inputs.poetry-version }}
- name: Refresh shell hashtable and fixup softlinks
if: steps.cache-bin-poetry.outputs.cache-hit == 'true'
shell: bash
env:
POETRY_VERSION: ${{ inputs.poetry-version }}
PYTHON_VERSION: ${{ inputs.python-version }}
run: |
set -eux
# Refresh the shell hashtable, to ensure correct `which` output.
hash -r
# `actions/cache@v3` doesn't always seem able to correctly unpack softlinks.
# Delete and recreate the softlinks pipx expects to have.
rm /opt/pipx/venvs/poetry/bin/python
cd /opt/pipx/venvs/poetry/bin
ln -s "$(which "python$PYTHON_VERSION")" python
chmod +x python
cd /opt/pipx_bin/
ln -s /opt/pipx/venvs/poetry/bin/poetry poetry
chmod +x poetry
# Ensure everything got set up correctly.
/opt/pipx/venvs/poetry/bin/python --version
/opt/pipx_bin/poetry --version
- name: Install poetry
if: steps.cache-bin-poetry.outputs.cache-hit != 'true'
shell: bash
env:
POETRY_VERSION: ${{ inputs.poetry-version }}
PYTHON_VERSION: ${{ inputs.python-version }}
# Install poetry using the python version installed by setup-python step.
run: pipx install "poetry==$POETRY_VERSION" --python '${{ steps.setup-python.outputs.python-path }}' --verbose
- name: Restore pip and poetry cached dependencies
uses: actions/cache@v3
env:
SEGMENT_DOWNLOAD_TIMEOUT_MIN: "4"
with:
path: |
~/.cache/pip
~/.cache/pypoetry/virtualenvs
~/.cache/pypoetry/cache
~/.cache/pypoetry/artifacts
./.venv
key: py-deps-${{ runner.os }}-${{ runner.arch }}-py-${{ inputs.python-version }}-poetry-${{ inputs.poetry-version }}-${{ inputs.cache-key }}-${{ hashFiles('./poetry.lock') }}
+5 -7
View File
@@ -3,9 +3,6 @@ name: CLI integration test
on:
workflow_call:
env:
POETRY_VERSION: "2.1.2"
jobs:
build:
runs-on: ubuntu-latest
@@ -25,13 +22,14 @@ jobs:
uses: Ana06/get-changed-files@v2.3.0
with:
filter: "libs/cli/**"
- name: Set up Python ${{ matrix.python-version }} + Poetry ${{ env.POETRY_VERSION }}
- name: Set up Python ${{ matrix.python-version }}
if: steps.changed-files.outputs.all
uses: "./.github/actions/poetry_setup"
uses: astral-sh/setup-uv@v6
with:
python-version: ${{ matrix.python-version }}
poetry-version: ${{ env.POETRY_VERSION }}
cache-key: integration-test-cli
enable-cache: true
cache-suffix: "cli-integration-test"
ignore-nothing-to-cache: true
- name: Setup env
if: steps.changed-files.outputs.all
working-directory: libs/cli/examples
+8 -33
View File
@@ -9,8 +9,6 @@ on:
description: "From which folder this pipeline executes"
env:
POETRY_VERSION: "2.1.2"
# This env var allows us to get inline annotations when ruff has complaints.
RUFF_OUTPUT_FORMAT: github
@@ -36,32 +34,18 @@ jobs:
uses: Ana06/get-changed-files@v2.3.0
with:
filter: "${{ inputs.working-directory }}/**"
- name: Set up Python ${{ matrix.python-version }} + Poetry ${{ env.POETRY_VERSION }}
- name: Set up Python ${{ matrix.python-version }}
if: steps.changed-files.outputs.all
uses: "./.github/actions/poetry_setup"
uses: astral-sh/setup-uv@v6
with:
python-version: ${{ matrix.python-version }}
poetry-version: ${{ env.POETRY_VERSION }}
cache-key: lint-${{ inputs.working-directory }}
- name: Check Poetry File
if: steps.changed-files.outputs.all
shell: bash
working-directory: ${{ inputs.working-directory }}
run: poetry check
enable-cache: true
cache-suffix: lint-${{ inputs.working-directory }}
- name: Install dependencies
if: steps.changed-files.outputs.all
# Also installs dev/lint/test/typing dependencies, to ensure we have
# type hints for as many of our libraries as possible.
# This helps catch errors that require dependencies to be spotted, for example:
# https://github.com/langchain-ai/langchain/pull/10249/files#diff-935185cd488d015f026dcd9e19616ff62863e8cde8c0bee70318d3ccbca98341
#
# If you change this configuration, make sure to change the `cache-key`
# in the `poetry_setup` action above to stop using the old cache.
# It doesn't matter how you change it, any change will cause a cache-bust.
working-directory: ${{ inputs.working-directory }}
run: poetry install --with dev
run: uv sync --frozen --group dev
- name: Get .mypy_cache to speed up mypy
if: steps.changed-files.outputs.all
@@ -71,7 +55,7 @@ jobs:
with:
path: |
${{ inputs.working-directory }}/.mypy_cache
key: mypy-lint-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python-version }}-${{ inputs.working-directory }}-${{ hashFiles(format('{0}/poetry.lock', inputs.working-directory)) }}
key: mypy-lint-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python-version }}-${{ inputs.working-directory }}-${{ hashFiles(format('{0}/uv.lock', inputs.working-directory)) }}
- name: Analysing package code with our lint
if: steps.changed-files.outputs.all
@@ -86,17 +70,8 @@ jobs:
- name: Install test dependencies
if: steps.changed-files.outputs.all
# Also installs dev/lint/test/typing dependencies, to ensure we have
# type hints for as many of our libraries as possible.
# This helps catch errors that require dependencies to be spotted, for example:
# https://github.com/langchain-ai/langchain/pull/10249/files#diff-935185cd488d015f026dcd9e19616ff62863e8cde8c0bee70318d3ccbca98341
#
# If you change this configuration, make sure to change the `cache-key`
# in the `poetry_setup` action above to stop using the old cache.
# It doesn't matter how you change it, any change will cause a cache-bust.
working-directory: ${{ inputs.working-directory }}
run: |
poetry install --with dev
run: uv sync --group dev
- name: Get .mypy_cache_test to speed up mypy
if: steps.changed-files.outputs.all
@@ -106,7 +81,7 @@ jobs:
with:
path: |
${{ inputs.working-directory }}/.mypy_cache_test
key: mypy-test-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python-version }}-${{ inputs.working-directory }}-${{ hashFiles(format('{0}/poetry.lock', inputs.working-directory)) }}
key: mypy-test-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python-version }}-${{ inputs.working-directory }}-${{ hashFiles(format('{0}/uv.lock', inputs.working-directory)) }}
- name: Analysing tests with our lint
if: steps.changed-files.outputs.all
+6 -11
View File
@@ -8,9 +8,6 @@ on:
type: string
description: "From which folder this pipeline executes"
env:
POETRY_VERSION: "2.1.2"
jobs:
build:
runs-on: ubuntu-latest
@@ -26,12 +23,12 @@ jobs:
name: "test #${{ matrix.python-version }}"
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }} + Poetry ${{ env.POETRY_VERSION }}
uses: "./.github/actions/poetry_setup"
- name: Set up Python ${{ matrix.python-version }}
uses: astral-sh/setup-uv@v6
with:
python-version: ${{ matrix.python-version }}
poetry-version: ${{ env.POETRY_VERSION }}
cache-key: test-${{ inputs.working-directory }}
enable-cache: true
cache-suffix: test-${{ inputs.working-directory }}
- name: Login to Docker Hub
uses: docker/login-action@v3
if: ${{ !github.event.pull_request.head.repo.fork }}
@@ -42,14 +39,12 @@ jobs:
- name: Install dependencies
shell: bash
working-directory: ${{ inputs.working-directory }}
run: |
poetry install --with dev
run: uv sync --frozen --group dev
- name: Run tests
shell: bash
working-directory: ${{ inputs.working-directory }}
run: |
make test
run: make test
- name: Ensure the tests did not create any additional files
shell: bash
+6 -11
View File
@@ -3,9 +3,6 @@ name: test
on:
workflow_call:
env:
POETRY_VERSION: "2.1.2"
jobs:
build:
runs-on: ubuntu-latest
@@ -24,12 +21,12 @@ jobs:
name: "test #${{ matrix.python-version }}"
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }} + Poetry ${{ env.POETRY_VERSION }}
uses: "./.github/actions/poetry_setup"
- name: Set up Python ${{ matrix.python-version }}
uses: astral-sh/setup-uv@v6
with:
python-version: ${{ matrix.python-version }}
poetry-version: ${{ env.POETRY_VERSION }}
cache-key: test-langgraph
enable-cache: true
cache-suffix: "test-langgraph"
- name: Login to Docker Hub
uses: docker/login-action@v3
if: ${{ !github.event.pull_request.head.repo.fork }}
@@ -39,13 +36,11 @@ jobs:
- name: Install dependencies
shell: bash
run: |
poetry install --with dev
run: uv sync --frozen --group dev
- name: Run tests
shell: bash
run: |
make test_parallel
run: make test_parallel
- name: Ensure the tests did not create any additional files
shell: bash
+7 -8
View File
@@ -9,7 +9,6 @@ on:
description: "From which folder this pipeline executes"
env:
POETRY_VERSION: "2.1.2"
PYTHON_VERSION: "3.10"
jobs:
@@ -24,12 +23,12 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Set up Python + Poetry ${{ env.POETRY_VERSION }}
uses: "./.github/actions/poetry_setup"
- name: Set up Python $${ env.PYTHON_VERSION }}
uses: astral-sh/setup-uv@v6
with:
python-version: ${{ env.PYTHON_VERSION }}
poetry-version: ${{ env.POETRY_VERSION }}
cache-key: release
enable-cache: true
cache-suffix: "release"
# We want to keep this build stage *separate* from the release stage,
# so that there's no sharing of permissions between them.
@@ -43,7 +42,7 @@ jobs:
# > from the publish job.
# https://github.com/pypa/gh-action-pypi-publish#non-goals
- name: Build project for distribution
run: poetry build
run: uv build
working-directory: ${{ inputs.working-directory }}
- name: Upload build
@@ -57,8 +56,8 @@ jobs:
shell: bash
working-directory: ${{ inputs.working-directory }}
run: |
echo pkg-name="$(poetry version | cut -d ' ' -f 1)" >> $GITHUB_OUTPUT
echo version="$(poetry version --short)" >> $GITHUB_OUTPUT
echo pkg-name=$(grep -m 1 "^name = " pyproject.toml | cut -d '"' -f 2)
echo version=$(grep -m 1 "^version = " pyproject.toml | cut -d '"' -f 2)
publish:
needs:
+6 -11
View File
@@ -3,9 +3,6 @@ name: test
on:
workflow_call:
env:
POETRY_VERSION: "2.1.2"
jobs:
build:
runs-on: ubuntu-latest
@@ -21,12 +18,12 @@ jobs:
name: "test #${{ matrix.python-version }}"
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }} + Poetry ${{ env.POETRY_VERSION }}
uses: "./.github/actions/poetry_setup"
- name: Set up Python ${{ matrix.python-version }}
uses: astral-sh/setup-uv@v6
with:
python-version: ${{ matrix.python-version }}
poetry-version: ${{ env.POETRY_VERSION }}
cache-key: test-scheduler-kafka
enable-cache: true
cache-suffix: "test-scheduler-kafka"
- name: Login to Docker Hub
uses: docker/login-action@v3
if: ${{ !github.event.pull_request.head.repo.fork }}
@@ -36,13 +33,11 @@ jobs:
- name: Install dependencies
shell: bash
run: |
poetry install --with dev
run: uv sync --frozen --group dev
- name: Run tests
shell: bash
run: |
make test
run: make test
- name: Ensure the tests did not create any additional files
shell: bash
+5 -8
View File
@@ -7,9 +7,6 @@ on:
paths:
- "libs/**"
env:
POETRY_VERSION: "2.1.2"
jobs:
benchmark:
runs-on: ubuntu-latest
@@ -19,14 +16,14 @@ jobs:
steps:
- uses: actions/checkout@v4
- run: SHA=$(git rev-parse HEAD) && echo "SHA=$SHA" >> $GITHUB_ENV
- name: Set up Python 3.11 + Poetry ${{ env.POETRY_VERSION }}
uses: "./.github/actions/poetry_setup"
- name: Set up Python 3.11
uses: astral-sh/setup-uv@v6
with:
python-version: "3.11"
poetry-version: ${{ env.POETRY_VERSION }}
cache-key: bench
enable-cache: true
cache-suffix: "bench"
- name: Install dependencies
run: poetry install --with dev
run: uv sync --group dev
- name: Run benchmarks
run: OUTPUT=out/benchmark-baseline.json make -s benchmark
- name: Save outputs
+6 -9
View File
@@ -5,9 +5,6 @@ on:
paths:
- "libs/**"
env:
POETRY_VERSION: "2.1.2"
jobs:
benchmark:
runs-on: ubuntu-latest
@@ -21,14 +18,14 @@ jobs:
uses: Ana06/get-changed-files@v2.3.0
with:
format: json
- name: Set up Python 3.11 + Poetry ${{ env.POETRY_VERSION }}
uses: "./.github/actions/poetry_setup"
- name: Set up Python 3.11
uses: astral-sh/setup-uv@v6
with:
python-version: "3.11"
poetry-version: ${{ env.POETRY_VERSION }}
cache-key: bench
enable-cache: true
cache-suffix: "bench"
- name: Install dependencies
run: poetry install --with dev
run: uv sync --group dev
- name: Download baseline
uses: actions/cache/restore@v4
with:
@@ -53,7 +50,7 @@ jobs:
echo 'OUTPUT<<EOF'
mv out/benchmark-baseline.json out/main.json
mv out/benchmark.json out/changes.json
poetry run pyperf compare_to out/main.json out/changes.json --table --group-by-speed
uv run pyperf compare_to out/main.json out/changes.json --table --group-by-speed
echo EOF
} >> "$GITHUB_OUTPUT"
- name: Annotation
+7 -10
View File
@@ -16,9 +16,6 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
POETRY_VERSION: "2.1.2"
jobs:
changes:
runs-on: ubuntu-latest
@@ -125,26 +122,26 @@ jobs:
- "3.11"
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }} + Poetry ${{ env.POETRY_VERSION }}
uses: "./.github/actions/poetry_setup"
- name: Set up Python ${{ matrix.python-version }}
uses: astral-sh/setup-uv@v6
with:
python-version: "3.11"
poetry-version: ${{ env.POETRY_VERSION }}
cache-key: schema-check-cli
enable-cache: true
cache-suffix: "schema-check-cli"
- name: Install CLI dependencies
run: |
cd libs/cli
poetry install
uv sync
- name: Generate schema and check for changes
run: |
cd libs/cli
# Create a temporary copy of the current schema
cp schemas/schema.json schemas/schema.current.json
# Generate new schema
poetry run python generate_schema.py
uv run python generate_schema.py
# Compare the new schema with the original
if ! diff -q schemas/schema.json schemas/schema.current.json > /dev/null; then
echo "Error: Langgraph.json configuration schema has changed. Please run 'poetry run python generate_schema.py' in the libs/cli directory and commit the changes."
echo "Error: Langgraph.json configuration schema has changed. Please run 'uv run python generate_schema.py' in the libs/cli directory and commit the changes."
diff schemas/schema.json schemas/schema.current.json
exit 1
fi
+8 -11
View File
@@ -9,9 +9,6 @@ on:
- main
workflow_dispatch:
env:
POETRY_VERSION: "2.1.2"
permissions:
contents: read
pages: write
@@ -57,21 +54,21 @@ jobs:
with:
fetch-depth: 0
- name: Set up Python + Poetry ${{ env.POETRY_VERSION }}
uses: "./.github/actions/poetry_setup"
- name: Set up Python
uses: astral-sh/setup-uv@v6
with:
python-version: "3.12"
poetry-version: ${{ env.POETRY_VERSION }}
cache-key: docs
enable-cache: true
cache-suffix: "docs"
- name: Install dependencies
run: |
yarn
poetry install --with test --with docs --no-root
uv sync --all-groups
# we run this installation only for internal PRs
# as GITHUB_TOKEN is not available for PRs from outside contributors
if [ -n "${GITHUB_TOKEN}" ]; then
poetry run pip install "git+https://${GITHUB_TOKEN}@github.com/langchain-ai/mkdocs-material-insiders.git"
uv run pip install "git+https://${GITHUB_TOKEN}@github.com/langchain-ai/mkdocs-material-insiders.git"
fi
- name: Run unit tests
@@ -103,7 +100,7 @@ jobs:
run: |
if [ "${{ github.event_name }}" == "schedule" ]; then
echo "Running link check on all HTML files matching notebooks in docs directory..."
poetry run pytest -v \
uv run pytest -v \
--check-links-ignore "https://(api|web|docs)\.smith\.langchain\.com/.*" \
--check-links-ignore "https://academy\.langchain\.com/.*" \
--check-links-ignore "https://x.com/.*" \
@@ -128,7 +125,7 @@ jobs:
echo "Changed files: ${CHANGED_FILES}"
if [ -n "${CHANGED_FILES}" ]; then
echo "Running link check on HTML files matching changed notebook files..."
poetry run pytest -v \
uv run pytest -v \
--check-links-ignore "https://(api|web|docs)\.smith\.langchain\.com/.*" \
--check-links-ignore "https://academy\.langchain\.com/.*" \
--check-links-ignore "http://localhost:8123/.*" \
-3
View File
@@ -11,9 +11,6 @@ on:
- cron: "0 5 * * *"
workflow_dispatch:
env:
POETRY_VERSION: "2.1.2"
jobs:
markdown-link-check:
runs-on: ubuntu-latest
+24 -25
View File
@@ -10,7 +10,6 @@ on:
env:
PYTHON_VERSION: "3.11"
POETRY_VERSION: "2.1.2"
jobs:
build:
@@ -26,12 +25,12 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Set up Python + Poetry ${{ env.POETRY_VERSION }}
uses: "./.github/actions/poetry_setup"
- name: Set up Python
uses: astral-sh/setup-uv@v6
with:
python-version: ${{ env.PYTHON_VERSION }}
poetry-version: ${{ env.POETRY_VERSION }}
cache-key: release
enable-cache: true
cache-suffix: "release"
# We want to keep this build stage *separate* from the release stage,
# so that there's no sharing of permissions between them.
@@ -45,7 +44,7 @@ jobs:
# > from the publish job.
# https://github.com/pypa/gh-action-pypi-publish#non-goals
- name: Build project for distribution
run: poetry build
run: uv build
working-directory: ${{ inputs.working-directory }}
- name: Upload build
@@ -59,8 +58,8 @@ jobs:
shell: bash
working-directory: ${{ inputs.working-directory }}
run: |
PKG_NAME="$(poetry version | cut -d ' ' -f 1)"
VERSION="$(poetry version --short)"
PKG_NAME=$(grep -m 1 "^name = " pyproject.toml | cut -d '"' -f 2)
VERSION=$(grep -m 1 "^version = " pyproject.toml | cut -d '"' -f 2)
SHORT_PKG_NAME="$(echo "$PKG_NAME" | sed -e 's/langgraph//g' -e 's/-//g')"
if [ -z $SHORT_PKG_NAME ]; then
TAG="$VERSION"
@@ -163,11 +162,11 @@ jobs:
# - The package is published, and it breaks on the missing dependency when
# used in the real world.
- name: Set up Python + Poetry ${{ env.POETRY_VERSION }}
uses: "./.github/actions/poetry_setup"
- name: Set up Python
uses: astral-sh/setup-uv@v6
with:
python-version: ${{ env.PYTHON_VERSION }}
poetry-version: ${{ env.POETRY_VERSION }}
enable-cache: true
- name: Import published package
shell: bash
@@ -185,18 +184,18 @@ jobs:
# - attempt install again after 5 seconds if it fails because there is
# sometimes a delay in availability on test pypi
run: |
poetry run pip install \
uv run pip install \
--extra-index-url https://test.pypi.org/simple/ \
"$PKG_NAME==$VERSION" || \
( \
sleep 5 && \
poetry run pip install \
uv run pip install \
--extra-index-url https://test.pypi.org/simple/ \
"$PKG_NAME==$VERSION" \
)
if [[ "$PKG_NAME" == *prebuilt* ]]; then
poetry run pip install langgraph
uv run pip install langgraph
fi
if [[ "$PKG_NAME" == *checkpoint* || "$PKG_NAME" == *prebuilt* ]]; then
@@ -209,10 +208,10 @@ jobs:
IMPORT_NAME="$(echo "$PKG_NAME" | sed s/-/_/g)"
fi
poetry run python -c "import $IMPORT_NAME; print(dir($IMPORT_NAME))"
uv run python -c "import $IMPORT_NAME; print(dir($IMPORT_NAME))"
- name: Import test dependencies
run: poetry install --with dev
run: uv sync --group dev
working-directory: ${{ inputs.working-directory }}
# Overwrite the local version of the package with the test PyPI version.
@@ -223,7 +222,7 @@ jobs:
PKG_NAME: ${{ needs.build.outputs.pkg-name }}
VERSION: ${{ needs.build.outputs.version }}
run: |
poetry run pip install \
uv run pip install \
--extra-index-url https://test.pypi.org/simple/ \
"$PKG_NAME==$VERSION"
@@ -253,12 +252,12 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Set up Python + Poetry ${{ env.POETRY_VERSION }}
uses: "./.github/actions/poetry_setup"
- name: Set up Python
uses: astral-sh/setup-uv@v6
with:
python-version: ${{ env.PYTHON_VERSION }}
poetry-version: ${{ env.POETRY_VERSION }}
cache-key: release
enable-cache: true
cache-suffix: "release"
- uses: actions/download-artifact@v4
with:
@@ -294,12 +293,12 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Set up Python + Poetry ${{ env.POETRY_VERSION }}
uses: "./.github/actions/poetry_setup"
- name: Set up Python
uses: astral-sh/setup-uv@v6
with:
python-version: ${{ env.PYTHON_VERSION }}
poetry-version: ${{ env.POETRY_VERSION }}
cache-key: release
enable-cache: true
cache-suffix: "release"
- uses: actions/download-artifact@v4
with:
+9 -9
View File
@@ -27,30 +27,30 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Set up Python + Poetry
uses: "./.github/actions/poetry_setup"
uses: astral-sh/setup-uv@v6
with:
python-version: 3.11
poetry-version: 2.1.2
cache-key: test-langgraph-notebooks
python-version: "3.11"
enable-cache: true
cache-suffix: "test-langgraph-notebooks"
- name: Install dependencies
run: |
poetry install --with test --no-root
poetry run pip install jupyter
uv sync --group test
uv run pip install jupyter
- name: Start services
run: make start-services
- name: Pre-download tiktoken files
run: |
poetry run python _scripts/download_tiktoken.py
uv run python _scripts/download_tiktoken.py
- name: Prepare notebooks
run: |
if [ "${{ matrix.lib-version }}" = "development" ]; then
poetry run python _scripts/prepare_notebooks_for_ci.py --comment-install-cells
uv run python _scripts/prepare_notebooks_for_ci.py --comment-install-cells
else
poetry run python _scripts/prepare_notebooks_for_ci.py
uv run python _scripts/prepare_notebooks_for_ci.py
fi
- name: Run notebooks
+2 -1
View File
@@ -153,7 +153,7 @@ Each category serves a distinct purpose and requires a specific approach to writ
Here are some other guidelines you should think about when writing and organizing documentation.
We generally do not merge new tutorials from outside contributors without an actue need.
We generally do not merge new tutorials from outside contributors without an actual need.
We welcome updates as well as new integration docs, how-tos, and references.
### Avoid duplication
@@ -227,6 +227,7 @@ see a preview of the documentation on the pull request page.
From the **monorepo root**, run the following command to install the dependencies:
<!-- TODO -->
```bash
poetry install --with docs --no-root
```
+34 -23
View File
@@ -12,45 +12,52 @@
[![Downloads](https://static.pepy.tech/badge/langgraph/month)](https://pepy.tech/project/langgraph)
[![Open Issues](https://img.shields.io/github/issues-raw/langchain-ai/langgraph)](https://github.com/langchain-ai/langgraph/issues)
[![Docs](https://img.shields.io/badge/docs-latest-blue)](https://langchain-ai.github.io/langgraph/)
[![GitMCP](https://img.shields.io/endpoint?url=https://gitmcp.io/badge/langchain-ai/langgraph)](https://gitmcp.io/langchain-ai/langgraph)
> [!NOTE]
> Looking for the JS version? See the [JS repo](https://github.com/langchain-ai/langgraphjs) and the [JS docs](https://langchain-ai.github.io/langgraphjs/).
LangGraph is a low-level orchestration framework for building controllable agents. While [LangChain](https://python.langchain.com/docs/introduction/) provides integrations and composable components to streamline LLM application development, the LangGraph library enables agent orchestration — offering customizable architectures, long-term memory, and human-in-the-loop to reliably handle complex tasks.
Trusted by companies shaping the future of agents including Klarna, Replit, Elastic, and more LangGraph is a low-level orchestration framework for building, managing, and deploying long-running, stateful agents.
## Get started
First, install LangGraph:
Install LangGraph:
```
pip install -U langgraph
```
There are two ways to get started with LangGraph:
Then, create an agent [using prebuilt components](https://langchain-ai.github.io/langgraph/agents/agents/):
- [Use prebuilt components](https://langchain-ai.github.io/langgraph/agents/agents/): Construct agentic systems quickly and reliably without the need to implement orchestration, memory, or human feedback handling from scratch.
- [Use LangGraph](https://langchain-ai.github.io/langgraph/tutorials/introduction/): Customize your architectures, use long-term memory, and implement human-in-the-loop to reliably handle complex tasks.
```python
# pip install -qU "langchain[anthropic]" to call the model
Once you have a LangGraph application and are ready to move into production, use [LangGraph Platform](https://langchain-ai.github.io/langgraph/cloud/quick_start/) to test, debug, and deploy your application.
from langgraph.prebuilt import create_react_agent
## What LangGraph provides
def get_weather(city: str) -> str:
"""Get weather for a given city."""
return f"It's always sunny in {city}!"
LangGraph provides low-level supporting infrastructure that sits underneath *any* workflow or agent. It does not abstract prompts or architecture, and provides three central benefits:
agent = create_react_agent(
model="anthropic:claude-3-7-sonnet-latest",
tools=[get_weather],
prompt="You are a helpful assistant"
)
### Persistence
# Run the agent
agent.invoke(
{"messages": [{"role": "user", "content": "what is the weather in sf"}]}
)
```
LangGraph has a [persistence layer](https://langchain-ai.github.io/langgraph/concepts/persistence/), which offers a number of benefits:
For more information, see the [Quickstart](https://langchain-ai.github.io/langgraph/agents/agents/). Or, to learn how to build an [agent workflow](https://langchain-ai.github.io/langgraph/concepts/low_level/) with a customizable architecture, long-term memory, and other complex task handling, see the [LangGraph basics tutorials](https://langchain-ai.github.io/langgraph/tutorials/get-started/1-build-basic-chatbot/).
- [Memory](https://langchain-ai.github.io/langgraph/concepts/memory/): LangGraph persists arbitrary aspects of your application's state, supporting memory of conversations and other updates within and across user interactions;
- [Human-in-the-loop](https://langchain-ai.github.io/langgraph/concepts/human_in_the_loop/): Because state is checkpointed, execution can be interrupted and resumed, allowing for decisions, validation, and corrections via human input.
## Core benefits
### Streaming
LangGraph provides low-level supporting infrastructure for *any* long-running, stateful workflow or agent. LangGraph does not abstract prompts or architecture, and provides the following central benefits:
LangGraph provides support for [streaming](https://langchain-ai.github.io/langgraph/concepts/streaming/) workflow / agent state to the user (or developer) over the course of execution. LangGraph supports streaming of both events ([such as feedback from a tool call](https://langchain-ai.github.io/langgraph/how-tos/streaming.ipynb#updates)) and [tokens from LLM calls](https://langchain-ai.github.io/langgraph/how-tos/streaming-tokens.ipynb) embedded in an application.
### Debugging and deployment
LangGraph provides an easy onramp for testing, debugging, and deploying applications via [LangGraph Platform](https://langchain-ai.github.io/langgraph/concepts/langgraph_platform/). This includes [Studio](https://langchain-ai.github.io/langgraph/concepts/langgraph_studio/), an IDE that enables visualization, interaction, and debugging of workflows or agents. This also includes numerous [options](https://langchain-ai.github.io/langgraph/tutorials/deployment/) for deployment.
- [Durable execution](https://langchain-ai.github.io/langgraph/concepts/durable_execution/): Build agents that persist through failures and can run for extended periods, automatically resuming from exactly where they left off.
- [Human-in-the-loop](https://langchain-ai.github.io/langgraph/concepts/human_in_the_loop/): Seamlessly incorporate human oversight by inspecting and modifying agent state at any point during execution.
- [Comprehensive memory](https://langchain-ai.github.io/langgraph/concepts/memory/): Create truly stateful agents with both short-term working memory for ongoing reasoning and long-term persistent memory across sessions.
- [Debugging with LangSmith](http://www.langchain.com/langsmith): Gain deep visibility into complex agent behavior with visualization tools that trace execution paths, capture state transitions, and provide detailed runtime metrics.
- [Production-ready deployment](https://langchain-ai.github.io/langgraph/concepts/deployment_options/): Deploy sophisticated agent systems confidently with scalable infrastructure designed to handle the unique challenges of stateful, long-running workflows.
## LangGraphs ecosystem
@@ -58,6 +65,10 @@ While LangGraph can be used standalone, it also integrates seamlessly with any L
- [LangSmith](http://www.langchain.com/langsmith) — Helpful for agent evals and observability. Debug poor-performing LLM app runs, evaluate agent trajectories, gain visibility in production, and improve performance over time.
- [LangGraph Platform](https://langchain-ai.github.io/langgraph/concepts/#langgraph-platform) — Deploy and scale agents effortlessly with a purpose-built deployment platform for long running, stateful workflows. Discover, reuse, configure, and share agents across teams — and iterate quickly with visual prototyping in [LangGraph Studio](https://langchain-ai.github.io/langgraph/concepts/langgraph_studio/).
- [LangChain](https://python.langchain.com/docs/introduction/) Provides integrations and composable components to streamline LLM application development.
> [!NOTE]
> Looking for the JS version of LangGraph? See the [JS repo](https://github.com/langchain-ai/langgraphjs) and the [JS docs](https://langchain-ai.github.io/langgraphjs/).
## Additional resources
@@ -66,8 +77,8 @@ While LangGraph can be used standalone, it also integrates seamlessly with any L
- [Examples](https://langchain-ai.github.io/langgraph/tutorials/): Guided examples on getting started with LangGraph.
- [LangChain Academy](https://academy.langchain.com/courses/intro-to-langgraph): Learn the basics of LangGraph in our free, structured course.
- [Templates](https://langchain-ai.github.io/langgraph/concepts/template_applications/): Pre-built reference apps for common agentic workflows (e.g. ReAct agent, memory, retrieval etc.) that can be cloned and adapted.
- [Case studies](https://www.langchain.com/built-with-langgraph): Hear how industry leaders use LangGraph to ship powerful, production-ready AI applications.
- [Case studies](https://www.langchain.com/built-with-langgraph): Hear how industry leaders use LangGraph to ship AI applications at scale.
## Acknowledgements
LangGraph is inspired by [Pregel](https://research.google/pubs/pub37252/) and [Apache Beam](https://beam.apache.org/). The public interface draws inspiration from [NetworkX](https://networkx.org/documentation/latest/). LangGraph is built by LangChain Inc, the creators of LangChain, but can be used without LangChain.
LangGraph is inspired by [Pregel](https://research.google/pubs/pub37252/) and [Apache Beam](https://beam.apache.org/). The public interface draws inspiration from [NetworkX](https://networkx.org/documentation/latest/). LangGraph is built by LangChain Inc, the creators of LangChain, but can be used without LangChain.
+16 -18
View File
@@ -12,32 +12,30 @@ build-prebuilt:
# generates the final prebuilt page.
@if [ "$(DOWNLOAD_STATS)" = "true" ]; then \
set -x; \
poetry run python -m _scripts.third_party_page.get_download_stats stats.yml; \
uv run python -m _scripts.third_party_page.get_download_stats stats.yml; \
set +x; \
else \
set -x; \
poetry run python -m _scripts.third_party_page.get_download_stats --fake stats.yml; \
uv run python -m _scripts.third_party_page.get_download_stats --fake stats.yml; \
set +x; \
fi
poetry run python -m _scripts.third_party_page.create_third_party_page stats.yml docs/agents/prebuilt.md --language python
uv run python -m _scripts.third_party_page.create_third_party_page stats.yml docs/agents/prebuilt.md --language python
build-docs: build-typedoc build-prebuilt
poetry run python -m mkdocs build --clean -f mkdocs.yml --strict
uv run python -m mkdocs build --clean -f mkdocs.yml --strict
llms-text:
poetry run python -m _scripts.generate_llms_text docs/llms-full.txt
uv run python -m _scripts.generate_llms_text docs/llms-full.txt
install-vercel-deps:
dnf install -y python3.11
curl -sSL https://install.python-poetry.org | python3 -
poetry self update 1.8.5
# don't use vercel's python - it wasn't compiled with sqlite support, and it fails when installing ipython's kernel
poetry env use /usr/bin/python3.11
poetry install --with docs --with test --no-root
curl -sL "https://astral.sh/uv/install.sh" | bash -s
export PATH="${HOME}/.cargo/bin:${PATH}"
uv venv --python 3.11
uv sync --all-groups
tests:
# Run unit tests
poetry run pytest tests/unit_tests
uv run pytest tests/unit_tests
vercel-build-docs: install-vercel-deps
@@ -45,10 +43,10 @@ vercel-build-docs: install-vercel-deps
serve-clean-docs: clean-docs
poetry run python -m mkdocs serve -c -f mkdocs.yml --strict -w ../libs/langgraph
uv run python -m mkdocs serve -c -f mkdocs.yml --strict -w ../libs/langgraph
serve-docs: build-typedoc
poetry run python -m mkdocs serve -f mkdocs.yml -w ../libs/langgraph -w ../libs/checkpoint -w ../libs/sdk-py --dirty
uv run python -m mkdocs serve -f mkdocs.yml -w ../libs/langgraph -w ../libs/checkpoint -w ../libs/sdk-py --dirty
clean-docs:
find ./docs -name "*.ipynb" -type f -delete
@@ -56,13 +54,13 @@ clean-docs:
## Run format against the project documentation.
format-docs:
poetry run ruff format docs
poetry run ruff check --fix docs
uv run ruff format docs
uv run ruff check --fix docs
# Check the docs for linting violations
lint-docs:
poetry run ruff format --check docs
poetry run ruff check docs
uv run ruff format --check docs
uv run ruff check docs
codespell:
./codespell_notebooks.sh .
+1 -1
View File
@@ -3,7 +3,7 @@
To setup requirements for building docs you can run:
```bash
poetry install --with test
uv sync --group test
```
## Serving documentation locally
+1 -1
View File
@@ -8,7 +8,7 @@ execute_notebook() {
file="$1"
echo "Starting execution of $file"
start_time=$(date +%s)
if ! output=$(time poetry run jupyter execute "$file" 2>&1); then
if ! output=$(time uv run jupyter execute "$file" 2>&1); then
end_time=$(date +%s)
execution_time=$((end_time - start_time))
echo "Error in $file. Execution time: $execution_time seconds"
+238 -26
View File
@@ -1,10 +1,19 @@
"""Experimental script to generate consolidated llms text from the docs."""
import asyncio
import glob
import os
from typing import TypedDict, List, Optional
import pydantic
import re
from pydantic import BaseModel, Field
from langchain_core.rate_limiters import InMemoryRateLimiter
import yaml
from langchain.chat_models import init_chat_model
from mkdocs.structure.files import File
from mkdocs.structure.pages import Page
from yaml import SafeLoader
from _scripts.notebook_hooks import _on_page_markdown_with_config
@@ -13,7 +22,49 @@ HERE = os.path.dirname(os.path.abspath(__file__))
SOURCE_DIR = os.path.abspath(os.path.join(os.path.dirname(HERE), "docs"))
def _make_llms_text(output_file: str) -> str:
async def convert_ipynb_to_md(file_path: str) -> Optional[str]:
"""Process a file (markdown or notebook) to markdown format.
Args:
file_path: Path to the file to process
Returns:
Processed markdown content if successful, None otherwise
"""
rel_path = os.path.relpath(file_path, SOURCE_DIR)
# Create File and Page objects to match mkdocs structure
file_obj = File(
path=rel_path, src_dir=SOURCE_DIR, dest_dir="", use_directory_urls=True
)
page = Page(
title="",
file=file_obj,
config={},
)
try:
# Read raw content
with open(file_path, "r", encoding="utf-8") as f:
content = f.read()
# Convert to markdown without logic to resolve API references
processed_content = _on_page_markdown_with_config(
content, page, add_api_references=False, remove_base64_images=True
)
# Remove self-closing img tags <img ... />
processed_content = re.sub(r"<img[^>]*/>", "", processed_content)
# Remove img tags with content <img ...>...</img>
processed_content = re.sub(
r"<img[^>]*>.*?</img>", "", processed_content, flags=re.DOTALL
)
return processed_content
except Exception as e:
print(f"Error processing file {file_path}: {e}")
return None
async def generate_full_llms_text(output_file: str) -> None:
"""Generate a consolidated text file from markdown/notebook files for LLM training.
Args:
@@ -21,11 +72,9 @@ def _make_llms_text(output_file: str) -> str:
"""
# Collect all markdown and notebook files
all_files = glob.glob(os.path.join(SOURCE_DIR, "how-tos/*.md"), recursive=True)
all_files.extend(
glob.glob(os.path.join(SOURCE_DIR, "how-tos/*.ipynb"), recursive=True)
)
# Add all concepts
all_files.extend(
glob.glob(os.path.join(SOURCE_DIR, "concepts/*.md"), recursive=True)
)
@@ -35,30 +84,14 @@ def _make_llms_text(output_file: str) -> str:
all_content = []
# Process each file
for file_path in all_files:
print(f"Processing {file_path}")
rel_path = os.path.relpath(file_path, SOURCE_DIR)
# Process files concurrently
tasks = [convert_ipynb_to_md(file_path) for file_path in all_files]
results = await asyncio.gather(*tasks)
# Create File and Page objects to match mkdocs structure
file_obj = File(
path=rel_path, src_dir=SOURCE_DIR, dest_dir="", use_directory_urls=True
)
page = Page(
title="",
file=file_obj,
config={},
)
# Read raw content
with open(file_path, "r", encoding="utf-8") as f:
content = f.read()
# Convert to markdown without logic to resolve API references
processed_content = _on_page_markdown_with_config(
content, page, add_api_references=False, remove_base64_images=True
)
# Combine results with file paths
for file_path, processed_content in zip(all_files, results):
if processed_content:
rel_path = os.path.relpath(file_path, SOURCE_DIR)
# Add file name
all_content.append(f"---\n{rel_path}\n---")
# Add content
@@ -69,6 +102,168 @@ def _make_llms_text(output_file: str) -> str:
f.write("\n\n".join(all_content))
def no_op_constructor(*args):
"""No-op"""
SafeLoader.add_multi_constructor(
"tag:yaml.org,2002:python/name",
no_op_constructor,
)
class NavItem(TypedDict):
title: str
url: str
hierarchy: tuple[str, ...]
description: str
def _flatten_nav(
nav: list[dict[str, str | list] | str], path: tuple[str, ...] = ()
) -> list[NavItem]:
flat: List[NavItem] = []
for item in nav:
if isinstance(item, dict):
for title, node in item.items():
new_path = path + (title,)
if isinstance(node, str):
# Leaf page
flat.append(
{
"title": title,
"url": node,
"hierarchy": new_path,
"description": "",
}
)
elif isinstance(node, list):
# Dive in, carrying along the updated path
flat.extend(_flatten_nav(node, new_path))
else:
raise TypeError(
f"Unexpected node type {type(node)} under {title!r}"
)
elif isinstance(item, str):
# Bare string entry → use itself as title, and as URL
new_path = path + (item,)
flat.append(
{"title": item, "url": item, "hierarchy": new_path, "description": ""}
)
else:
raise TypeError(f"Unexpected item type {type(item)} in nav")
return flat
class PageInfo(BaseModel):
title: str = Field(description="The title of the page")
description: str = Field(
description="A short description of the page no longer than 3 sentences "
"explaining the kind of content that can be found in the page."
)
async def process_nav_items(nav_items: list[NavItem]) -> list[NavItem]:
"""Open the contents of each nav item and come up with a better title and description."""
rate_limiter = InMemoryRateLimiter(requests_per_second=10)
model = init_chat_model("gpt-4o-mini", temperature=0.0, rate_limiter=rate_limiter)
model = model.with_structured_output(PageInfo)
async def process_single_item(item: NavItem) -> NavItem:
path = item["url"]
file_path = os.path.join(SOURCE_DIR, path)
# Process the file content (handles both markdown and notebooks)
if path.endswith(".ipynb"):
content = await convert_ipynb_to_md(file_path)
else:
with open(file_path, "r", encoding="utf-8") as f:
content = f.read()
if not content:
return item
# Generate a better title and description
response = await model.ainvoke(
[
{
"role": "system",
"content": "You are a technical documentation writer. "
"You are given a markdown page of documentation. "
"Please come up with an appropriate title and "
"description for the page. The description should "
"be a short summary of the page content that is "
"no longer than 3 sentences.",
},
{
"role": "user",
"content": "The markdown page is as follows:\n\n" + content,
},
]
)
return {
"title": response.title,
"url": item["url"],
"hierarchy": item["hierarchy"],
"description": response.description,
}
# Remove any items that start with http:// or https:// looking only for
# local file at this stages.
nav_items = [
item for item in nav_items if not item["url"].startswith(("http://", "https://"))
]
# Process items in parallel
tasks = [process_single_item(item) for item in nav_items]
new_nav_items = await asyncio.gather(*tasks)
return new_nav_items
async def generate_nav_links_text(
output_file: str, *, replace_links: bool = False
) -> None:
"""Generate llms.txt from mkdocs.yaml."""
# Get path to mkdocs.yaml relative to this script
script_dir = os.path.dirname(os.path.abspath(__file__))
mkdocs_path = os.path.join(os.path.dirname(script_dir), "mkdocs.yml")
# Load and parse yaml
with open(mkdocs_path, "r") as f:
config = yaml.safe_load(f)
# Extract nav section
nav = config.get("nav", [])
flattened = _flatten_nav(nav)
processed_nav = await process_nav_items(flattened)
with open(output_file, "w") as f:
current_section = None
for item in processed_nav:
# Get the top-level section (first item in hierarchy)
section = item["hierarchy"][0]
if section not in {"Guides", "Examples", "Resources"}:
continue
# If we're starting a new section, add a heading
if section != current_section:
f.write(f"\n# {section}\n\n")
current_section = section
title = item["title"]
# Process URL based on replace_links flag
url = item["url"]
if replace_links:
# Remove .md extension and ensure single trailing slash
url = url.removesuffix(".md")
url = url.removesuffix(".ipynb")
url = url.rstrip("/") + "/"
url = f"https://langchain-ai.github.io/langgraph/{url}"
f.write(f"- [{title}]({url}): {item['description']}\n")
if __name__ == "__main__":
import argparse
@@ -78,6 +273,23 @@ if __name__ == "__main__":
)
)
parser.add_argument("output_file", help="Path to output the consolidated text file")
parser.add_argument(
"--link-only",
action="store_true",
help="Only include link references in the output",
)
parser.add_argument(
"--replace-links",
action="store_true",
help="Replace markdown links with full URLs in the output",
)
args = parser.parse_args()
_make_llms_text(args.output_file)
if args.link_only:
coro = generate_nav_links_text(
args.output_file, replace_links=args.replace_links
)
else:
coro = generate_full_llms_text(args.output_file)
asyncio.run(coro)
+8 -1
View File
@@ -39,6 +39,7 @@ 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",
@@ -52,6 +53,7 @@ REDIRECT_MAP = {
"how-tos/persistence_redis.ipynb": "how-tos/persistence.ipynb#use-in-production",
"how-tos/subgraph-persistence.ipynb": "how-tos/persistence.ipynb#use-with-subgraphs",
"how-tos/cross-thread-persistence.ipynb": "how-tos/persistence.ipynb#add-long-term-memory",
"cloud/how-tos/copy_threads": "cloud/how-tos/use_threads",
# tool calling how-tos
"how-tos/tool-calling-errors.ipynb": "how-tos/tool-calling.ipynb#handle-errors",
"how-tos/pass-config-to-tools.ipynb": "how-tos/tool-calling.ipynb#access-config",
@@ -67,6 +69,9 @@ 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",
"concepts/platform_architecture.md": "concepts/langgraph_cloud#architecture",
# 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",
@@ -94,7 +99,9 @@ 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"
"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"
}
+11 -5
View File
@@ -5,6 +5,7 @@ import os
import json
import click
import nbformat
import re
logger = logging.getLogger(__name__)
NOTEBOOK_DIRS = ("docs/how-tos","docs/tutorials")
@@ -88,12 +89,10 @@ 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 code.replace(
"display(Image(graph.get_graph().draw_mermaid_png()))",
# replace with a dummy statement
"print()"
)
return MERMAID_PATTERN.sub('print()', code)
def add_vcr_to_notebook(
@@ -108,6 +107,8 @@ 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
@@ -195,6 +196,11 @@ 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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
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
@@ -0,0 +1 @@
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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
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
@@ -1 +1 @@
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
@@ -1 +1 @@
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
@@ -0,0 +1 @@
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
@@ -0,0 +1 @@
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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +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 @@
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==
+1 -1
View File
@@ -31,7 +31,7 @@ pip install -U langgraph "langchain[anthropic]"
## 2. Create an agent
To create an agent, use [`create_react_agent`](langgraph.prebuilt.chat_agent_executor.create_react_agent):
To create an agent, use [`create_react_agent`][langgraph.prebuilt.chat_agent_executor.create_react_agent]:
```python
from langgraph.prebuilt import create_react_agent
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

+1 -1
View File
@@ -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 Cloud or self-hosted options. 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 Platform. Refer to the [deployment options guide](../tutorials/deployment.md) for detailed instructions on all supported deployment models.
+1 -1
View File
@@ -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`](../reference/types.md#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`][langgraph.types.Command] object to resume the graph with a value provided by the human.
## Using with Agent Inbox
+19 -17
View File
@@ -29,7 +29,7 @@ from langchain_mcp_adapters.client import MultiServerMCPClient
from langgraph.prebuilt import create_react_agent
# highlight-next-line
async with MultiServerMCPClient(
client = MultiServerMCPClient(
{
"math": {
"command": "python",
@@ -39,22 +39,24 @@ async with MultiServerMCPClient(
},
"weather": {
# Ensure your start your weather server on port 8000
"url": "http://localhost:8000/sse",
"transport": "sse",
"url": "http://localhost:8000/mcp",
"transport": "streamable_http",
}
}
) as client:
agent = create_react_agent(
"anthropic:claude-3-7-sonnet-latest",
# highlight-next-line
client.get_tools()
)
math_response = await agent.ainvoke(
{"messages": [{"role": "user", "content": "what's (3 + 5) x 12?"}]}
)
weather_response = await agent.ainvoke(
{"messages": [{"role": "user", "content": "what is the weather in nyc?"}]}
)
)
# highlight-next-line
tools = await client.get_tools()
agent = create_react_agent(
"anthropic:claude-3-7-sonnet-latest",
# highlight-next-line
tools
)
math_response = await agent.ainvoke(
{"messages": [{"role": "user", "content": "what's (3 + 5) x 12?"}]}
)
weather_response = await agent.ainvoke(
{"messages": [{"role": "user", "content": "what is the weather in nyc?"}]}
)
```
## Custom MCP servers
@@ -87,7 +89,7 @@ if __name__ == "__main__":
mcp.run(transport="stdio")
```
```python title="Example Weather Server (SSE transport)"
```python title="Example Weather Server (Streamable HTTP transport)"
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("Weather")
@@ -98,7 +100,7 @@ async def get_weather(location: str) -> str:
return "It's always sunny in New York"
if __name__ == "__main__":
mcp.run(transport="sse")
mcp.run(transport="streamable-http")
```
## Additional resources
+2 -2
View File
@@ -294,7 +294,7 @@ agent.invoke(
)
```
For more details, see [how to update state from tools](../how-tos/update-state-from-tools.ipynb).
For more details, see [how to update state from tools](../how-tos/tool-calling.ipynb#update).
## 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/cross-thread-persistence.ipynb) to persist data across invocations.
1. [Configure a store](../how-tos/persistence.ipynb#add-long-term-memory) to persist data across invocations.
2. Use the [`get_store`][langgraph.config.get_store] function to access the store from within tools or prompts.
### Read { #read-long-term }
+169 -16
View File
@@ -23,29 +23,182 @@ Compatible models can be found in the [LangChain integrations directory](https:/
You can configure an agent with a model name string:
```python
from langgraph.prebuilt import create_react_agent
=== "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
)
```
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:
```python
from langchain.chat_models import init_chat_model
=== "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
)
```
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.
+136
View File
@@ -53,3 +53,139 @@ The high-level components are organized into several packages, each with a speci
| `langmem` | Agent memory management: [**short-term and long-term**](./memory.md) | `pip install -U langmem` |
| `agentevals` | Utilities to [**evaluate agent performance**](./evals.md) | `pip install -U agentevals` |
## Visualize an agent graph
Use the following tool to visualize the graph generated by
[`create_react_agent`][langgraph.prebuilt.chat_agent_executor.create_react_agent]
and to view an outline of the corresponding code.
It allows you to explore the infrastructure of the agent as defined by the presence of:
* [`tools`](../agents/tools.md): A list of tools (functions, APIs, or other callable objects) that the agent can use to perform tasks.
* [`pre_model_hook`](../how-tos/create-react-agent-manage-message-history.ipynb): A function that is called before the model is invoked. It can be used to condense messages or perform other preprocessing tasks.
* `post_model_hook`: A function that is called after the model is invoked. It can be used to implement guardrails, human-in-the-loop flows, or other postprocessing tasks.
* [`response_format`](../agents/agents.md#6-configure-structured-output): A data structure used to constrain the type of the final output, e.g., a `pydantic` `BaseModel`.
<div class="agent-layout">
<div class="agent-graph-features-container">
<div class="agent-graph-features">
<h3 class="agent-section-title">Features</h3>
<label><input type="checkbox" id="tools" checked> <code>tools</code></label>
<label><input type="checkbox" id="pre_model_hook"> <code>pre_model_hook</code></label>
<label><input type="checkbox" id="post_model_hook"> <code>post_model_hook</code></label>
<label><input type="checkbox" id="response_format"> <code>response_format</code></label>
</div>
</div>
<div class="agent-graph-container">
<h3 class="agent-section-title">Graph</h3>
<img id="agent-graph-img" src="../assets/react_agent_graphs/0001.svg" alt="graph image" style="max-width: 100%;"/>
</div>
</div>
The following code snippet shows how to create the above agent (and underlying graph) with
[`create_react_agent`][langgraph.prebuilt.chat_agent_executor.create_react_agent]:
<div class="language-python">
<pre><code id="agent-code" class="language-python"></code></pre>
</div>
<script>
function getCheckedValue(id) {
return document.getElementById(id).checked ? "1" : "0";
}
function getKey() {
return [
getCheckedValue("response_format"),
getCheckedValue("post_model_hook"),
getCheckedValue("pre_model_hook"),
getCheckedValue("tools")
].join("");
}
function generateCodeSnippet({ tools, pre, post, response }) {
const lines = [
"from langgraph.prebuilt import create_react_agent",
"from langchain_openai import ChatOpenAI"
];
if (response) lines.push("from pydantic import BaseModel");
lines.push("", 'model = ChatOpenAI("o4-mini")', "");
if (tools) {
lines.push(
"def tool() -> None:",
' """Testing tool."""',
" ...",
""
);
}
if (pre) {
lines.push(
"def pre_model_hook() -> None:",
' """Pre-model hook."""',
" ...",
""
);
}
if (post) {
lines.push(
"def post_model_hook() -> None:",
' """Post-model hook."""',
" ...",
""
);
}
if (response) {
lines.push(
"class ResponseFormat(BaseModel):",
' """Response format for the agent."""',
" result: str",
""
);
}
lines.push("agent = create_react_agent(");
lines.push(" model,");
if (tools) lines.push(" tools=[tool],");
if (pre) lines.push(" pre_model_hook=pre_model_hook,");
if (post) lines.push(" post_model_hook=post_model_hook,");
if (response) lines.push(" response_format=ResponseFormat,");
lines.push(")", "", "agent.get_graph().draw_mermaid_png()");
return lines.join("\n");
}
async function render() {
const key = getKey();
document.getElementById("agent-graph-img").src = `../assets/react_agent_graphs/${key}.svg`;
const state = {
tools: document.getElementById("tools").checked,
pre: document.getElementById("pre_model_hook").checked,
post: document.getElementById("post_model_hook").checked,
response: document.getElementById("response_format").checked
};
document.getElementById("agent-code").textContent = generateCodeSnippet(state);
}
function initializeWidget() {
render(); // no need for `await` here
document.querySelectorAll(".agent-graph-features input").forEach((input) => {
input.addEventListener("change", render);
});
}
// Init for both full reload and SPA nav (used by MkDocs Material)
window.addEventListener("DOMContentLoaded", initializeWidget);
document$.subscribe(initializeWidget);
</script>
+3 -3
View File
@@ -1,10 +1,10 @@
[//]: # (This file is automatically generated using a script in docs/_scripts. Do not edit this file directly!)
# Community Agents
# Community agents
If youre looking for other prebuilt libraries, explore the community-built options
below. These libraries can extend LangGraph's functionality in various ways.
## 📚 Available Libraries
## 📚 Available libraries
[//]: # (This file is automatically generated using a script in docs/_scripts. Do not edit this file directly!)
| Name | GitHub URL | Description | Weekly Downloads | Stars |
@@ -23,7 +23,7 @@ below. These libraries can extend LangGraph's functionality in various ways.
| **langgraph-reflection** | [langchain-ai/langgraph-reflection](https://github.com/langchain-ai/langgraph-reflection) | LangGraph agent that runs a reflection step. | -12345 | ![GitHub stars](https://img.shields.io/github/stars/langchain-ai/langgraph-reflection?style=social)
| **langgraph-codeact** | [langchain-ai/langgraph-codeact](https://github.com/langchain-ai/langgraph-codeact) | LangGraph implementation of CodeAct agent that generates and executes code instead of tool calling. | -12345 | ![GitHub stars](https://img.shields.io/github/stars/langchain-ai/langgraph-codeact?style=social)
## ✨ Contributing Your Library
## ✨ Contributing your library
Have you built an awesome open-source library using LangGraph? We'd love to feature
your project on the official LangGraph documentation pages! 🏆
+2 -2
View File
@@ -10,7 +10,7 @@ hide:
# Running agents
Agents support both synchronous and asynchronous execution using either `.invoke()` / `await .invoke()` for full responses, or `.stream()` / `.astream()` for **incremental** [streaming](streaming.md) output. This section explains how to provide input, interpret output, enable streaming, and control execution limits.
Agents support both synchronous and asynchronous execution using either `.invoke()` / `await .ainvoke()` for full responses, or `.stream()` / `.astream()` for **incremental** [streaming](streaming.md) output. This section explains how to provide input, interpret output, enable streaming, and control execution limits.
## Basic usage
@@ -18,7 +18,7 @@ Agents support both synchronous and asynchronous execution using either `.invoke
Agents can be executed in two primary modes:
- **Synchronous** using `.invoke()` or `.stream()`
- **Asynchronous** using `await .invoke()` or `async for` with `.astream()`
- **Asynchronous** using `await .ainvoke()` or `async for` with `.astream()`
=== "Sync invocation"
```python
+15 -1
View File
@@ -280,7 +280,21 @@ LangGraph allows access to short-term and long-term memory from tools. See [Memo
## Prebuilt tools
LangChain supports a wide range of prebuilt tool integrations for interacting with APIs, databases, file systems, web data, and more. These tools extend the functionality of agents and enable rapid development.
You can use prebuilt tools from model providers by passing a dictionary with tool specs to the `tools` parameter of `create_react_agent`. For example, to use the `web_search_preview` tool from OpenAI:
```python
from langgraph.prebuilt import create_react_agent
agent = create_react_agent(
model="openai:gpt-4o-mini",
tools=[{"type": "web_search_preview"}]
)
response = agent.invoke(
{"messages": ["What was a positive news story from today?"]}
)
```
Additionally, LangChain supports a wide range of prebuilt tool integrations for interacting with APIs, databases, file systems, web data, and more. These tools extend the functionality of agents and enable rapid development.
You can browse the full list of available integrations in the [LangChain integrations directory](https://python.langchain.com/docs/integrations/tools/).
+1 -1
View File
@@ -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 Cloud](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 Platform](https://langchain-ai.github.io/langgraph/cloud/quick_start/).
Then, navigate to [Agent Chat UI](https://agentchat.vercel.app), or clone the repository and [run the dev server locally](https://github.com/langchain-ai/agent-chat-ui?tab=readme-ov-file#setup):
+19
View File
@@ -0,0 +1,19 @@
{
"0000": "graph TD;\n\t__start__ --> agent;\n\tagent --> __end__;",
"0001": "graph TD;\n\t__start__ --> agent;\n\tagent -.-> __end__;\n\tagent -.-> tools;\n\ttools --> agent;",
"0010": "graph TD;\n\t__start__ --> pre_model_hook;\n\tpre_model_hook --> agent;\n\tagent --> __end__;",
"0011": "graph TD;\n\t__start__ --> pre_model_hook;\n\tagent -.-> __end__;\n\tagent -.-> tools;\n\tpre_model_hook --> agent;\n\ttools --> pre_model_hook;",
"0100": "graph TD;\n\t__start__ --> agent;\n\tagent --> post_model_hook;\n\tpost_model_hook --> __end__;",
"0101": "graph TD;\n\t__start__ --> agent;\n\tagent --> post_model_hook;\n\tpost_model_hook -.-> __end__;\n\tpost_model_hook -.-> agent;\n\tpost_model_hook -.-> tools;\n\ttools --> agent;",
"0110": "graph TD;\n\t__start__ --> pre_model_hook;\n\tagent --> post_model_hook;\n\tpre_model_hook --> agent;\n\tpost_model_hook --> __end__;",
"0111": "graph TD;\n\t__start__ --> pre_model_hook;\n\tagent --> post_model_hook;\n\tpost_model_hook -.-> __end__;\n\tpost_model_hook -.-> pre_model_hook;\n\tpost_model_hook -.-> tools;\n\tpre_model_hook --> agent;\n\ttools --> pre_model_hook;",
"1000": "graph TD;\n\t__start__ --> agent;\n\tagent --> generate_structured_response;\n\tgenerate_structured_response --> __end__;",
"1001": "graph TD;\n\t__start__ --> agent;\n\tagent -.-> generate_structured_response;\n\tagent -.-> tools;\n\ttools --> agent;\n\tgenerate_structured_response --> __end__;",
"1010": "graph TD;\n\t__start__ --> pre_model_hook;\n\tagent --> generate_structured_response;\n\tpre_model_hook --> agent;\n\tgenerate_structured_response --> __end__;",
"1011": "graph TD;\n\t__start__ --> pre_model_hook;\n\tagent -.-> generate_structured_response;\n\tagent -.-> tools;\n\tpre_model_hook --> agent;\n\ttools --> pre_model_hook;\n\tgenerate_structured_response --> __end__;",
"1100": "graph TD;\n\t__start__ --> agent;\n\tagent --> post_model_hook;\n\tpost_model_hook --> generate_structured_response;\n\tgenerate_structured_response --> __end__;",
"1101": "graph TD;\n\t__start__ --> agent;\n\tagent --> post_model_hook;\n\tpost_model_hook -.-> agent;\n\tpost_model_hook -.-> generate_structured_response;\n\tpost_model_hook -.-> tools;\n\ttools --> agent;\n\tgenerate_structured_response --> __end__;",
"1110": "graph TD;\n\t__start__ --> pre_model_hook;\n\tagent --> post_model_hook;\n\tpost_model_hook --> generate_structured_response;\n\tpre_model_hook --> agent;\n\tgenerate_structured_response --> __end__;",
"1111": "graph TD;\n\t__start__ --> pre_model_hook;\n\tagent --> post_model_hook;\n\tpost_model_hook -.-> generate_structured_response;\n\tpost_model_hook -.-> pre_model_hook;\n\tpost_model_hook -.-> tools;\n\tpre_model_hook --> agent;\n\ttools --> pre_model_hook;\n\tgenerate_structured_response --> __end__;"
}
+2 -2
View File
@@ -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 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:
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:
- 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 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.
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.
+1 -1
View File
@@ -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 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.
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.
+5 -4
View File
@@ -1,11 +1,12 @@
# Threads
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 contains the accumulated state of a sequence of [runs](./runs.md). When a run is executed, the [state](../../concepts/low_level.md#state) of the underlying graph of the assistant will be persisted to the thread.
A thread's current and historical state can be retrieved. To persist state, a thread must be created prior to executing a run.
The state of a thread at a particular point in time is called a [checkpoint](../../concepts/persistence.md#checkpoints). Checkpoints 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 are persisted and can be used to restore the state of a thread at a later time.
For more on threads and checkpoints, see this section of the [LangGraph conceptual guide](../../concepts/persistence.md).
## Learn more
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.
* 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.
+2 -2
View File
@@ -1,7 +1,7 @@
# Webhooks
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.
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.
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.
Many LangGraph Platform endpoints accept a `webhook` parameter. If this parameter is specified by an endpoint that can accept POST requests, LangGraph Platform will send a request at the completion of a run.
See the corresponding [how-to guide](../../cloud/how-tos/webhooks.md) for more detail.
+5 -5
View File
@@ -4,14 +4,14 @@ Before deploying, review the [conceptual guide for the Cloud SaaS](../../concept
## Prerequisites
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.
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.
## 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 Cloud deployments.
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 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 Cloud deployments.
1. In the left-hand navigation panel, select `LangGraph Platform`. The `LangGraph Platform` view contains a list of existing LangGraph Platform deployments.
1. Select an existing deployment to 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 Cloud deployments.
1. In the left-hand navigation panel, select `LangGraph Platform`. The `LangGraph Platform` view contains a list of existing LangGraph Platform 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`.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 461 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 642 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 288 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 418 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 401 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 453 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

@@ -2,6 +2,9 @@
Before deploying, review the [conceptual guide for the Self-Hosted Control Plane](../../concepts/langgraph_self_hosted_control_plane.md) deployment option.
!!! info "Important"
The Self-Hosted Control Plane deployment option is currently in beta stage and requires an [Enterprise](../../concepts/plans.md) plan.
## Prerequisites
1. You are using Kubernetes.
@@ -2,6 +2,9 @@
Before deploying, review the [conceptual guide for the Self-Hosted Data Plane](../../concepts/langgraph_self_hosted_data_plane.md) deployment option.
!!! info "Important"
The Self-Hosted Data Plane deployment option is currently in beta stage and requires an [Enterprise](../../concepts/plans.md) plan.
## Prerequisites
1. Use the [LangGraph CLI](../../concepts/langgraph_cli.md) to [test your application locally](../../tutorials/langgraph-platform/local-server.md).
@@ -15,7 +18,7 @@ Before deploying, review the [conceptual guide for the Self-Hosted Data Plane](.
helm repo add kedacore https://kedacore.github.io/charts
helm install keda kedacore/keda --namespace keda --create-namespace
1. A valid `Ingress` controller is install on your cluster.
1. A valid `Ingress` controller is installed on your cluster.
1. You have slack space in your cluster for multiple deployments. `Cluster-Autoscaler` is recommended to automatically provision new nodes.
### Setup
+3 -6
View File
@@ -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 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.
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.
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 Cloud.
If you prefer using poetry for dependency management, check out [this how-to guide](./setup_pyproject.md) on using `pyproject.toml` for LangGraph Platform.
!!! 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.
@@ -31,7 +31,7 @@ After each step, an example file directory is provided to demonstrate how code c
## Specify Dependencies
Dependencies can optionally be specified in one of the following files: `pyproject.toml`, `setup.py`, or `requirements.txt`. If none of these files is created, then dependencies can be specified later in the [LangGraph configuration file](#create-langgraph-api-config).
Dependencies can optionally be specified in one of the following files: `pyproject.toml`, `setup.py`, or `requirements.txt`. If none of these files is created, then dependencies can be specified later in the [LangGraph configuration file](#create-langgraph-configuration-file).
The dependencies below will be included in the image, you can also use them in your code, as long as with a compatible version range:
@@ -129,9 +129,6 @@ workflow.add_edge("action", "agent")
graph = workflow.compile()
```
!!! warning "Assign `CompiledGraph` to Variable"
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:
```bash
@@ -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 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.
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.
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.
@@ -155,10 +155,6 @@ const workflow = new StateGraph(MessagesAnnotation)
export const graph = workflow.compile();
```
!!! info "Assign `CompiledGraph` to Variable"
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:
```bash
+19 -17
View File
@@ -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 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.
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.
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.
@@ -31,7 +31,7 @@ After each step, an example file directory is provided to demonstrate how code c
## Specify Dependencies
Dependencies can optionally be specified in one of the following files: `pyproject.toml`, `setup.py`, or `requirements.txt`. If none of these files is created, then dependencies can be specified later in the [LangGraph configuration file](#create-langgraph-api-config).
Dependencies can optionally be specified in one of the following files: `pyproject.toml`, `setup.py`, or `requirements.txt`. If none of these files is created, then dependencies can be specified later in the [LangGraph configuration file](#create-langgraph-configuration-file).
The dependencies below will be included in the image, you can also use them in your code, as long as with a compatible version range:
@@ -56,22 +56,27 @@ cloudpickle>=3.0.0
Example `pyproject.toml` file:
```toml
[tool.poetry]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "my-agent"
version = "0.0.1"
description = "An excellent agent build for LangGraph cloud."
authors = ["Polly the parrot <1223+polly@users.noreply.github.com>"]
license = "MIT"
description = "An excellent agent build for LangGraph Platform."
authors = [
{name = "Polly the parrot", email = "1223+polly@users.noreply.github.com"}
]
license = {text = "MIT"}
readme = "README.md"
requires-python = ">=3.9"
dependencies = [
"langgraph>=0.2.0",
"langchain-fireworks>=0.1.3"
]
[tool.poetry.dependencies]
python = ">=3.9"
langgraph = "^0.2.0"
langchain-fireworks = "^0.1.3"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
[tool.hatch.build.targets.wheel]
packages = ["my_agent"]
```
Example file directory:
@@ -137,9 +142,6 @@ workflow.add_edge("action", "agent")
graph = workflow.compile()
```
!!! warning "Assign `CompiledGraph` to Variable"
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:
```bash
@@ -22,7 +22,7 @@ Before deploying, review the [conceptual guide for the Standalone Container](../
`<database_name_1>` and `database_name_2` are different databases within the same instance, but `<hostname_1>` is shared. **The same database cannot be used for separate deployments**.
1. `LANGSMITH_API_KEY`: (if using [Lite](../../concepts/langgraph_server.md#server-versions)) LangSmith API key. This will be used to authenticate ONCE at server start up.
1. `LANGGRAPH_CLOUD_LICENSE_KEY`: (if using [Enterprise](../../concepts/langgraph_data_plane.md#lite-vs-enterprise)) LangGraph Platform license key. This will be used to authenticate ONCE at server start up.
1. `LANGGRAPH_CLOUD_LICENSE_KEY`: (if using [Enterprise](../../concepts/langgraph_data_plane.md#licensing)) LangGraph Platform license key. This will be used to authenticate ONCE at server start up.
1. `LANGSMITH_ENDPOINT`: To send traces to a [self-hosted LangSmith](https://docs.smith.langchain.com/self_hosting) instance, set `LANGSMITH_ENDPOINT` to the hostname of the self-hosted LangSmith instance.
## Kubernetes (Helm)
@@ -0,0 +1,342 @@
# 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.
@@ -1,152 +0,0 @@
# 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.
@@ -1,203 +0,0 @@
# 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'
+25 -20
View File
@@ -1,31 +1,36 @@
# Testing local agents with remote traces
# Debug LangSmith traces
## Overview
This guide explains how to open LangSmith traces in LangGraph Studio for interactive investigation and debugging.
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.
## Open deployed threads
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.
1. Open the LangSmith trace, selecting the root run.
2. Click "Run in Studio".
## Requirements
This will open LangGraph Studio connected to the associated LangGraph Platform deployment with the trace's parent thread selected.
!!! info "Prerequisites"
## 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"
- langgraph>=0.3.18
- langgraph-api>=0.0.32
- Contains the same set of nodes present in the remote trace
- 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.
### Cloning Thread
## Cloning Thread
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.
First navigate to the LangSmith trace. Here you should see a button to "Run in Studio".
![Run in Studio](img/run_in_studio.png){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.
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.
+109 -12
View File
@@ -1,11 +1,6 @@
# How to create Assistants
# Manage assistants
!!! 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.
In this guide we will show how to create, configure, and manage an [assistant](../../concepts/assistants.md).
First, as a brief refresher on the concept of configurations, consider the following simple `call_model` node and configuration schema. Observe that this node tries to read and use the `model_name` as defined by the `config` object's `configurable`.
@@ -51,11 +46,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).
## Creating an Assistant
## Create an assistant
### LangGraph SDK
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.
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.
This example uses the same configuration schema as above, and creates an assistant with `model_name` set to `openai`.
@@ -79,7 +74,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> });
let openAIAssistant = await client.assistants.create({
const openAIAssistant = await client.assistants.create({
graphId: 'agent',
name: "Open AI Assistant",
config: { "configurable": { "model_name": "openai" } },
@@ -123,7 +118,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.
## Using an Assistant
## Use an assistant
### LangGraph SDK
@@ -150,7 +145,7 @@ We have now created an assistant called "Open AI Assistant" that has `model_name
```js
const thread = await client.threads.create();
let input = { "messages": [{ "role": "user", "content": "who made you?" }] };
const input = { "messages": [{ "role": "user", "content": "who made you?" }] };
const streamResponse = client.runs.stream(
thread["thread_id"],
@@ -228,3 +223,105 @@ 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 previous 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 assistant, 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 its 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.
-134
View File
@@ -1,134 +0,0 @@
# 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.
+3 -3
View File
@@ -1,10 +1,10 @@
# Cron Jobs
# Use 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 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.
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.
## Setup
First, let's setup our SDK client, assistant, and thread:
First, let's set up our SDK client, assistant, and thread:
=== "Python"

Some files were not shown because too many files have changed in this diff Show More