Compare commits

..
Author SHA1 Message Date
Vadym BardaandGitHub 5cab47f751 checkpoint: move memory into a directory to fix namespace import issues (#1249) 2024-08-07 09:41:06 -04:00
Vadym BardaandGitHub 172b4af6ed langgraph: release 0.2.0 (#1246) 2024-08-06 23:25:18 -04:00
Vadym BardaandGitHub 186dfd3976 checkpoint-postgres: release 1.0.0 (#1245) 2024-08-06 22:53:29 -04:00
Vadym BardaandGitHub d9adebed84 checkpoint-sqlite: release 1.0.0 (#1244) 2024-08-06 22:43:35 -04:00
Vadym BardaandGitHub 2b860f04ce checkpoint: release 1.0.1 (#1243) 2024-08-06 22:38:13 -04:00
b37f78942d checkpoint-postgres: new library for postgres checkpointer implementation (#1236)
* checkpoint-postgres: new library for postgres checkpointer implementation

---------

Co-authored-by: Nuno Campos <nuno@langchain.dev>
2024-08-06 22:37:06 -04:00
Nuno CamposandGitHub 81e60893b6 Merge pull request #1240 from langchain-ai/nc/6aug/untracked-value
Add UntrackedValue to mark a state key as not checkpointable
2024-08-06 17:15:19 -07:00
Emil WåreusandGitHub 657cd30045 Update pass_private_state.ipynb (#1213)
Fix typo
2024-08-06 23:59:58 +00:00
Vadym BardaandGitHub 7cf2132df1 Merge branch 'main' into nc/6aug/untracked-value 2024-08-06 19:46:56 -04:00
vbarda b0b6c0ae7b lint + comment 2024-08-06 19:46:39 -04:00
Nuno Campos 5f9b0bc0ae Add UntrackedValue to mark a state key as not checkpointable 2024-08-06 15:41:52 -07:00
Nuno CamposandGitHub f7c09f5e9e Fix output schema affecting stream output (#1239)
* Fix output schema affecting stream output

* Remove file

* Remove file
2024-08-06 22:26:43 +00:00
Andrew NguonlyandGitHub 9c1aeb31dd Remove LangGraph Cloud waitlist from README (#1238)
* Remove waitlist from README.

* Remove waitlist from Cloud index page.

* Remove waitlist from README.
2024-08-06 13:32:55 -07:00
gbaian10andGitHub 43fb6012be fix: the execution error passed from ToolExecutor to create_react_agent (#1234) 2024-08-06 17:51:55 +00:00
Nuno CamposandGitHub 997a2afc79 Merge pull request #1233 from langchain-ai/nc/6aug/add-test-inherited-state-keys
Add test for inherited state keys
2024-08-06 09:30:59 -07:00
Nuno Campos f050f71584 Add test for inherited state keys 2024-08-06 09:20:33 -07:00
Nuno CamposandGitHub 3b63010673 Merge pull request #1232 from langchain-ai/dqbd/js-output-schema
feat(sdk-js): add output schema to match API, expose copy method for thread
2024-08-06 08:18:00 -07:00
Tat Dat Duong bb47a0d9b7 Reformat 2024-08-06 16:24:12 +02:00
Tat Dat Duong 475019731a Bump to 0.0.4 2024-08-06 15:34:28 +02:00
Tat Dat Duong 1e3a0aa88c Expose copy method for thread 2024-08-06 14:15:04 +02:00
Tat Dat Duong 3ba8500e90 feat(sdk-js): Add output schema typedef 2024-08-06 14:06:47 +02:00
Nuno CamposandGitHub 8850e8a373 Merge pull request #1228 from langchain-ai/vb/update-sqlite
checkpoint: stop using sqlite checkpointers as context managers, make memorysaver a context manager
2024-08-05 14:09:42 -07:00
vbarda 566e6c9b60 fix 2024-08-05 17:03:50 -04:00
vbarda 9aaa73cc55 undo comment 2024-08-05 16:55:19 -04:00
vbarda 55717c9bde update memory saver + tests 2024-08-05 16:53:33 -04:00
vbarda 64c30508d9 checkpoint-sqlite: stop using checkpointers as context managers, use contextmanager only in from_conn_string 2024-08-05 16:20:16 -04:00
ccurmeandGitHub a00ace21cb Merge pull request #1227 from langchain-ai/cc/fix_toolbar_title
docs: update title of doc in sidebar
2024-08-05 14:00:29 -04:00
Chester Curme e590a027db update title of doc in sidebar 2024-08-05 13:57:22 -04:00
ccurmeandGitHub 2976d4a3ba Merge pull request #1117 from langchain-ai/cc/many_tools_guide
docs: add how-to guide for handling many tools
2024-08-05 13:55:58 -04:00
Chester Curme 14d448b74d remove usage of upsert 2024-08-05 13:45:47 -04:00
Chester Curme ad31da06d1 update script + index + mkdocs 2024-08-05 13:34:30 -04:00
Chester Curme f124ce2f72 move file 2024-08-05 13:28:54 -04:00
Chester Curme 95f1032215 Merge branch 'main' into cc/many_tools_guide 2024-08-05 13:25:32 -04:00
Nuno CamposandGitHub c2c5b31f4b Merge pull request #1223 from langchain-ai/nc/5aug/test-update-state-custom-class
Add test for update_state when using custom state class
2024-08-05 09:00:34 -07:00
Nuno Campos e8c2fefb29 Add test for update_state when using custom state class 2024-08-05 08:54:16 -07:00
Nuno Campos 4f2f5f7cbc sdk0.1.27 2024-08-03 11:47:21 -07:00
Nuno CamposandGitHub 2ae1121994 sdk: Use an identifiable root path (#1216) 2024-08-03 18:46:36 +00:00
Nuno CamposandGitHub 3b5669a90b Merge pull request #1215 from langchain-ai/nc/3aug/sdk-asgi
sdk: Use ASGI transport when called inside langgraph-api
2024-08-03 11:20:26 -07:00
Nuno Campos 85454b6371 sdk: Use ASGI transport when called inside langgraph-api 2024-08-03 11:17:34 -07:00
Nuno CamposandGitHub 25a98fa888 Merge pull request #1210 from langchain-ai/vb/update-deps
checkpoint-sqlite: add checkpoint dependency
2024-08-02 17:14:54 -07:00
vbarda 2370db0f8c order 2024-08-02 19:08:16 -04:00
vbarda 1b353aed73 checkpoint-sqlite: add checkpoint dependency 2024-08-02 19:06:38 -04:00
Vadym BardaandGitHub 7b441e64e7 ci: update import pre-release check (#1209)
* ci: update import pre-release check

* fix
2024-08-02 18:58:04 -04:00
Vadym BardaandGitHub 351a29fcfb ci: handle initial library version in tags (#1208) 2024-08-02 18:44:25 -04:00
Vadym BardaandGitHub c149a99b44 checkpoint-sqlite: new library for sqlite checkpointer implementation (#1203)
* checkpoint-sqlite: new library for sqlite checkpointer implementation
2024-08-02 22:14:12 +00:00
Nuno CamposandGitHub 16a6450534 Merge pull request #1191 from langchain-ai/dqbd/js-sdk-types
feat(sdk-js): bump to 0.0.3, update types of updateState
2024-08-02 13:05:20 -07:00
Nuno CamposandGitHub 045f07c396 Merge pull request #1204 from langchain-ai/nc/2aug/graph-metadata-interrupt
Add interrupt info to graph repr
2024-08-02 13:05:11 -07:00
Nuno CamposandGitHub 373bcfe5cf Merge pull request #1206 from langchain-ai/nc/2aug/test-watch-all
Add make test_watch_all command
2024-08-02 13:04:39 -07:00
2742b2f884 aupdate_state now accepts null values (#1181)
* aupdate_state now accepts null values

---------

Co-authored-by: vbarda <vadym@langchain.dev>
2024-08-02 15:59:15 -04:00
Nuno Campos c30e80df67 Add missing 2024-08-02 12:59:11 -07:00
Nuno Campos 55044ba231 Add make test_watch_all command 2024-08-02 12:56:53 -07:00
Nuno Campos b1d0dbac77 Add interrupt info to graph repr 2024-08-02 12:20:56 -07:00
Nuno CamposandGitHub 742f17689e Merge pull request #1199 from langchain-ai/vb/bump-core
langgraph: bump core to 0.2.27
2024-08-02 12:07:23 -07:00
Vadym BardaandGitHub bbd5e692e1 checkpoint: release 1.0.0 (#1201) 2024-08-02 14:10:00 -04:00
Isaac FranciscoandGitHub c40df063d6 draft (#1200) 2024-08-02 11:06:52 -07:00
vbarda a0cd3ff7ad langgraph: bump core to 0.2.27 2024-08-02 13:56:26 -04:00
Nuno CamposandGitHub b2b31a323b Merge pull request #1197 from langchain-ai/nc/2aug/managed-rm-graph-arg
Remove graph arg from ManagedValue
2024-08-02 09:28:07 -07:00
Vadym BardaandGitHub 50eea98fc6 langgraph: remove deprecations and add new warnings (#1196)
* langgraph: remove deprecations and add new warnings
2024-08-02 12:18:34 -04:00
Nuno Campos a83718bec8 Remove graph arg from ManagedValue 2024-08-02 08:49:41 -07:00
Vadym BardaandGitHub 4d7a42a65e langgraph: remove FewShotExamples managed value (#1195) 2024-08-02 11:05:17 -04:00
Isaac FranciscoandGitHub 487157eafa typo fix (#1169) 2024-08-01 21:48:37 -04:00
4b2187c9a3 checkpoint: switch thread_ts -> checkpoint_id, add checkpoint_ns, change serializer protocol (#1185)
---------

Co-authored-by: Nuno Campos <nuno@langchain.dev>
2024-08-02 01:08:19 +00:00
Tat Dat Duong a6e32e57e8 Bump to 0.0.3 2024-08-01 14:14:40 -07:00
Tat Dat Duong 51dbb9493c Improve types for updateState 2024-08-01 14:14:12 -07:00
Nuno CamposandGitHub 862afa27de Merge pull request #1189 from langchain-ai/nfcampos-patch-2
Update constraints
2024-08-01 10:13:18 -07:00
Nuno CamposandGitHub aa8cd8259d Update setup_pyproject.md 2024-08-01 10:08:42 -07:00
Nuno CamposandGitHub 000066d1a1 Update setup.md 2024-08-01 10:08:07 -07:00
Nuno CamposandGitHub cd2b6642ee Merge pull request #1188 from langchain-ai/nc/1aug/update-sdks
Nc/1aug/update sdks
2024-08-01 09:54:47 -07:00
Nuno Campos 15ded2c17b Mark all schemas as optional in js and py sdk typings 2024-08-01 09:43:46 -07:00
Nuno Campos e4905f438a Fix create entrypoints script 2024-08-01 09:43:30 -07:00
Nuno Campos eb762c4a77 Undo 2024-07-31 15:02:25 -07:00
Nuno Campos ea5eb73b9f Enable builds outside of master 2024-07-31 15:00:44 -07:00
ae74825ea7 langgraph checkpoint: new library for checkpoint interfaces (#1163)
---------

Co-authored-by: Nuno Campos <nuno@langchain.dev>
2024-07-31 16:45:52 -04:00
Nuno CamposandGitHub 913a2d975b Merge pull request #1180 from langchain-ai/eugene/add_any_id_handling
langgraph[patch]: update unit tests to handle AnyStr() for pydantic 2 models
2024-07-31 12:30:54 -07:00
Eugene Yurtsev 5043aaf4fa UPdate 2024-07-31 14:33:20 -04:00
Vadym BardaandGitHub c3f6c58e13 docs: fix typo in retries (#1177) 2024-07-31 14:39:43 +00:00
Nuno Campos 298c93ca4a lib0.1.17 2024-07-30 18:27:48 -07:00
Nuno CamposandGitHub dd52472312 Merge pull request #1172 from langchain-ai/nc/30jul/update-no-values
Allow call to update_state without values
2024-07-30 18:27:00 -07:00
Nuno Campos 6475d81f29 Oops 2024-07-30 18:26:39 -07:00
Nuno Campos 51b4475fcc Allow call to update_state without values
- this means "fork without update" (eg to rerun a node)
2024-07-30 18:21:15 -07:00
Nuno Campos 3238fa0870 Add test for drawing lance example 2024-07-30 15:50:18 -07:00
Nuno CamposandGitHub fdaa5a3037 Merge pull request #1159 from akshseh/fix_visualization_example
fix: update the function for node colors
2024-07-30 10:04:05 -07:00
Akarsha SehwagandGitHub 12238c7b7e Merge branch 'main' into fix_visualization_example 2024-07-30 14:13:35 +02:00
Nuno Campos 3006084326 lib0.1.16 2024-07-29 12:46:33 -07:00
Nuno CamposandGitHub f448df4638 Merge pull request #1160 from langchain-ai/nc/29jul/fix-cond-after-multi-send
Fix issue when cond edge visited after multiple executions of Send
2024-07-29 12:46:00 -07:00
Nuno Campos 466cb8acb5 Fix issue when cond edge visited after multiple executions of Send
- cond edge will run for each execution of Send, so target channels need to support multiple publishes
2024-07-29 12:38:48 -07:00
Akarsha SehwagandGitHub 1a0ad5fdd0 fix: update the function for node colors
NodeColors does not exist anymore in Langchain_core -> updated to NodeStyles and changed the param names.
2024-07-29 17:23:48 +02:00
ea071935fe adding message info (#1150)
---------

Co-authored-by: vbarda <vadym@langchain.dev>
2024-07-26 20:24:19 +00:00
Nuno Campos 794a0fff03 lib0.1.15 2024-07-26 10:58:56 -07:00
Nuno CamposandGitHub 06ed6d7cab Merge pull request #1152 from langchain-ai/nc/26jul/pydantic-2-compat
lib: Improve compat with pydantic 2 models
2024-07-26 10:58:24 -07:00
Nuno Campos 6eacc6b7c8 lib: Improve compat with pydantic 2 models 2024-07-26 10:51:59 -07:00
Nuno CamposandGitHub f6ac881591 Merge pull request #1151 from langchain-ai/wfh/shrink
Shrink images
2024-07-26 10:50:56 -07:00
William Fu-Hinthorn f431b415fc Shrink images 2024-07-26 10:06:20 -07:00
Chester Curme 8d4b95afa8 add section 2024-07-26 11:22:01 -04:00
Lance MartinandGitHub 4b51c27461 Add llama3.1 tool calling (#1148) 2024-07-26 08:08:22 -07:00
Vadym BardaandGitHub 585c5c41ce docs: sync readmes (#1147) 2024-07-26 14:14:42 +00:00
BagaturandGitHub c64588a673 docs: rm discord from readme (#1145) 2024-07-26 08:19:54 -04:00
Vadym BardaandGitHub 75fa7395bd docs: add better state/reducers description in intro tutorial (#1140) 2024-07-25 22:01:18 +00:00
Isaac FranciscoandGitHub 66ad48e771 typo (#1139) 2024-07-25 21:36:28 +00:00
Isaac FranciscoandGitHub 41fd8020ee sdk-py: add docstrings (#1130) 2024-07-25 16:51:25 -04:00
Nuno CamposandGitHub 09a28ccef6 Merge pull request #1136 from langchain-ai/isaac/reducegifsizes
reduce video sizes
2024-07-25 11:57:21 -07:00
isaac hershenson c0431227d8 reduce videos 2024-07-25 11:53:29 -07:00
Nuno CamposandGitHub b5f861722d Merge branch 'main' into cc/many_tools_guide 2024-07-25 11:15:05 -07:00
Nuno CamposandGitHub 770e1601e5 Merge pull request #1135 from langchain-ai/nc/25jul/ci-large-size
Add CI check for large files added
2024-07-25 10:26:11 -07:00
Nuno Campos 045c2af663 Remove test image 2024-07-25 10:20:03 -07:00
Nuno Campos 23d3a7ac07 Improve output 2024-07-25 10:18:55 -07:00
Nuno Campos 67d00aca90 Fix 2024-07-25 10:16:56 -07:00
Nuno Campos e54989ca74 Add quotes 2024-07-25 10:05:25 -07:00
Nuno Campos 14372a4515 Try again 2024-07-25 10:04:05 -07:00
Nuno Campos 6e33bda433 Different flag? 2024-07-25 10:00:21 -07:00
Nuno Campos 77eb88eef2 Try again 2024-07-25 09:59:23 -07:00
Nuno Campos 7584f058c2 Add prints 2024-07-25 09:47:06 -07:00
Nuno Campos fba6e0504c Fix 2024-07-25 09:45:27 -07:00
Nuno Campos c17fe2d189 Support paths with spaces 2024-07-25 09:44:31 -07:00
Nuno Campos 4a58dcccf2 Add test large image 2024-07-25 09:36:02 -07:00
Nuno Campos ed2e1a736f Add CI check for large files added 2024-07-25 09:34:33 -07:00
Nuno CamposandGitHub a168615f2d Merge pull request #1126 from langchain-ai/vb/add-graph-factory-example 2024-07-24 18:35:36 -07:00
vbarda 190372e137 update 2024-07-24 21:22:19 -04:00
Isaac FranciscoandGitHub eba8303c98 display state management (#1127) 2024-07-24 16:57:17 -07:00
William FHandGitHub 2845d7ace5 Shrink Images (#1125) 2024-07-24 16:50:08 -07:00
vbarda 2ceac211e7 docs: add how to for graph factory + update cli 2024-07-24 18:14:34 -04:00
Vadym BardaandGitHub 8f6b3b636d docs: fix link in subgraphs how-to (#1124) 2024-07-24 16:59:46 -04:00
Nuno Campos ebe01c2639 lib0.1.14 2024-07-24 10:53:23 -07:00
Nuno CamposandGitHub 272219e410 Merge pull request #1121 from langchain-ai/nc/24jul/disable-nested-checkpoints-unless-interrupt 2024-07-24 10:52:29 -07:00
Nuno Campos bb1324cdc3 Disable nested checkpoints unless interrupts set on subgraph 2024-07-24 10:45:39 -07:00
Nuno Campos 85af0603fa lib0.1.13 2024-07-24 09:49:23 -07:00
Chester Curme 590f810b53 add concluding text 2024-07-24 12:45:37 -04:00
Nuno CamposandGitHub e6e5911aae Merge pull request #1119 from langchain-ai/nc/24jul/avoid-crash-missing-node
Avoid crash when a node in pending sends is removed
2024-07-24 09:41:20 -07:00
Nuno CamposandGitHub 9fdbd0dd49 Merge pull request #1118 from langchain-ai/nc/24jul/fix-recursion-limit-thread
Fix recursion limit considering steps taken in previous runs on same thread
2024-07-24 09:35:12 -07:00
Nuno Campos 8a20c6f7e4 Avoid crash when a node in pending sends is removed 2024-07-24 09:29:38 -07:00
Nuno Campos 03f9b27e3b Fix 2024-07-24 09:26:49 -07:00
Nuno CamposandGitHub 19f6f7d5ca Merge pull request #1110 from langchain-ai/wfh/test_pending_writes_null
Test Null Pending Writes
2024-07-24 08:51:33 -07:00
Nuno Campos 745b96eb63 Fix recursion limit considering steps taken in previous runs on same thread 2024-07-24 08:50:32 -07:00
Chester Curme 7ea5da73c7 add guide 2024-07-24 11:49:05 -04:00
b90d44d97d Add local tool calling agent example (#1109)
* Add local tool calling agent example

* Update copy_notebooks.py

---------

Co-authored-by: Nuno Campos <nuno@langchain.dev>
2024-07-24 07:39:53 -07:00
Vadym BardaandGitHub d99d3e05c7 langgraph: release 0.1.12 (#1116) 2024-07-24 10:38:07 -04:00
Vadym BardaandGitHub ca6aef4746 langgraph: bring back tool content stringify (#1114) 2024-07-24 10:36:32 -04:00
William Fu-Hinthorn f5b9e463e3 Merge branch 'main' into wfh/test_pending_writes_null 2024-07-23 21:28:51 -07:00
William FHandGitHub 82cbe25be8 Update typedoc build (#1087) 2024-07-23 21:27:40 -07:00
William Fu-Hinthorn 36505d9656 Add test 2024-07-23 21:15:53 -07:00
Lance MartinandGitHub b72ea0ea1b Update MR docs (#1098) 2024-07-23 17:13:20 -07:00
Vadym BardaandGitHub 603a137d33 langgraph: release 0.1.11 (#1107) 2024-07-23 18:06:07 -04:00
Nuno CamposandGitHub 4cdb38d337 Fix null pending writes (#1106) 2024-07-23 17:59:16 -04:00
Nuno Campos 03bf1e5414 Fix null pending writes 2024-07-23 14:55:07 -07:00
Nuno CamposandGitHub 4fa5c8bab0 Merge pull request #1089 from mihaic/patch-1
Fix StateGraph.add_edge end_key check
2024-07-23 13:52:33 -07:00
Nuno CamposandGitHub 9711f39995 Merge pull request #1090 from langchain-ai/harrison/private-state
Harrison/private state
2024-07-23 13:50:02 -07:00
Nuno CamposandGitHub 091bc003ae Merge pull request #1088 from langchain-ai/vb/update-return-type
langgraph: add proper type annotation for StateGraph.compile
2024-07-23 13:49:12 -07:00
Nuno CamposandGitHub 57c40026f8 Update pass_private_state.ipynb 2024-07-23 13:46:56 -07:00
Nuno CamposandGitHub bf61f6f869 Merge pull request #1102 from langchain-ai/an/23jul/docs-langgraph-cloud-constraints
docs: Add constraints info to LangGraph Cloud setup docs
2024-07-23 13:45:30 -07:00
Nuno Campos 6c299a1b5a Fix docs build 2024-07-23 13:42:57 -07:00
Nuno CamposandGitHub aeae796e60 Update setup_pyproject.md 2024-07-23 13:29:56 -07:00
Nuno CamposandGitHub 7ec37575de Update setup.md 2024-07-23 13:29:43 -07:00
Nuno CamposandGitHub 4fdd05532d Merge pull request #1100 from langchain-ai/harrison/migrations
migrations of graph
2024-07-23 13:26:18 -07:00
Nuno CamposandGitHub 43c793f7fe Update low_level.md 2024-07-23 13:24:46 -07:00
Lance MartinandGitHub 1765000263 Local RAG agent testing with Llama3.1 (#1104) 2024-07-23 13:05:51 -07:00
Vadym BardaandGitHub 3fdd4715f5 langgraph: release 0.1.10 (#1103) 2024-07-23 14:13:35 -04:00
Isaac FranciscoandGitHub 97cb44058a [docs]:open studio from local deployment (#1037)
* open studio from local deployment

* spelling

* harrison comments
2024-07-23 12:50:57 -04:00
Andrew Nguonly 73e0871345 Add constraints info to LangGraph Cloud setup docs. 2024-07-23 08:56:56 -07:00
Harrison Chase c8f046869b migrations of graph 2024-07-23 08:20:03 -07:00
Nuno CamposandGitHub bd4dccd9cb Merge pull request #1075 from langchain-ai/nc/19jul/nested-checkpoints 2024-07-22 18:41:56 -07:00
vbarda c268d695f0 spellcheck 2024-07-22 21:28:11 -04:00
vbarda 0891cc1864 comments / bring back sleep 2024-07-22 21:18:42 -04:00
Nuno Campos ee7cab664a Fix checkpoint put ordering issue 2024-07-22 17:52:09 -07:00
vbarda fd8f9e87dc update types 2024-07-22 20:48:21 -04:00
vbarda ed9c015d24 remove utils 2024-07-22 20:43:28 -04:00
vbarda cd4fae601c remove sleep 2024-07-22 20:26:12 -04:00
Nuno Campos aed313ebb5 Fix stack not being unwound when suppressing interrupt 2024-07-22 17:09:47 -07:00
Nuno Campos 2008df03fa Merge branch 'main' into nc/19jul/nested-checkpoints 2024-07-22 16:33:33 -07:00
Nuno Campos 3f860617a5 Update tests 2024-07-22 16:32:18 -07:00
Nuno Campos 98a962661b Instead of saving an additional interrupt checkpoint, make child graphs keep a single checkpoint for each parent checkpoint
- while the inner graph makes progress it overwrites the partial progress checkpoints, eventually keeping only one for each outer step
- implement parent_config in MemorySaver
- fix edge cases in PregelLoop
2024-07-22 16:32:15 -07:00
Harrison Chase 9483da1bdb cr 2024-07-22 16:31:14 -07:00
Harrison Chase 8d3da565c4 cr 2024-07-22 16:30:34 -07:00
Nuno Campos 42174ad9fb Add optional id arg to create_checkpoint 2024-07-22 16:30:19 -07:00
Nuno Campos 0c1ec8d5d2 Update sync bg executor to wait on all tasks on exit 2024-07-22 16:30:08 -07:00
Harrison Chase fdbb33ddad add private state 2024-07-22 16:26:31 -07:00
Mihai CapotăandGitHub 3f43e0d945 Fix StateGraph.add_edge end_key check
It looks like a typo to me: `END` should be `START`.

`END` should be accepted as `end_key` and it is accepted with the `super` call path.
2024-07-22 15:55:19 -07:00
vbarda d00503ecc4 langgraph: add proper type annotation for StateGraph.compile 2024-07-22 18:03:16 -04:00
trevor-cyiandGitHub ab80c113ac docs: Use bound model in convo history (#1082)
This fixes a small mistake in the manage-conversation-history notebook
where the model bound with tools was not used, instead, the original model
was used when invocations occur.
2024-07-22 16:27:42 -04:00
vbarda fbced023f3 add more tests 2024-07-22 16:23:54 -04:00
William FHandGitHub 91d6b964c4 Update js sdk links (#1086) 2024-07-22 13:08:48 -07:00
vbarda 6e57fa5f9a return future on self._put_checkpoint 2024-07-22 14:32:57 -04:00
vbarda 77f6ec5a2b update memory 2024-07-22 13:03:30 -04:00
vbarda f2a95ad67f fix broken interrupt tests 2024-07-22 12:18:12 -04:00
vbarda d635a22302 sort memory checkpoints monotonically decreasing 2024-07-22 12:14:33 -04:00
Nuno CamposandGitHub b1ffa52da6 Merge pull request #1064 from KYUUUW/patch-1
[docs]Fix runtime error README.md example
2024-07-22 08:42:34 -07:00
Nuno CamposandGitHub e2b959ee7e Merge pull request #1073 from langchain-ai/nc/19jul/binop-lambda-eq
Don't include lambdas in __eq__ for BinOp
2024-07-22 08:41:30 -07:00
Nuno Campos 84e404eb71 Lint 2024-07-22 08:24:53 -07:00
Nuno Campos a1a5fc01a9 cli0.1.50 2024-07-22 08:22:05 -07:00
Nuno CamposandGitHub 2616614470 Merge pull request #1080 from langchain-ai/nc/21jul/cli-stdout-overrun
cli: Fix crash when subprocess has a very long stdout/stderr line
2024-07-22 08:21:25 -07:00
Nuno CamposandGitHub 64fd304ea0 Merge pull request #1083 from langchain-ai/vb/update-sdk 2024-07-22 08:05:50 -07:00
vbarda f820ca8f7f sdk-py: add threads copy 2024-07-21 20:21:27 -04:00
Vadym BardaandGitHub 4bacbdd2bd docs: update create_react_agent to use state_modifier (#1081) 2024-07-21 17:07:50 -04:00
Nuno CamposandGitHub 34407d9de1 Update exec.py 2024-07-21 12:19:19 -07:00
Nuno Campos b81612c292 cli: Fix crash when subprocess has a very long stdout/stderr line 2024-07-21 12:16:37 -07:00
LEE KYU WONandGitHub b3e44bed22 Fix example error 2024-07-21 18:14:01 +09:00
LEE KYU WONandGitHub e8b05faff4 Merge branch 'main' into patch-1 2024-07-21 18:12:38 +09:00
BagaturandGitHub 610b6cc78c langgraph[patch]: InjectedState annotation (#1067)
Add annotated for injecting state vars into a Tool
2024-07-19 20:07:21 -07:00
William FHandGitHub 75f8a33c9e [Docs] Format notebooks (#1076) 2024-07-19 18:29:24 -07:00
Nuno CamposandGitHub afe3905958 Update loop.py 2024-07-19 18:15:54 -07:00
Nuno CamposandGitHub 375d9a45d9 Update loop.py 2024-07-19 18:15:47 -07:00
Nuno Campos f5390a4ee5 Port to new architecture 2024-07-19 18:04:55 -07:00
William FHandGitHub 29d7a812ae [Docs] Update checkpointer docstrings (#1074) 2024-07-19 17:52:47 -07:00
Nuno CamposandGitHub fb9d5b9304 Merge pull request #1059 from langchain-ai/nc/18jul/loop
Make Pregel loop runnable step-by-step
2024-07-19 17:16:48 -07:00
Nuno Campos b77ef7d162 Lint 2024-07-19 17:13:51 -07:00
Nuno Campos b0e0d269c5 Lint 2024-07-19 16:40:50 -07:00
Nuno Campos 1dbf7a3392 Move all checkpoint edits to apply_writes 2024-07-19 16:37:27 -07:00
Nuno Campos 27cd4e2221 Update core 2024-07-19 15:44:21 -07:00
Nuno Campos dc703857dd Run tests in parallel 2024-07-19 15:43:40 -07:00
Nuno CamposandGitHub 5df5eb6b09 Merge pull request #1072 from langchain-ai/isaac/retrynodeshowto
[docs]: add node retry docs
2024-07-19 15:00:03 -07:00
isaac hershenson eaa23ef4e5 added retrypolicy reference 2024-07-19 14:56:14 -07:00
Nuno Campos 4dc1195bcd Don't include lambdas in __eq__ for BinOp
When using forward refs, inline lambdas in Annotated are re-evaluated for every subclass, thus making the comparison fail
2024-07-19 14:46:01 -07:00
isaac hershenson 55c78bbf90 remove unneccesary imports 2024-07-19 13:13:02 -07:00
isaac hershenson 2a7ae83bf8 spelling 2024-07-19 13:07:26 -07:00
isaac hershenson d7dc16310f nuno comments 2024-07-19 13:05:28 -07:00
isaac hershenson afd940d9bd first draft 2024-07-19 12:38:55 -07:00
Nuno Campos ed5d114087 Fix 2024-07-19 12:19:43 -07:00
Nuno CamposandGitHub 9d14f4f2f1 docs: Add note on langgraph v langgraph cloud (#1071) 2024-07-19 10:40:02 -07:00
Nuno CamposandGitHub 29e860acca Update manager.py 2024-07-19 08:52:08 -07:00
Nuno CamposandGitHub b26be0c113 Merge pull request #1070 from langchain-ai/vb/update-assistants
sdk-py: add if_exists for assistants
2024-07-19 08:39:11 -07:00
Nuno CamposandGitHub ec2892098b Update client.py 2024-07-19 08:21:58 -07:00
vbarda c7133fd34b Merge branch 'main' into vb/update-assistants 2024-07-19 10:12:26 -04:00
Nuno Campos 7ed5f9e4bc Implement AsyncLoop 2024-07-18 17:28:44 -07:00
vbarda 8bd2d88be4 sdk-py: add if_exists for assistants 2024-07-18 19:59:12 -04:00
BagaturandGitHub 3a53843185 langgraph[patch]: refactor ToolNode (#1066) 2024-07-18 16:13:06 -07:00
Nuno Campos f974471f7d Lint 2024-07-18 15:52:20 -07:00
Nuno Campos 909bf4433f Lint 2024-07-18 15:51:37 -07:00
Nuno Campos 73b4b6ec5b Lint 2024-07-18 15:50:45 -07:00
Nuno Campos 06f83710c8 Lint 2024-07-18 15:49:58 -07:00
Nuno Campos b3ee728839 Pregel.stream passing all tests 2024-07-18 15:49:58 -07:00
Nuno Campos 60f7a7d593 Lint 2024-07-18 15:49:58 -07:00
Nuno Campos 56238036d7 WIP: Split out loop logic from Pregel class 2024-07-18 15:49:58 -07:00
LEE KYU WONandGitHub 3f35a0a44e Update wait-user-input.ipynb (#1065) 2024-07-18 18:48:22 -04:00
LEE KYU WONandGitHub 58af2c7d9f Fix error in example 2024-07-19 04:29:39 +09:00
Vadym BardaandGitHub 95a4a55849 docs: add how to for passing config to tools (#1063) 2024-07-18 13:33:56 -04:00
Nuno Campos 97281d708b lib 0.1.9 2024-07-18 10:28:24 -07:00
Nuno CamposandGitHub c39f4ce47a Merge pull request #1062 from langchain-ai/nc/18jul/node-retry-policy
Enable configuring retry policy for each node of StateGraph
2024-07-18 10:24:31 -07:00
Nuno Campos 833b1faba2 Enable configuring retry policy for each node of StateGraph 2024-07-18 10:18:29 -07:00
Nuno Campos 2ca816ca25 cli 0.1.49 2024-07-18 10:01:43 -07:00
Nuno CamposandGitHub 50861cdf1b Merge pull request #1036 from langchain-ai/nc/16jul/node-state
Add node state, customizable graph input and output schemas
2024-07-18 09:56:20 -07:00
Nuno Campos b5b0f8d7e3 Remove node output schemas 2024-07-18 09:46:06 -07:00
Nuno CamposandGitHub 87e80fe45a Merge pull request #1058 from langchain-ai/nc/18jul/cli-dockerfile
cli: Add dockerfile command to write dockerfile
2024-07-18 09:28:44 -07:00
Nuno Campos ffe9a6d5b8 cli: Add dockerfile command to write dockerfile 2024-07-18 09:04:14 -07:00
BagaturandGitHub 97ae473002 docs: pass in graph state to tool (#1056) 2024-07-18 07:34:19 -07:00
William FHandGitHub 246b7dafcf Add Docstrings (#1057) 2024-07-18 07:12:12 -07:00
William FHandGitHub 254926c4a8 Update template links (#1045) 2024-07-18 07:10:10 -07:00
Vadym BardaandGitHub 5fbc9555c3 docs: add how-to section on tool calling (#1051) 2024-07-18 09:36:15 -04:00
Vadym BardaandGitHub 83089c3a7f langgraph: add incorrect tool name handling to ToolNode (#1052) 2024-07-17 21:33:46 -04:00
Nuno CamposandGitHub c2e25e2ac2 Merge pull request #1049 from langchain-ai/nc/17jul/pregel-node-retry-policy
pregel: Support retry policy per node
2024-07-17 14:54:23 -07:00
Andrew NguonlyandGitHub e3ef9adac7 Add instructions to test locally before deploying to Cloud. (#1050) 2024-07-17 14:04:15 -07:00
Nuno Campos e4bfcffed4 pregel: Support retry policy per node
- to expose from StateGraph in future PR
2024-07-17 13:17:15 -07:00
Nuno CamposandGitHub a184c7f23a lib: Separate out executor logic from stream methods (#1048)
* lib: Separate out executor logic from stream methods

- first step towards splitting out tick and stream

* Lint

* Lint

* Lint

* Lint
2024-07-17 13:16:35 -07:00
clement.landGitHub a96f5f8f27 docs: fix typo in low_level.md (#1044) 2024-07-17 09:57:26 -04:00
Vadym BardaandGitHub 79dc1a64b3 langgraph: allow passing custom state & let message modifiers access state in create_react_agent (#1023) 2024-07-16 20:41:17 -04:00
Nuno Campos 7bc489f1f4 Add node state, customizable graph input and output schemas 2024-07-16 13:50:20 -07:00
924cd1f2d8 [docs]: minor changes for testing locally (#1029)
* small changes

* harrison comments

* Update docs/docs/cloud/deployment/test_locally.md

---------

Co-authored-by: Harrison Chase <hw.chase.17@gmail.com>
2024-07-16 10:43:03 -07:00
William FHandGitHub 6fd1dc5697 Add note to docstring (#1035) 2024-07-16 08:43:49 -07:00
Arthur BermanandGitHub 09c5ebc94d Update low_level.md (#1016)
replace add_edge by add_conditional_edges
fix a mistake
2024-07-16 08:43:40 -07:00
Vadym BardaandGitHub ebeebcd095 sdk-js: release 0.0.2 (#1033) 2024-07-16 09:20:25 -04:00
Vadym BardaandGitHub 3713ba66a8 sdk-js: add thread_id and if_exists to ThreadsClient.create (#1032) 2024-07-16 09:18:22 -04:00
Jacob LeeandGitHub 449b42717c Add guide on handling tool calling errors (#1012)
* Fix example

CC @vbarda

* Progress on tool calling errors

* Update

* Rename

* Format

* Clean up outputs

* Revert

* Use stream instead of invoke for final example
2024-07-15 23:22:50 -07:00
BagaturandGitHub 2a1cdd31c5 langgraph[patch]: ToolNode support for tools outputting msg (#977)
ToolNode passes ToolCall to tools directly and outputs the generated ToolMessages directly
2024-07-15 16:40:37 -07:00
Nuno CamposandGitHub 5444563fdc Only render end node if there is an edge leading to it (#1022) 2024-07-15 11:26:05 -07:00
Vadym BardaandGitHub fad4de789f sdk-py: release 0.1.26 (#1026) 2024-07-15 13:44:41 -04:00
Vadym BardaandGitHub bf10f39b6b docs: update delete messages how-to for clarity (#1025) 2024-07-15 13:36:19 -04:00
Vadym BardaandGitHub ebf6316fa7 docs: fix collapsible code block affecting TOC in tutorial (#1024) 2024-07-15 13:16:28 -04:00
Vadym BardaandGitHub 647c5737f0 sdk-py: add if_exists to threads.create (#1009) 2024-07-15 12:15:43 -04:00
Vedanta SPandGitHub 9e7695085e Update langgraph_self_rag_local.ipynb (#1014)
replaced pprint with print
2024-07-14 16:32:07 -04:00
Nuno CamposandGitHub 8cf10fa460 Specific error when updating state as an unknown node (#1015) 2024-07-14 06:54:01 -07:00
Vadym BardaandGitHub 3ef65c37b3 docs: add note for langgraph cloud checkpointers (#1011) 2024-07-12 16:48:03 -04:00
Vadym BardaandGitHub 4b5152fde5 docs: small fix in postgres docs (#1010) 2024-07-12 16:27:58 -04:00
Jacob LeeandGitHub 07c3c5ff6f Hide output spacer column in docs (#1008) 2024-07-12 13:00:01 -07:00
Lance MartinandGitHub ada17d2ccb Improve visuals (#1005) 2024-07-12 12:55:32 -07:00
738f725aea Support multiple interruptions after resuming execution (#1004)
* Support multiple interruptions

* Add async test, Use a different fix

* Update comment

---------

Co-authored-by: Nuno Campos <nuno@langchain.dev>
2024-07-12 12:53:17 -07:00
Jacob LeeandGitHub 558a513a1a Remove Jupyter cell 'In[<number>]' display column from docs (#1007) 2024-07-12 12:47:07 -07:00
Vadym BardaandGitHub 5a55974f15 docs: update postgres checkpointer (#1001) 2024-07-12 13:40:55 -04:00
Vadym BardaandGitHub f84f9374d3 docs: bring back how-to on passing runtime values to tools (#1003) 2024-07-12 11:52:34 -04:00
Vadym BardaandGitHub f8c87afee1 docs: update streaming from within the tool to use dispatch_custom_event (#1000) 2024-07-11 21:31:55 -04:00
dedbdefd93 [Docs] Added Asynchronous implementation of MongoDB persistence (#983)
---------

Co-authored-by: Vadym Barda <vadim.barda@gmail.com>
Co-authored-by: Vadym Barda <vadym@langchain.dev>
2024-07-11 21:20:19 -04:00
Nuno Campos f13cf5dc2c Update snapshot tests after core release 2024-07-11 17:06:12 -07:00
Nuno Campos 1e2682616f lib0.1.8 2024-07-11 17:03:56 -07:00
Nuno CamposandGitHub 71d9d02d26 lib: Add metadata arg to add_node() (#990)
* lib: Add metadata arg to add_node()

- use metadata when drawing graph
- use metadata for tracing

* Update core

* Lock
2024-07-11 17:03:22 -07:00
Lance MartinandGitHub bd100fdfef Update controllability docs (#995)
* Update controllability docs

* Update controllability docs

* Update docs
2024-07-11 16:10:12 -07:00
Jacob LeeandGitHub 3f4a8e557e Remove broken edit button, view source button (#998)
* Fix edit button, remove view source button

* Update mkdocs.yml

* Update mkdocs.yml
2024-07-11 15:17:23 -07:00
224 changed files with 29495 additions and 16056 deletions
+10 -10
View File
@@ -1,5 +1,5 @@
name: "\U0001F41B Bug Report"
description: Report a bug in LangChain. To report a security issue, please instead use the security option below. For questions, please use the GitHub Discussions.
description: Report a bug in LangGraph. To report a security issue, please instead use the security option below. For questions, please use the GitHub Discussions.
labels: ["02 Bug Report"]
body:
- type: markdown
@@ -15,11 +15,11 @@ body:
Relevant links to check before filing a bug report to see if your issue has already been reported, fixed or
if there's another way to solve your problem:
[LangGraph documentation](https://langchain-ai.github.io/langgraph/).
[LangChain documentation with the integrated search](https://python.langchain.com/docs/get_started/introduction),
[API Reference](https://api.python.langchain.com/en/stable/),
[GitHub search](https://github.com/langchain-ai/langchain),
[LangChain Github Discussions](https://github.com/langchain-ai/langchain/discussions),
[LangChain Github Issues](https://github.com/langchain-ai/langchain/issues?q=is%3Aissue),
[GitHub search](https://github.com/langchain-ai/langgraph),
[LangChain Github Discussions](https://github.com/langchain-ai/langgraph/discussions),
[LangChain Github Issues](https://github.com/langchain-ai/langgraph/issues),
[LangChain ChatBot](https://chat.langchain.com/)
- type: checkboxes
id: checks
@@ -54,13 +54,13 @@ body:
* Avoid screenshots when possible, as they are hard to read and (more importantly) don't allow others to copy-and-paste your code.
placeholder: |
from langchain_core.runnables import RunnableLambda
from langgraph.graph import StateGraph
def bad_code(inputs) -> int:
raise NotImplementedError('For demo purpose')
chain = RunnableLambda(bad_code)
chain.invoke('Hello!')
chain = StateGraph(list)
chain.invoke('Hello!')
render: python
- type: textarea
id: error
@@ -82,7 +82,7 @@ body:
Write a short description telling what you are doing, what you expect to happen, and what is currently happening.
placeholder: |
* I'm trying to use the `langchain` library to do X.
* I'm trying to use the `langgraph` library to do X.
* I expect to see Y.
* Instead, it does Z.
validations:
@@ -102,7 +102,7 @@ body:
python -m langchain_core.sys_info
placeholder: |
"pip freeze | grep langchain"
"pip freeze | grep langgraph"
platform
python version
+6 -6
View File
@@ -3,13 +3,13 @@ version: 2.1
contact_links:
- name: 🤔 Question or Problem
about: Ask a question or ask about a problem in GitHub Discussions.
url: https://www.github.com/langchain-ai/langchain/discussions/categories/q-a
- name: Discord
url: https://discord.gg/6adMQxSpJS
about: General community discussions
url: https://github.com/langchain-ai/langgraph/discussions/categories/q-a
- name: Feature Request
url: https://www.github.com/langchain-ai/langchain/discussions/categories/ideas
url: https://github.com/langchain-ai/langgraph/discussions/categories/ideas
about: Suggest a feature or an idea
- name: Show and tell
about: Show what you built with LangChain
url: https://www.github.com/langchain-ai/langchain/discussions/categories/show-and-tell
url: https://github.com/langchain-ai/langgraph/discussions/categories/show-and-tell
- name: Slack
url: https://www.langchain.com/join-community
about: General community discussions
+1 -1
View File
@@ -1,5 +1,5 @@
name: Documentation
description: Report an issue related to the LangChain documentation.
description: Report an issue related to the LangGraph documentation.
title: "DOC: <Please write a comprehensive title after the 'DOC: ' prefix>"
labels: [03 - Documentation]
+8 -2
View File
@@ -36,7 +36,10 @@
working-directory: [
"libs/langgraph",
"libs/sdk-py",
"libs/cli"
"libs/cli",
"libs/checkpoint",
"libs/checkpoint-sqlite",
"libs/checkpoint-postgres"
]
uses: ./.github/workflows/_lint.yml
with:
@@ -50,7 +53,10 @@
matrix:
working-directory: [
"libs/langgraph",
"libs/cli"
"libs/cli",
"libs/checkpoint",
"libs/checkpoint-sqlite",
"libs/checkpoint-postgres"
]
uses: ./.github/workflows/_test.yml
with:
+12 -7
View File
@@ -6,7 +6,7 @@ on:
working-directory:
required: true
type: string
default: 'libs/langgraph'
default: "libs/langgraph"
env:
PYTHON_VERSION: "3.11"
@@ -104,7 +104,7 @@ jobs:
REGEX="^$SHORT_PKG_NAME==\\d+\\.\\d+\\.\\d+((a|b|rc)\\d+)?\$"
fi
echo $REGEX
PREV_TAG=$(git tag --sort=-creatordate | grep -P $REGEX | head -1)
PREV_TAG=$(git tag --sort=-creatordate | grep -P $REGEX | head -1 || echo "")
echo $PREV_TAG
if [ "$TAG" == "$PREV_TAG" ]; then
echo "No new version to release"
@@ -137,8 +137,7 @@ jobs:
- build
- release-notes
permissions: write-all
uses:
./.github/workflows/_test_release.yml
uses: ./.github/workflows/_test_release.yml
with:
working-directory: ${{ inputs.working-directory }}
secrets: inherit
@@ -198,9 +197,15 @@ jobs:
"$PKG_NAME==$VERSION" \
)
# Replace all dashes in the package name with underscores,
# since that's how Python imports packages with dashes in the name.
IMPORT_NAME="$(echo "$PKG_NAME" | sed s/-/_/g)"
if [[ "$PKG_NAME" == *checkpoint* ]]; then
# since checkpoint packages are namespace packages, import them with . convention
# i.e. import langgraph.checkpoint or langgraph.checkpoint.sqlite
IMPORT_NAME="$(echo "$PKG_NAME" | sed s/-/./g)"
else
# Replace all dashes in the package name with underscores,
# since that's how Python imports packages with dashes in the name.
IMPORT_NAME="$(echo "$PKG_NAME" | sed s/-/_/g)"
fi
poetry run python -c "import $IMPORT_NAME; print(dir($IMPORT_NAME))"
+28
View File
@@ -0,0 +1,28 @@
name: Check File Size
on:
push:
branches:
- main
pull_request:
branches:
- main
workflow_dispatch:
jobs:
file-size-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v44
- name: Filter by size
run: |
large_added_files=$(find ${{ steps.changed-files.outputs.added_files }} -maxdepth 0 -size +1M)
if [ -n "$large_added_files" ]; then
echo "Large files added: $large_added_files"
echo "# Large files added:" >> $GITHUB_STEP_SUMMARY
echo "$large_added_files" >> $GITHUB_STEP_SUMMARY
exit 1
fi
+11 -3
View File
@@ -1,6 +1,14 @@
.PHONY: build-docs serve-docs serve-clean-docs clean-docs codespell
.PHONY: build-docs serve-docs serve-clean-docs clean-docs codespell build-typedoc
build-docs:
build-typedoc:
cd libs/sdk-js && yarn install --include-dev && yarn typedoc
cd libs/sdk-js && yarn --silent concat-md --decrease-title-levels --ignore=js_ts_sdk_ref.md --start-title-level-at 2 docs > ../../docs/docs/cloud/reference/sdk/js_ts_sdk_ref.md 2>/dev/null
# Add links to the monorepo
sed -e '1,10s|@langchain/langgraph-sdk|[@langchain/langgraph-sdk](https://github.com/langchain-ai/langgraph/tree/main/libs/sdk-js)|g' docs/docs/cloud/reference/sdk/js_ts_sdk_ref.md > temp_file && mv temp_file docs/docs/cloud/reference/sdk/js_ts_sdk_ref.md
build-docs: build-typedoc
poetry run python docs/_scripts/copy_notebooks.py
poetry run python -m mkdocs build --clean -f docs/mkdocs.yml --strict
@@ -8,7 +16,7 @@ serve-clean-docs: clean-docs
poetry run python docs/_scripts/copy_notebooks.py
poetry run python -m mkdocs serve -c -f docs/mkdocs.yml --strict -w ./libs/langgraph
serve-docs:
serve-docs: build-typedoc
poetry run python docs/_scripts/copy_notebooks.py
poetry run python -m mkdocs serve -f docs/mkdocs.yml -w ./libs/langgraph --dirty
+6 -8
View File
@@ -3,7 +3,6 @@
![Version](https://img.shields.io/pypi/v/langgraph)
[![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)
[![](https://dcbadge.vercel.app/api/server/6adMQxSpJS?compact=true&style=flat)](https://discord.com/channels/1038097195422978059/1170024642245832774)
[![Docs](https://img.shields.io/badge/docs-latest-blue)](https://langchain-ai.github.io/langgraph/)
⚡ Building language agents as graphs ⚡
@@ -11,9 +10,6 @@
> [!NOTE]
> Looking for the JS version? Click [here](https://github.com/langchain-ai/langgraphjs) ([JS docs](https://langchain-ai.github.io/langgraphjs/)).
> [!TIP]
> Looking to deploy your LangGraph application? [Join the waitlist](https://www.langchain.com/langgraph-cloud-beta) for [LangGraph Cloud](https://langchain-ai.github.io/langgraph/cloud/), our managed service for deploying and hosting LangGraph applications.
## Overview
[LangGraph](https://langchain-ai.github.io/langgraph/) is a library for building stateful, multi-actor applications with LLMs, used to create agent and multi-agent workflows. Compared to other LLM frameworks, it offers these core benefits: cycles, controllability, and persistence. LangGraph allows you to define flows that involve cycles, essential for most agentic architectures, differentiating it from DAG-based solutions. As a very low-level framework, it provides fine-grained control over both the flow and state of your application, crucial for creating reliable agents. Additionally, LangGraph includes built-in persistence, enabling advanced human-in-the-loop and memory features.
@@ -57,10 +53,12 @@ export LANGSMITH_API_KEY=lsv2_sk_...
```
```python
from typing import Annotated, Literal, TypedDict
from langchain_core.messages import HumanMessage
from langchain_anthropic import ChatAnthropic
from langchain_core.tools import tool
from langgraph.checkpoint import MemorySaver
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import END, StateGraph, MessagesState
from langgraph.prebuilt import ToolNode
@@ -71,8 +69,8 @@ def search(query: str):
"""Call to surf the web."""
# This is a placeholder, but don't tell the LLM that...
if "sf" in query.lower() or "san francisco" in query.lower():
return ["It's 60 degrees and foggy."]
return ["It's 90 degrees and sunny."]
return "It's 60 degrees and foggy."
return "It's 90 degrees and sunny."
tools = [search]
@@ -82,7 +80,7 @@ tool_node = ToolNode(tools)
model = ChatAnthropic(model="claude-3-5-sonnet-20240620", temperature=0).bind_tools(tools)
# Define the function that determines whether to continue or not
def should_continue(state: MessagesState) -> Union[Literal["tools"], type(END)]:
def should_continue(state: MessagesState) -> Literal["tools", END]:
messages = state['messages']
last_message = messages[-1]
# If the LLM makes a tool call, then we route to the "tools" node
+2
View File
@@ -1,2 +1,4 @@
*.ipynb
site/
docs/tutorials/**/*.png
docs/cloud/reference/sdk/js_ts_sdk_ref.md
+9 -1
View File
@@ -27,6 +27,8 @@ _MANUAL = {
"streaming-events-from-within-tools-without-langchain.ipynb",
"streaming-from-final-node.ipynb",
"persistence.ipynb",
"input_output_schema.ipynb",
"pass_private_state.ipynb",
"memory/manage-conversation-history.ipynb",
"memory/delete-messages.ipynb",
"memory/add-summary-conversation-history.ipynb",
@@ -38,6 +40,10 @@ _MANUAL = {
"subgraph.ipynb",
"force-calling-a-tool-first.ipynb",
"pass-run-time-values-to-tools.ipynb",
"tool-calling.ipynb",
"tool-calling-errors.ipynb",
"pass-config-to-tools.ipynb",
"many-tools.ipynb",
"dynamic-returning-direct.ipynb",
"managing-agent-steps.ipynb",
"respond-in-format.ipynb",
@@ -53,6 +59,7 @@ _MANUAL = {
"human_in_the_loop/time-travel.ipynb",
"human_in_the_loop/edit-graph-state.ipynb",
"human_in_the_loop/wait-user-input.ipynb",
"node-retries.ipynb",
],
"tutorials": [
"introduction.ipynb",
@@ -88,13 +95,14 @@ _HIDE = set(
"dynamically-returning-directly.ipynb",
"force-calling-a-tool-first.ipynb",
"managing-agent-steps.ipynb",
"pass-run-time-values-to-tools.ipynb",
"respond-in-format.ipynb",
"quickstart.ipynb",
"human-in-the-loop.ipynb",
"learning.ipynb",
"docs/quickstart.ipynb",
"tutorials/rag-agent-testing.ipynb",
"tutorials/rag-agent-testing-local.ipynb",
"tutorials/tool-calling-agent-local.ipynb",
"time-travel.ipynb",
"code_assistant/langgraph_code_assistant_mistral.ipynb",
]
+4
View File
@@ -12,6 +12,10 @@ An assistant is a configured instance of a [`CompiledGraph`][compiledgraph]. It
The LangGraph Cloud API provides several endpoints for creating and managing assistants. See the <a href="../reference/api/api_ref.html#tag/assistantscreate" target="_blank">API reference</a> for more details.
#### Configuring Assistants
You can save custom assistants from the same graph to set different default prompts, models, and other configurations without changing a line of code in your graph. This allows you the ability to quickly test out different configurations without having to rewrite your graph every time, and also give users the flexibility to select different configurations when using your LangGraph application. See <a href="https://langchain-ai.github.io/langgraph/cloud/how-tos/cloud_examples/configuration_cloud/">this</a> how-to for information on how to configure a deployed graph.
### Threads
A thread contains the accumulated state of a group of runs. If a run is executed on a thread, then the [state][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.
+3 -2
View File
@@ -2,9 +2,10 @@
LangGraph Cloud is available within <a href="https://www.langchain.com/langsmith" target="_blank">LangSmith</a>. To deploy a LangGraph Cloud API, navigate to the <a href="https://smith.langchain.com/" target="_blank">LangSmith UI</a>.
## Setup GitHub Repository
## Prerequisites
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. 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](test_locally.md). If the API does not build and run successfully (i.e. `langgraph up`), deploying to LangGraph Cloud will fail as well.
## Create New Deployment
+146
View File
@@ -0,0 +1,146 @@
# Rebuild Graph at Runtime
You might need to rebuild your graph with a different configuration for a new run. For example, you might need to use a different graph state or graph structure depending on the config. This guide shows how you can do this.
!!! note "Note"
In most cases, customizing behavior based on the config should be handled by a single graph where each node can read a config and change its behavior based on it
## Prerequisites
Make sure to check out [this how-to guide](./setup.md) on setting up your app for deployment first.
## Define graphs
Let's say you have an app with a simple graph that calls an LLM and returns the response to the user. The app file directory looks like the following:
```
my-app/
|-- requirements.txt
|-- .env
|-- openai_agent.py # code for your graph
```
where the graph is defined in `openai_agent.py`.
### No rebuild
In the standard LangGraph API configuration, the server uses the compiled graph instance that's defined at the top level of `openai_agent.py`, which looks like the following:
```python
from langchain_openai import ChatOpenAI
from langgraph.graph import END, MessageGraph
model = ChatOpenAI(temperature=0)
graph_workflow = MessageGraph()
graph_workflow.add_node("agent", model)
graph_workflow.add_edge("agent", END)
graph_workflow.set_entry_point("agent")
agent = graph_workflow.compile()
```
To make the server aware of your graph, you need to specify a path to the variable that contains the `CompiledStateGraph` instance in your LangGraph API configuration (`langgraph.json`), e.g.:
```
{
"dependencies": ["."],
"graphs": {
"openai_agent": "./openai_agent.py:agent",
},
"env": "./.env"
}
```
### Rebuild
To make your graph rebuild on each new run with custom configuration, you need to rewrite `openai_agent.py` to instead provide a _function_ that takes a config and returns a graph (or compiled graph) instance. Let's say we want to return our existing graph for user ID '1', and a tool-calling agent for other users. We can modify `openai_agent.py` as follows:
```python
from typing import Annotated, TypedDict
from langchain_openai import ChatOpenAI
from langgraph.graph import END, MessageGraph
from langgraph.graph.state import StateGraph
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode
from langchain_core.tools import tool
from langchain_core.messages import BaseMessage
from langchain_core.runnables import RunnableConfig
class State(TypedDict):
messages: Annotated[list[BaseMessage], add_messages]
model = ChatOpenAI(temperature=0)
def make_default_graph():
"""Make a simple LLM agent"""
graph_workflow = StateGraph(State)
def call_model(state):
return {"messages": [model.invoke(state["messages"])]}
graph_workflow.add_node("agent", call_model)
graph_workflow.add_edge("agent", END)
graph_workflow.set_entry_point("agent")
agent = graph_workflow.compile()
return agent
def make_alternative_graph():
"""Make a tool-calling agent"""
@tool
def add(a: float, b: float):
"""Adds two numbers."""
return a + b
tool_node = ToolNode([add])
model_with_tools = model.bind_tools([add])
def call_model(state):
return {"messages": [model_with_tools.invoke(state["messages"])]}
def should_continue(state: State):
if state["messages"][-1].tool_calls:
return "tools"
else:
return END
graph_workflow = StateGraph(State)
graph_workflow.add_node("agent", call_model)
graph_workflow.add_node("tools", tool_node)
graph_workflow.add_edge("tools", "agent")
graph_workflow.set_entry_point("agent")
graph_workflow.add_conditional_edges("agent", should_continue)
agent = graph_workflow.compile()
return agent
# this is the graph making function that will decide which graph to
# build based on the provided config
def make_graph(config: RunnableConfig):
user_id = config.get("configurable", {}).get("user_id")
# route to different graph state / structure based on the user ID
if user_id == "1":
return make_default_graph()
else:
return make_alternative_graph()
```
Finally, you need to specify the path to your graph-making function (`make_graph`) in `langgraph.json`:
```
{
"dependencies": ["."],
"graphs": {
"openai_agent": "./openai_agent.py:make_graph",
},
"env": "./.env"
}
```
See more info on LangGraph API configuration file [here](../reference/cli.md#configuration-file)
+22 -7
View File
@@ -1,6 +1,9 @@
# How to Set Up a LangGraph Application for Deployment
A LangGraph application must be configured with a [LangGraph API 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. If you prefer using poetry for dependency management, check out [this how-to guide](./setup_pyproject.md) on using `pyproject.toml` for LangGraph Cloud.
A LangGraph application must be configured with a [LangGraph API 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.
!!! 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.
The final repo structure will look something like this:
@@ -19,6 +22,22 @@ After each step, an example file directory is provided to demonstrate how code c
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 API configuration file](#create-langgraph-api-config).
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:
```
langgraph>=0.1.19,<0.2.0
langchain-core>=0.2.8,<0.3.0
langsmith>=0.1.63
orjson>=3.10.1
httpx>=0.27.0
tenacity>=8.3.0
uvicorn>=0.29.0
sse-starlette>=2.1.0
uvloop>=0.19.0
httptools>=0.6.1
jsonschema-rs>=0.18.0
croniter>=1.0.1
```
Example `requirements.txt` file:
```
langgraph
@@ -70,7 +89,7 @@ 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.
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:
```
@@ -115,10 +134,6 @@ my-app/
|-- langgraph.json # configuration file for LangGraph
```
## Upload to GitHub
To deploy the LangGraph application to LangGraph Cloud, the code must be uploaded to a GitHub repository.
## Next
After you setup your repo, it's time to [deploy your app](./cloud.md).
After you setup your project and place it in a github repo, it's time to [deploy your app](./cloud.md).
+22 -7
View File
@@ -20,6 +20,22 @@ After each step, an example file directory is provided to demonstrate how code c
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 API configuration file](#create-langgraph-api-config).
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:
```
langgraph>=0.1.19,<0.2.0
langchain-core>=0.2.8,<0.3.0
langsmith>=0.1.63
orjson>=3.10.1
httpx>=0.27.0
tenacity>=8.3.0
uvicorn>=0.29.0
sse-starlette>=2.1.0
uvloop>=0.19.0
httptools>=0.6.1
jsonschema-rs>=0.18.0
croniter>=1.0.1
```
Example `pyproject.toml` file:
```toml
@@ -33,7 +49,7 @@ readme = "README.md"
[tool.poetry.dependencies]
python = ">=3.9.0,<3.13"
langgraph = "^0.1.0"
langgraph = "^0.1.7"
langchain-fireworks = "^0.1.3"
@@ -102,7 +118,7 @@ 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.
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:
@@ -133,6 +149,9 @@ Example `langgraph.json` file:
Note that the variable name of the `CompiledGraph` appears at the end of the value of each subkey in the top-level `graphs` key (i.e. `:<variable_name>`).
!!! warning "Configuration Location"
The LangGraph API configuration file must be placed in a directory that is at the same level or higher than the Python files that contain compiled graphs and associated dependencies.
Example file directory:
```bash
@@ -145,10 +164,6 @@ my-app/
└── pyproject.toml
```
## Upload to GitHub
To deploy the LangGraph application to LangGraph Cloud, the code must be uploaded to a GitHub repository.
## Next
After you setup your repo, it's time to [deploy your app](./cloud.md).
After you setup your project and place it in a github repo, it's time to [deploy your app](./cloud.md).
@@ -2,6 +2,8 @@
This guide assumes you have a LangGraph app correctly set up with a proper configuration file and a corresponding compiled graph, and that you have a proper LangChain API key.
Testing locally ensures that there are no errors or conflicts with Python dependencies and confirms that the configuration file is specified correctly.
## Setup
Install the proper packages:
@@ -10,6 +12,12 @@ Install the proper packages:
pip install langgraph-cli
```
Ensure you have an API key, which you can create from the LangSmith UI (Settings > API Keys). This is required to authenticate that you have LangGraph Cloud access. After you have saved the key to a safe place, place the following line in your `.env` file:
```python
LANGCHAIN_API_KEY = *********
```
## Start the API server
Once you have downloaded the CLI, you can run the following command to start the API server for local testing:
Binary file not shown.

After

Width:  |  Height:  |  Size: 322 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 MiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 721 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 MiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 MiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 MiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

+5 -1
View File
@@ -11,10 +11,13 @@ Welcome to the LangGraph Cloud how-to guides! These guides provide practical, st
LangGraph Cloud gives you best in class observability, testing, and hosting services. Read more about them in these how to guides:
- [How to set up app for deployment](../deployment/setup.md)
- [How to set up app for deployment (requirements.txt)](../deployment/setup.md)
- [How to set up app for deployment (pyproject.toml)](../deployment/setup_pyproject.md)
- [How to test locally](../deployment/test_locally.md)
- [How to deploy to LangGraph cloud](../deployment/cloud.md)
- [How to self-host](../deployment/self_hosted.md)
## Streaming
Streaming the results of your LLM application is vital for ensuring a good user experience, especially when your graph may call multiple models and take a long time to fully complete a run. Read about how to stream values from your graph in these how to guides:
@@ -49,6 +52,7 @@ When creating complex graphs, leaving every decision up to the LLM can be danger
LangGraph Studio is a built-in UI for visualizing, testing, and debugging your agents.
- [How to enter LangGraph Studio](./test_deployment.md)
- [How to enter LangGraph Studio for local deployment](./test_local_deployment.md)
- [How to test your graph in LangGraph Studio](./invoke_studio.md)
- [Interact with threads in LangGraph Studio](./threads_studio.md)
+6 -4
View File
@@ -1,13 +1,15 @@
# Invoke Assistant
The LangGraph Studio lets you test different configurations and inputs to your graph. The UI allows you to see exactly how your
The LangGraph Studio lets you test different configurations and inputs to your graph. It also provides a nice visualization of your graph during execution so it is easy to see which nodes are being run and what the outputs of each individual node are.
1. The LangGraph Studio UI displays a visualization of the selected assistant.
1. In the top-right dropdown menu of the left-hand pane, select an assistant.
1. In the top-left dropdown menu of the left-hand pane, select an assistant.
1. In the bottom of the left-hand pane, edit the `Input` and `Configure` the assistant.
1. Select `Submit` to invoke the selected assistant.
1. View output of the invocation in the right-hand pane.
The following GIF shows these exact steps being carried out:
The following video shows these exact steps being carried out:
![Using LangGraph Studio](./img/studio_input.gif)
<video controls allowfullscreen="true" poster="../img/studio_input_poster.png">
<source src="../img/studio_input.mp4" type="video/mp4">
</video>
+4 -2
View File
@@ -9,6 +9,8 @@ Starting from the <a href="https://smith.langchain.com/" target="_blank">LangSmi
1. In the top-right corner, select `Open LangGraph Studio`.
1. [Invoke an assistant](./invoke_studio.md) or [view an existing thread](./threads_studio.md).
The following GIF shows these exact steps being carried out:
The following video shows these exact steps being carried out:
![Using LangGraph Studio](./img/studio_usage.gif)
<video controls allowfullscreen="true" poster="../img/studio_usage_poster.png">
<source src="../img/studio_usage.mp4" type="video/mp4">
</video>
@@ -0,0 +1,28 @@
# LangGraph Studio With Local Deployment
!!! warning "Browser Compatibility"
Viewing the studio page of a local LangGraph deployment does not work in Safari. Use Chrome instead.
## Setup
Make sure you have setup your app correctly, by creating a compiled graph, a `.env` file with any environment variables, and a `langgraph.json` config file that points to your environment file and compiled graph. See [here](https://langchain-ai.github.io/langgraph/cloud/deployment/setup/) for more detailed instructions.
After you have your app setup, head into the directory with your `langgraph.json` file and call `langgraph up -c langgraph.json --watch` to start the API server in watch mode which means it will restart on code changes, which is ideal for local testing. If the API server start correctly you should see logs that look something like this:
Ready!
- API: http://localhost:8123
2024-06-26 19:20:41,056:INFO:uvicorn.access 127.0.0.1:44138 - "GET /ok HTTP/1.1" 200
Read this [reference](https://langchain-ai.github.io/langgraph/cloud/reference/cli/#up) to learn about all the options for starting the API server.
## Access Studio
Once you have successfully started the API server, you can access the studio by going to the following URL: `https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:8123` (see warning above if using Safari).
If everything is working correctly you should see the studio show up looking something like this (with your graph diagram on the left hand side):
![LangGraph Studio](./img/studio_screenshot.png)
## Use the Studio for Testing
To learn about how to use the studio for testing, read the [LangGraph Studio how-tos](https://langchain-ai.github.io/langgraph/cloud/how-tos/#langgraph-studio).
+8 -4
View File
@@ -6,14 +6,18 @@
1. View the state of the thread (i.e. the output) in the right-hand pane.
1. To create a new thread, select `+ New Thread`.
The following GIF shows these exact steps being carried out:
The following video shows these exact steps being carried out:
![Using LangGraph Studio](./img/studio_threads.gif)
<video controls="true" allowfullscreen="true" poster="../img/studio_threads_poster.png">
<source src="../img/studio_threads.mp4" type="video/mp4">
</video>
## Edit Thread State
The LangGraph Studio UI contains features for editing thread state. Explore these features in the right-hand pane. Select the `Edit` icon, modify the desired state, and then select `Fork` to invoke the assistant with the updated state.
The following GIF shows how to edit a thread in the studio:
The following video shows how to edit a thread in the studio:
![Using LangGraph Studio](./img/studio_forks.gif)
<video controls allowfullscreen="true" poster="../img/studio_forks_poster.png">
<source src="../img/studio_forks.mp4" type="video/mp4">
</video>
+10 -3
View File
@@ -1,12 +1,19 @@
# LangGraph Cloud (beta)
!!! danger "Important"
LangGraph Cloud is a closed source, paid product in an invite-only stage. We are currently focused on providing high bandwidth support to make our select early customers successful. If you are interested in applying for access, please fill out [this form](https://www.langchain.com/langgraph-cloud-beta).
!!! tip
- LangGraph is an MIT-licensed open-source library, which we are committed to maintaining and growing for the community.
- LangGraph Cloud is an optional managed hosting service for LangGraph, which provides additional features geared towards production deployments.
- We are actively contributing improvements back to LangGraph informed by our work on LangGraph Cloud.
- You can always deploy LangGraph applications on your own infrastructure using the open-source LangGraph project.
!!! warning "Under Construction"
LangGraph Cloud documentation is under construction. Contents may change until general availability.
![GIF](./how-tos/img/studio_input.gif)
<video controls preload="auto" allowfullscreen="true" poster="how-tos/img/studio_forks_poster.png">
<source src="how-tos/img/studio_forks.mp4" type="video/mp4">
</video>
## Overview
+2 -2
View File
@@ -13,7 +13,7 @@ The LangGraph CLI requires a JSON configuration file with the following keys:
| Key | Description |
| --- | ----------- |
| `dependencies` | **Required**. Array of dependencies for LangGraph Cloud API server. Dependencies can be one of the following: (1) `"."`, which will look for local Python packages, (2) `pyproject.toml`, `setup.py` or `requirements.txt` in the app directory `"./local_package"`, or (3) a package name. |
| `graphs` | **Required**. Mapping from graph ID to path where the compiled graph is defined. Example: `./your_package/your_file.py:variable`, where `variable` is an instance of `langgraph.graph.graph.CompiledGraph`. |
| `graphs` | **Required**. Mapping from graph ID to path where the compiled graph or a function that makes a graph is defined. Example: <ul><li>`./your_package/your_file.py:variable`, where `variable` is an instance of `langgraph.graph.state.CompiledStateGraph`</li><li>`./your_package/your_file.py:make_graph`, where `make_graph` is a function that takes a config dictionary (`langchain_core.runnables.RunnableConfig`) and creates an instance of `langgraph.graph.state.StateGraph` / `langgraph.graph.state.CompiledStateGraph`.</li></ul> |
| `env` | Path to `.env` file or a mapping from environment variable to its value. |
| `python_version` | `3.11` or `3.12`. Defaults to `3.11`. |
| `pip_config_file`| Path to `pip` config file. |
@@ -49,7 +49,7 @@ Example:
"."
],
"graphs": {
"my_graph_id": "./your_package/your_file.py:variable"
"my_graph_id": "./your_package/your_file.py:make_graph"
},
"env": {
"OPENAI_API_KEY": "secret-key"
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -20,7 +20,7 @@ Low Level Concepts
- [State](low_level.md#state)
- [Schema](low_level.md#schema)
- [Reducers](low_level.md#reducers)
- [MessageState](low_level.md#messagestate)
- [MessageState](low_level.md#working-with-messages-in-graph-state)
- [Nodes](low_level.md#nodes)
- [`START` node](low_level.md#start-node)
- [`END` node](low_level.md#end-node)
+43 -7
View File
@@ -46,6 +46,10 @@ The first thing you do when you define a graph is define the `State` of the grap
The main documented way to specify the schema of a graph is by using `TypedDict`. However, we also support [using a Pydantic BaseModel](../how-tos/state-model.ipynb) as your graph state to add **default values** and additional data validation.
By default, the graph will have the same input and output schemas. If you want to change this, you can also specify explicit input and output schemas directly. This is useful when you have a lot of keys, and some are explicitly for input and others for output. See the [notebook here](../how-tos/input_output_schema.ipynb) for how to use.
By default, all nodes in the graph will share the same state. This means that they will read and write to the same state channels. It is possible to have nodes write to private state channels inside the graph for internal node communication - see [this notebook](../how-tos/pass_private_state.ipynb) for how to do that.
### Reducers
Reducers are key to understanding how updates from nodes are applied to the `State`. Each key in the `State` has its own independent reducer function. If no reducer function is explicitly specified then it is assumed that all updates to that key should override it. Let's take a look at a few examples to understand them better.
@@ -75,22 +79,44 @@ class State(TypedDict):
In this example, we've used the `Annotated` type to specify a reducer function (`operator.add`) for the second key (`bar`). Note that the first key remains unchanged. Let's assume the input to the graph is `{"foo": 1, "bar": ["hi"]}`. Let's then assume the first `Node` returns `{"foo": 2}`. This is treated as an update to the state. Notice that the `Node` does not need to return the whole `State` schema - just an update. After applying this update, the `State` would then be `{"foo": 2, "bar": ["hi"]}`. If the second node returns `{"bar": ["bye"]}` then the `State` would then be `{"foo": 2, "bar": ["hi", "bye"]}`. Notice here that the `bar` key is updated by adding the two lists together.
### MessageState
### Working with Messages in Graph State
`MessageState` is one of the few opinionated components in LangGraph. `MessageState` is a special state designed to make it easy to use a list of messages as a key in your state. Specifically, `MessageState` is defined as:
#### Why use messages?
Most modern LLM providers have a chat model interface that accepts a list of messages as input. LangChain's [`ChatModel`](https://python.langchain.com/v0.2/docs/concepts/#chat-models) in particular accepts a list of `Message` objects as inputs. These messages come in a variety of forms such as `HumanMessage` (user input) or `AIMessage` (LLM response). To read more about what message objects are, please refer to [this](https://python.langchain.com/v0.2/docs/concepts/#messages) conceptual guide.
#### Using Messages in your Graph
In many cases, it is helpful to store prior conversation history as a list of messages in your graph state. To do so, we can add a key (channel) to the graph state that stores a list of `Message` objects and annotate it with a reducer function (see `messages` key in the example below). The reducer function is vital to telling the graph how to update the list of `Message` objects in the state with each state update (for example, when a node sends an update). If you don't specify a reducer, every state update will overwrite the list of messages with the most recently provided value. If you wanted to simply append messages to the existing list, you could use `operator.add` as a reducer.
However, you might also want to manually update messages in your graph state (e.g. human-in-the-loop). If you were to use `operator.add`, the manual state updates you send to the graph would be appended to the existing list of messages, instead of updating existing messages. To avoid that, you need a reducer that can keep track of message IDs and overwrite existing messages, if updated. To achieve this, you can use the prebuilt `add_messages` function. For brand new messages, it will simply append to existing list, but it will also handle the updates for existing messages correctly.
#### Serialization
In addition to keeping track of message IDs, the `add_messages` function will also try to deserialize messages into LangChain `Message` objects whenever a state update is received on the `messages` channel. See more information on LangChain serialization/deserialization [here](https://python.langchain.com/v0.2/docs/how_to/serialization/). This allows sending graph inputs / state updates in the following format:
```python
# this is supported
{"messages": [HumanMessage(content="message")]}
# and this is also supported
{"messages": [{"type": "human", "content": "message"}]}
```
Since the state updates are always deserialized into LangChain `Messages` when using `add_messages`, you should use dot notation to access message attributes, like `state["messages"][-1].content`. Below is an example of a graph that uses `add_messages` as it's reducer function.
```python
from langchain_core.messages import AnyMessage
from langgraph.graph.message import add_messages
from typing import Annotated, TypedDict
class MessagesState(TypedDict):
class GraphState(TypedDict):
messages: Annotated[list[AnyMessage], add_messages]
```
What this is doing is creating a `TypedDict` with a single key: `messages`. This is a list of `Message` objects, with `add_messages` as a reducer. `add_messages` basically adds messages to the existing list (it also does some nice extra things, like convert from OpenAI message format to the standard LangChain message format, handle updates based on message IDs, etc).
#### MessagesState
We often see a list of messages being a key component of state, so this prebuilt state is intended to make it easy to use messages. Typically, there is more state to track than just messages, so we see people subclass this state and add more fields, like:
Since having a list of messages in your state is so common, there exists a prebuilt state called `MessagesState` which makes it easy to use messages. `MessagesState` is defined with a single `messages` key which is a list of `AnyMessage` objects and uses the `add_messages` reducer. Typically, there is more state to track than just messages, so we see people subclass this state and add more fields, like:
```python
from langgraph.graph import MessagesState
@@ -180,7 +206,7 @@ graph.add_edge("node_a", "node_b")
If you want to **optionally** route to 1 or more edges (or optionally terminate), you can use the [add_conditional_edges][langgraph.graph.StateGraph.add_conditional_edges] method. This method accepts the name of a node and a "routing function" to call after that node is executed:
```python
graph.add_edge("node_a", routing_function)
graph.add_conditional_edges("node_a", routing_function)
```
Similar to nodes, the `routing_function` accept the current `state` of the graph and return a value.
@@ -190,7 +216,7 @@ By default, the return value `routing_function` is used as the name of the node
You can optionally provide a dictionary that maps the `routing_function`'s output to the name of the next node.
```python
graph.add_edge("node_a", routing_function, {True: "node_b", False: "node_c"})
graph.add_conditional_edges("node_a", routing_function, {True: "node_b", False: "node_c"})
```
### Entry Point
@@ -327,6 +353,16 @@ The final thing you specify when calling `update_state` is `as_node`. This updat
The reason this matters is that the next steps in the graph to execute depend on the last node to have given an update, so this can be used to control which node executes next.
## Graph Migrations
LangGraph can easily handle migrations of graph definitions (nodes, edges, and state) even when using a checkpointer to track state.
- For threads at the end of the graph (i.e. not interrupted) you can change the entire topology of the graph (i.e. all nodes and edges, remove, add, rename, etc)
- For threads currently interrupted, we support all topology changes other than renaming / removing nodes (as that thread could now be about to enter a node that no longer exists) -- if this is a blocker please reach out and we can prioritize a solution.
- For modifying state, we have full backwards and forwards compatibility for adding and removing keys
- State keys that are renamed lose their saved state in existing threads
- State keys whose types change in incompatible ways could currently cause issues in threads with state from before the change -- if this is a blocker please reach out and we can prioritize a solution.
## Configuration
When creating a graph, you can also mark that certain parts of the graph are configurable. This is commonly done to enable easily switching between models or system prompts. This allows you to create a single "cognitive architecture" (the graph) but have multiple different instance of it.
+17
View File
@@ -54,12 +54,29 @@ These guides show how to use different streaming modes.
- [How to stream events from within a tool without LangChain models](streaming-events-from-within-tools-without-langchain.ipynb)
- [How to stream events from the final node](streaming-from-final-node.ipynb)
## Tool calling
- [How to call tools using ToolNode](tool-calling.ipynb)
- [How to handle tool calling errors](tool-calling-errors.ipynb)
- [How to pass graph state to tools](pass-run-time-values-to-tools.ipynb)
- [How to pass config to tools](pass-config-to-tools.ipynb)
- [How to handle large numbers of tools](many-tools.ipynb)
## State Management
- [Use Pydantic model as state](state-model.ipynb)
- [Use a context object in state](state-context-key.ipynb)
- [Have a separate input and output schema](input_output_schema.ipynb)
- [Pass private state between nodes inside the graph](pass_private_state.ipynb)
## Other
- [How to run graph asynchronously](async.ipynb)
- [How to visualize your graph](visualization.ipynb)
- [How to add runtime configuration to your graph](configuration.ipynb)
- [How to use a Pydantic model as your state](state-model.ipynb)
- [How to use a context object in state](state-context-key.ipynb)
- [How to add node retries](node-retries.ipynb)
## Prebuilt ReAct Agent
+9 -7
View File
@@ -7,19 +7,23 @@ You can [compile][langgraph.graph.MessageGraph.compile] any LangGraph workflow w
- Resilience for long-running, error-prone agents
- Time travel retry and branch from a previous checkpoint
Key checkpointer interfaces and primitives are defined in [`langgraph_checkpoint`](https://github.com/langchain-ai/langgraph/tree/main/libs/checkpoint) library.
### Checkpoint
::: langgraph.checkpoint.Checkpoint
::: langgraph.checkpoint.base.Checkpoint
### CheckpointMetadata
::: langgraph.checkpoint.base.CheckpointMetadata
### BaseCheckpointSaver
::: langgraph.checkpoint.base.BaseCheckpointSaver
handler: python
### SerializerProtocol
::: langgraph.checkpoint.SerializerProtocol
handler: python
::: langgraph.checkpoint.base.SerializerProtocol
## Implementations
@@ -28,12 +32,10 @@ LangGraph also natively provides the following checkpoint implementations.
### MemorySaver
::: langgraph.checkpoint.memory.MemorySaver
handler: python
### AsyncSqliteSaver
::: langgraph.checkpoint.aiosqlite.AsyncSqliteSaver
handler: python
::: langgraph.checkpoint.sqlite.aio.AsyncSqliteSaver
### SqliteSaver
+5 -1
View File
@@ -65,4 +65,8 @@ builder.add_conditional_edges("my_node", my_condition)
## Send
::: langgraph.constants.Send
::: langgraph.constants.Send
## RetryPolicy
::: langgraph.pregel.types.RetryPolicy
+10 -1
View File
@@ -55,4 +55,13 @@ from langgraph.prebuilt import tools_condition
from langgraph.prebuilt import ValidationNode
```
::: langgraph.prebuilt.ValidationNode
::: langgraph.prebuilt.ValidationNode
## InjectedState
```python
from langgraph.prebuilt import InjectedState
```
::: langgraph.prebuilt.InjectedState
handler: python
+16 -5
View File
@@ -12,8 +12,6 @@ theme:
repo: fontawesome/brands/git-alt
features:
- announce.dismiss
- content.action.edit
- content.action.view
- content.code.annotate
- content.code.copy
- content.code.select
@@ -154,12 +152,22 @@ nav:
- Stream events from within tools: how-tos/streaming-events-from-within-tools.ipynb
- Stream events from within tools without LangChain models: how-tos/streaming-events-from-within-tools-without-langchain.ipynb
- Stream events from the final node: how-tos/streaming-from-final-node.ipynb
- Tool calling:
- Call tools using ToolNode: how-tos/tool-calling.ipynb
- Handle tool calling errors: how-tos/tool-calling-errors.ipynb
- Pass graph state to tools: how-tos/pass-run-time-values-to-tools.ipynb
- Pass config to tools: how-tos/pass-config-to-tools.ipynb
- Handle many tools: how-tos/many-tools.ipynb
- State Management:
- Use Pydantic model as state: how-tos/state-model.ipynb
- Use a context object in state: how-tos/state-context-key.ipynb
- Have a separate input and output schema: how-tos/input_output_schema.ipynb
- Pass private state between nodes inside the graph: how-tos/pass_private_state.ipynb
- Other:
- Run graph asynchronously: how-tos/async.ipynb
- Visualize your graph: how-tos/visualization.ipynb
- Add runtime configuration: how-tos/configuration.ipynb
- Use Pydantic model as state: how-tos/state-model.ipynb
- Use a context object in state: how-tos/state-context-key.ipynb
- Add node retries: how-tos/node-retries.ipynb
- Prebuilt ReAct Agent:
- Create a ReAct agent: how-tos/create-react-agent.ipynb
- Add memory to a ReAct agent: how-tos/create-react-agent-memory.ipynb
@@ -182,10 +190,12 @@ nav:
- Quick Start: "cloud/quick_start.md"
- How-to Guides:
- "cloud/how-tos/index.md"
- Deployment:
- Setup:
- Setup App: "cloud/deployment/setup.md"
- Setup App (pyproject.toml): "cloud/deployment/setup_pyproject.md"
- Rebuild Graph at Runtime: "cloud/deployment/graph_rebuild.md"
- Test App Locally: "cloud/deployment/test_locally.md"
- Deployment:
- Deploy to Cloud: "cloud/deployment/cloud.md"
- Self-Host: "cloud/deployment/self_hosted.md"
- Streaming:
@@ -207,6 +217,7 @@ nav:
- Replay and Branch from Prior States: "cloud/how-tos/human_in_the_loop_time_travel.md"
- LangGraph Studio:
- Test Cloud Deployment: "cloud/how-tos/test_deployment.md"
- Test Local Deployment: "cloud/how-tos/test_local_deployment.md"
- Invoke graph in LangGraph Studio: "cloud/how-tos/invoke_studio.md"
- Interact with threads in LangGraph Studio: "cloud/how-tos/threads_studio.md"
- Different Types of Runs:
+7
View File
@@ -146,6 +146,13 @@
display: none !important;
}
.jupyter-wrapper .jp-CodeCell .jp-Cell-inputWrapper .jp-InputPrompt.jp-InputArea-prompt {
display: none !important;
}
.jupyter-wrapper .jp-Notebook .jp-Cell .jp-OutputPrompt {
display: none !important;
}
</style>
{% endblock %}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

After

Width:  |  Height:  |  Size: 56 KiB

+24 -262
View File
@@ -32,10 +32,7 @@
"scrolled": true
},
"outputs": [],
"source": [
"%%capture --no-stderr\n",
"%pip install -U langgraph langchain-community langchain-openai scikit-learn"
]
"source": ["%%capture --no-stderr\n%pip install -U langgraph langchain-community langchain-openai scikit-learn"]
},
{
"cell_type": "markdown",
@@ -51,15 +48,7 @@
"id": "3d1ef253-6b0c-4481-868c-e1fe84f2c8ff",
"metadata": {},
"outputs": [],
"source": [
"import requests\n",
"\n",
"url = \"https://storage.googleapis.com/benchmarks-artifacts/chinook/Chinook.db\"\n",
"response = requests.get(url)\n",
"\n",
"with open(\"Chinook.db\", \"wb\") as file:\n",
" file.write(response.content)"
]
"source": ["import requests\n\nurl = \"https://storage.googleapis.com/benchmarks-artifacts/chinook/Chinook.db\"\nresponse = requests.get(url)\n\nwith open(\"Chinook.db\", \"wb\") as file:\n file.write(response.content)"]
},
{
"cell_type": "code",
@@ -88,12 +77,7 @@
"output_type": "execute_result"
}
],
"source": [
"from langchain_community.utilities import SQLDatabase\n",
"\n",
"db = SQLDatabase.from_uri(\"sqlite:///Chinook.db\")\n",
"db.get_usable_table_names()"
]
"source": ["from langchain_community.utilities import SQLDatabase\n\ndb = SQLDatabase.from_uri(\"sqlite:///Chinook.db\")\ndb.get_usable_table_names()"]
},
{
"cell_type": "markdown",
@@ -112,11 +96,7 @@
"id": "d9ea4e80-30e6-4d46-b480-35f0be2fb055",
"metadata": {},
"outputs": [],
"source": [
"from langchain_openai import ChatOpenAI\n",
"\n",
"model = ChatOpenAI(temperature=0, streaming=True, model=\"gpt-4o\")"
]
"source": ["from langchain_openai import ChatOpenAI\n\nmodel = ChatOpenAI(temperature=0, streaming=True, model=\"gpt-4o\")"]
},
{
"cell_type": "markdown",
@@ -138,9 +118,7 @@
"id": "ea958e9f-ab1f-49b5-bd85-16332055297c",
"metadata": {},
"outputs": [],
"source": [
"from langchain_core.messages import HumanMessage, SystemMessage"
]
"source": ["from langchain_core.messages import HumanMessage, SystemMessage"]
},
{
"cell_type": "markdown",
@@ -159,12 +137,7 @@
"id": "975b039a",
"metadata": {},
"outputs": [],
"source": [
"# This tool is given to the agent to look up information about a customer\n",
"def get_customer_info(customer_id: int):\n",
" \"\"\"Look up customer info given their ID. ALWAYS make sure you have the customer ID before invoking this.\"\"\"\n",
" return db.run(f\"SELECT * FROM Customer WHERE CustomerID = {customer_id};\")"
]
"source": ["# This tool is given to the agent to look up information about a customer\ndef get_customer_info(customer_id: int):\n \"\"\"Look up customer info given their ID. ALWAYS make sure you have the customer ID before invoking this.\"\"\"\n return db.run(f\"SELECT * FROM Customer WHERE CustomerID = {customer_id};\")"]
},
{
"cell_type": "code",
@@ -172,20 +145,7 @@
"id": "1d5fa446",
"metadata": {},
"outputs": [],
"source": [
"customer_prompt = \"\"\"Your job is to help a user update their profile.\n",
"\n",
"You only have certain tools you can use. These tools require specific input. If you don't know the required input, then ask the user for it.\n",
"\n",
"If you are unable to help the user, you can \"\"\"\n",
"\n",
"\n",
"def get_customer_messages(messages):\n",
" return [SystemMessage(content=customer_prompt)] + messages\n",
"\n",
"\n",
"customer_chain = get_customer_messages | model.bind_tools([get_customer_info])"
]
"source": ["customer_prompt = \"\"\"Your job is to help a user update their profile.\n\nYou only have certain tools you can use. These tools require specific input. If you don't know the required input, then ask the user for it.\n\nIf you are unable to help the user, you can \"\"\"\n\n\ndef get_customer_messages(messages):\n return [SystemMessage(content=customer_prompt)] + messages\n\n\ncustomer_chain = get_customer_messages | model.bind_tools([get_customer_info])"]
},
{
"cell_type": "markdown",
@@ -206,19 +166,7 @@
"id": "a8604a3b-b484-4b2b-a914-4236cb98c524",
"metadata": {},
"outputs": [],
"source": [
"from langchain_community.vectorstores import SKLearnVectorStore\n",
"from langchain_openai import OpenAIEmbeddings\n",
"\n",
"artists = db._execute(\"select * from Artist\")\n",
"songs = db._execute(\"select * from Track\")\n",
"artist_retriever = SKLearnVectorStore.from_texts(\n",
" [a[\"Name\"] for a in artists], OpenAIEmbeddings(), metadatas=artists\n",
").as_retriever()\n",
"song_retriever = SKLearnVectorStore.from_texts(\n",
" [a[\"Name\"] for a in songs], OpenAIEmbeddings(), metadatas=songs\n",
").as_retriever()"
]
"source": ["from langchain_community.vectorstores import SKLearnVectorStore\nfrom langchain_openai import OpenAIEmbeddings\n\nartists = db._execute(\"select * from Artist\")\nsongs = db._execute(\"select * from Track\")\nartist_retriever = SKLearnVectorStore.from_texts(\n [a[\"Name\"] for a in artists], OpenAIEmbeddings(), metadatas=artists\n).as_retriever()\nsong_retriever = SKLearnVectorStore.from_texts(\n [a[\"Name\"] for a in songs], OpenAIEmbeddings(), metadatas=songs\n).as_retriever()"]
},
{
"cell_type": "markdown",
@@ -234,16 +182,7 @@
"id": "0a2a2b74",
"metadata": {},
"outputs": [],
"source": [
"def get_albums_by_artist(artist):\n",
" \"\"\"Get albums by an artist (or similar artists).\"\"\"\n",
" docs = artist_retriever.get_relevant_documents(artist)\n",
" artist_ids = \", \".join([str(d.metadata[\"ArtistId\"]) for d in docs])\n",
" return db.run(\n",
" f\"SELECT Title, Name FROM Album LEFT JOIN Artist ON Album.ArtistId = Artist.ArtistId WHERE Album.ArtistId in ({artist_ids});\",\n",
" include_columns=True,\n",
" )"
]
"source": ["def get_albums_by_artist(artist):\n \"\"\"Get albums by an artist (or similar artists).\"\"\"\n docs = artist_retriever.get_relevant_documents(artist)\n artist_ids = \", \".join([str(d.metadata[\"ArtistId\"]) for d in docs])\n return db.run(\n f\"SELECT Title, Name FROM Album LEFT JOIN Artist ON Album.ArtistId = Artist.ArtistId WHERE Album.ArtistId in ({artist_ids});\",\n include_columns=True,\n )"]
},
{
"cell_type": "markdown",
@@ -259,16 +198,7 @@
"id": "da533f50",
"metadata": {},
"outputs": [],
"source": [
"def get_tracks_by_artist(artist):\n",
" \"\"\"Get songs by an artist (or similar artists).\"\"\"\n",
" docs = artist_retriever.invoke(artist)\n",
" artist_ids = \", \".join([str(d.metadata[\"ArtistId\"]) for d in docs])\n",
" return db.run(\n",
" f\"SELECT Track.Name as SongName, Artist.Name as ArtistName FROM Album LEFT JOIN Artist ON Album.ArtistId = Artist.ArtistId LEFT JOIN Track ON Track.AlbumId = Album.AlbumId WHERE Album.ArtistId in ({artist_ids});\",\n",
" include_columns=True,\n",
" )"
]
"source": ["def get_tracks_by_artist(artist):\n \"\"\"Get songs by an artist (or similar artists).\"\"\"\n docs = artist_retriever.invoke(artist)\n artist_ids = \", \".join([str(d.metadata[\"ArtistId\"]) for d in docs])\n return db.run(\n f\"SELECT Track.Name as SongName, Artist.Name as ArtistName FROM Album LEFT JOIN Artist ON Album.ArtistId = Artist.ArtistId LEFT JOIN Track ON Track.AlbumId = Album.AlbumId WHERE Album.ArtistId in ({artist_ids});\",\n include_columns=True,\n )"]
},
{
"cell_type": "markdown",
@@ -284,11 +214,7 @@
"id": "b3c07010",
"metadata": {},
"outputs": [],
"source": [
"def check_for_songs(song_title):\n",
" \"\"\"Check if a song exists by its name.\"\"\"\n",
" return song_retriever.invoke(song_title)"
]
"source": ["def check_for_songs(song_title):\n \"\"\"Check if a song exists by its name.\"\"\"\n return song_retriever.invoke(song_title)"]
},
{
"cell_type": "markdown",
@@ -304,23 +230,7 @@
"id": "72a14d5c",
"metadata": {},
"outputs": [],
"source": [
"song_system_message = \"\"\"Your job is to help a customer find any songs they are looking for. \n",
"\n",
"You only have certain tools you can use. If a customer asks you to look something up that you don't know how, politely tell them what you can help with.\n",
"\n",
"When looking up artists and songs, sometimes the artist/song will not be found. In that case, the tools will return information \\\n",
"on similar songs and artists. This is intentional, it is not the tool messing up.\"\"\"\n",
"\n",
"\n",
"def get_song_messages(messages):\n",
" return [SystemMessage(content=song_system_message)] + messages\n",
"\n",
"\n",
"song_recc_chain = get_song_messages | model.bind_tools(\n",
" [get_albums_by_artist, get_tracks_by_artist, check_for_songs]\n",
")"
]
"source": ["song_system_message = \"\"\"Your job is to help a customer find any songs they are looking for. \n\nYou only have certain tools you can use. If a customer asks you to look something up that you don't know how, politely tell them what you can help with.\n\nWhen looking up artists and songs, sometimes the artist/song will not be found. In that case, the tools will return information \\\non similar songs and artists. This is intentional, it is not the tool messing up.\"\"\"\n\n\ndef get_song_messages(messages):\n return [SystemMessage(content=song_system_message)] + messages\n\n\nsong_recc_chain = get_song_messages | model.bind_tools(\n [get_albums_by_artist, get_tracks_by_artist, check_for_songs]\n)"]
},
{
"cell_type": "code",
@@ -339,10 +249,7 @@
"output_type": "execute_result"
}
],
"source": [
"msgs = [HumanMessage(content=\"hi! can you help me find songs by amy whinehouse?\")]\n",
"song_recc_chain.invoke(msgs)"
]
"source": ["msgs = [HumanMessage(content=\"hi! can you help me find songs by amy whinehouse?\")]\nsong_recc_chain.invoke(msgs)"]
},
{
"cell_type": "markdown",
@@ -360,32 +267,7 @@
"id": "73e74268",
"metadata": {},
"outputs": [],
"source": [
"from langchain_core.messages import AIMessage, HumanMessage, SystemMessage\n",
"from langchain_core.pydantic_v1 import BaseModel, Field\n",
"\n",
"\n",
"class Router(BaseModel):\n",
" \"\"\"Call this if you are able to route the user to the appropriate representative.\"\"\"\n",
"\n",
" choice: str = Field(description=\"should be one of: music, customer\")\n",
"\n",
"\n",
"system_message = \"\"\"Your job is to help as a customer service representative for a music store.\n",
"\n",
"You should interact politely with customers to try to figure out how you can help. You can help in a few ways:\n",
"\n",
"- Updating user information: if a customer wants to update the information in the user database. Call the router with `customer`\n",
"- Recommending music: if a customer wants to find some music or information about music. Call the router with `music`\n",
"\n",
"If the user is asking or wants to ask about updating or accessing their information, send them to that route.\n",
"If the user is asking or wants to ask about music, send them to that route.\n",
"Otherwise, respond.\"\"\"\n",
"\n",
"\n",
"def get_messages(messages):\n",
" return [SystemMessage(content=system_message)] + messages"
]
"source": ["from langchain_core.messages import AIMessage, HumanMessage, SystemMessage\nfrom langchain_core.pydantic_v1 import BaseModel, Field\n\n\nclass Router(BaseModel):\n \"\"\"Call this if you are able to route the user to the appropriate representative.\"\"\"\n\n choice: str = Field(description=\"should be one of: music, customer\")\n\n\nsystem_message = \"\"\"Your job is to help as a customer service representative for a music store.\n\nYou should interact politely with customers to try to figure out how you can help. You can help in a few ways:\n\n- Updating user information: if a customer wants to update the information in the user database. Call the router with `customer`\n- Recommending music: if a customer wants to find some music or information about music. Call the router with `music`\n\nIf the user is asking or wants to ask about updating or accessing their information, send them to that route.\nIf the user is asking or wants to ask about music, send them to that route.\nOtherwise, respond.\"\"\"\n\n\ndef get_messages(messages):\n return [SystemMessage(content=system_message)] + messages"]
},
{
"cell_type": "code",
@@ -393,9 +275,7 @@
"id": "ddf27314",
"metadata": {},
"outputs": [],
"source": [
"chain = get_messages | model.bind_tools([Router])"
]
"source": ["chain = get_messages | model.bind_tools([Router])"]
},
{
"cell_type": "code",
@@ -414,10 +294,7 @@
"output_type": "execute_result"
}
],
"source": [
"msgs = [HumanMessage(content=\"hi! can you help me find a good song?\")]\n",
"chain.invoke(msgs)"
]
"source": ["msgs = [HumanMessage(content=\"hi! can you help me find a good song?\")]\nchain.invoke(msgs)"]
},
{
"cell_type": "code",
@@ -436,10 +313,7 @@
"output_type": "execute_result"
}
],
"source": [
"msgs = [HumanMessage(content=\"hi! what's the email you have for me?\")]\n",
"chain.invoke(msgs)"
]
"source": ["msgs = [HumanMessage(content=\"hi! what's the email you have for me?\")]\nchain.invoke(msgs)"]
},
{
"cell_type": "code",
@@ -447,15 +321,7 @@
"id": "bd6ddd8b-7500-46a7-811d-3bcb937bda51",
"metadata": {},
"outputs": [],
"source": [
"from langchain_core.messages import AIMessage\n",
"\n",
"\n",
"def add_name(message, name):\n",
" _dict = message.dict()\n",
" _dict[\"name\"] = name\n",
" return AIMessage(**_dict)"
]
"source": ["from langchain_core.messages import AIMessage\n\n\ndef add_name(message, name):\n _dict = message.dict()\n _dict[\"name\"] = name\n return AIMessage(**_dict)"]
},
{
"cell_type": "code",
@@ -463,45 +329,7 @@
"id": "27494de5-8345-4c23-bc0e-81e0dd5d47d8",
"metadata": {},
"outputs": [],
"source": [
"import json\n",
"\n",
"from langgraph.graph import END\n",
"\n",
"\n",
"def _get_last_ai_message(messages):\n",
" for m in messages[::-1]:\n",
" if isinstance(m, AIMessage):\n",
" return m\n",
" return None\n",
"\n",
"\n",
"def _is_tool_call(msg):\n",
" return hasattr(msg, \"additional_kwargs\") and \"tool_calls\" in msg.additional_kwargs\n",
"\n",
"\n",
"def _route(messages):\n",
" last_message = messages[-1]\n",
" if isinstance(last_message, AIMessage):\n",
" if not last_message.tool_calls:\n",
" return END\n",
" else:\n",
" if last_message.name == \"general\":\n",
" if len(last_message.tool_calls) > 1:\n",
" raise ValueError(\"Too many tools\")\n",
" return last_message.tool_calls[0][\"args\"][\"choice\"]\n",
" else:\n",
" return \"tools\"\n",
" last_m = _get_last_ai_message(messages)\n",
" if last_m is None:\n",
" return \"general\"\n",
" if last_m.name == \"music\":\n",
" return \"music\"\n",
" elif last_m.name == \"customer\":\n",
" return \"customer\"\n",
" else:\n",
" return \"general\""
]
"source": ["import json\n\nfrom langgraph.graph import END, START\n\n\ndef _get_last_ai_message(messages):\n for m in messages[::-1]:\n if isinstance(m, AIMessage):\n return m\n return None\n\n\ndef _is_tool_call(msg):\n return hasattr(msg, \"additional_kwargs\") and \"tool_calls\" in msg.additional_kwargs\n\n\ndef _route(messages):\n last_message = messages[-1]\n if isinstance(last_message, AIMessage):\n if not last_message.tool_calls:\n return END\n else:\n if last_message.name == \"general\":\n if len(last_message.tool_calls) > 1:\n raise ValueError(\"Too many tools\")\n return last_message.tool_calls[0][\"args\"][\"choice\"]\n else:\n return \"tools\"\n last_m = _get_last_ai_message(messages)\n if last_m is None:\n return \"general\"\n if last_m.name == \"music\":\n return \"music\"\n elif last_m.name == \"customer\":\n return \"customer\"\n else:\n return \"general\""]
},
{
"cell_type": "code",
@@ -509,12 +337,7 @@
"id": "8aec704a-46fe-4fb3-bdee-11c3bbffc370",
"metadata": {},
"outputs": [],
"source": [
"from langgraph.prebuilt import ToolNode\n",
"\n",
"tools = [get_albums_by_artist, get_tracks_by_artist, check_for_songs, get_customer_info]\n",
"tool_node = ToolNode(tools)"
]
"source": ["from langgraph.prebuilt import ToolNode\n\ntools = [get_albums_by_artist, get_tracks_by_artist, check_for_songs, get_customer_info]\ntool_node = ToolNode(tools)"]
},
{
"cell_type": "code",
@@ -522,16 +345,7 @@
"id": "4d5b75c6-73e0-4922-a765-a15be63f869e",
"metadata": {},
"outputs": [],
"source": [
"def _filter_out_routes(messages):\n",
" ms = []\n",
" for m in messages:\n",
" if _is_tool_call(m):\n",
" if m.name == \"general\":\n",
" continue\n",
" ms.append(m)\n",
" return ms"
]
"source": ["def _filter_out_routes(messages):\n ms = []\n for m in messages:\n if _is_tool_call(m):\n if m.name == \"general\":\n continue\n ms.append(m)\n return ms"]
},
{
"cell_type": "code",
@@ -539,13 +353,7 @@
"id": "fd4dbf98-dbb3-411a-bad6-2bb334072aaf",
"metadata": {},
"outputs": [],
"source": [
"from functools import partial\n",
"\n",
"general_node = _filter_out_routes | chain | partial(add_name, name=\"general\")\n",
"music_node = _filter_out_routes | song_recc_chain | partial(add_name, name=\"music\")\n",
"customer_node = _filter_out_routes | customer_chain | partial(add_name, name=\"customer\")"
]
"source": ["from functools import partial\n\ngeneral_node = _filter_out_routes | chain | partial(add_name, name=\"general\")\nmusic_node = _filter_out_routes | song_recc_chain | partial(add_name, name=\"music\")\ncustomer_node = _filter_out_routes | customer_chain | partial(add_name, name=\"customer\")"]
},
{
"cell_type": "code",
@@ -553,33 +361,7 @@
"id": "dcade924",
"metadata": {},
"outputs": [],
"source": [
"from langgraph.checkpoint.sqlite import SqliteSaver\n",
"\n",
"from langgraph.graph import MessageGraph\n",
"\n",
"memory = SqliteSaver.from_conn_string(\":memory:\")\n",
"graph = MessageGraph()\n",
"nodes = {\n",
" \"general\": \"general\",\n",
" \"music\": \"music\",\n",
" END: END,\n",
" \"tools\": \"tools\",\n",
" \"customer\": \"customer\",\n",
"}\n",
"# Define a new graph\n",
"workflow = MessageGraph()\n",
"workflow.add_node(\"general\", general_node)\n",
"workflow.add_node(\"music\", music_node)\n",
"workflow.add_node(\"customer\", customer_node)\n",
"workflow.add_node(\"tools\", tool_node)\n",
"workflow.add_conditional_edges(\"general\", _route, nodes)\n",
"workflow.add_conditional_edges(\"tools\", _route, nodes)\n",
"workflow.add_conditional_edges(\"music\", _route, nodes)\n",
"workflow.add_conditional_edges(\"customer\", _route, nodes)\n",
"workflow.set_conditional_entry_point(_route, nodes)\n",
"graph = workflow.compile()"
]
"source": ["from langgraph.checkpoint.sqlite import SqliteSaver\n\nfrom langgraph.graph import MessageGraph\n\nmemory = SqliteSaver.from_conn_string(\":memory:\")\ngraph = MessageGraph()\nnodes = {\n \"general\": \"general\",\n \"music\": \"music\",\n END: END,\n \"tools\": \"tools\",\n \"customer\": \"customer\",\n}\n# Define a new graph\nworkflow = MessageGraph()\nworkflow.add_node(\"general\", general_node)\nworkflow.add_node(\"music\", music_node)\nworkflow.add_node(\"customer\", customer_node)\nworkflow.add_node(\"tools\", tool_node)\nworkflow.add_conditional_edges(\"general\", _route, nodes)\nworkflow.add_conditional_edges(\"tools\", _route, nodes)\nworkflow.add_conditional_edges(\"music\", _route, nodes)\nworkflow.add_conditional_edges(\"customer\", _route, nodes)\nworkflow.add_conditional_edges(START, _route, nodes)\ngraph = workflow.compile()"]
},
{
"cell_type": "code",
@@ -715,27 +497,7 @@
]
}
],
"source": [
"import uuid\n",
"\n",
"from langchain_core.messages import HumanMessage\n",
"\n",
"from langgraph.graph.graph import START\n",
"\n",
"history = []\n",
"while True:\n",
" user = input(\"User (q/Q to quit): \")\n",
" if user in {\"q\", \"Q\"}:\n",
" print(\"AI: Byebye\")\n",
" break\n",
" history.append(HumanMessage(content=user))\n",
" async for output in graph.astream(history):\n",
" for key, value in output.items():\n",
" print(f\"Output from node '{key}':\")\n",
" print(\"---\")\n",
" print(value)\n",
" print(\"\\n---\\n\")"
]
"source": ["import uuid\n\nfrom langchain_core.messages import HumanMessage\n\nfrom langgraph.graph.graph import START\n\nhistory = []\nwhile True:\n user = input(\"User (q/Q to quit): \")\n if user in {\"q\", \"Q\"}:\n print(\"AI: Byebye\")\n break\n history.append(HumanMessage(content=user))\n async for output in graph.astream(history):\n for key, value in output.items():\n print(f\"Output from node '{key}':\")\n print(\"---\")\n print(value)\n print(\"\\n---\\n\")"]
}
],
"metadata": {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 322 KiB

After

Width:  |  Height:  |  Size: 432 KiB

File diff suppressed because it is too large Load Diff
+239 -239
View File
@@ -1,247 +1,247 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "992c4695-ec4f-428d-bd05-fb3b5fbd70f4",
"metadata": {},
"source": [
"# How to add human-in-the-loop processes to the prebuilt ReAct agent\n",
"\n",
"This tutorial will show how to add human-in-the-loop processes to the prebuilt ReAct agent. Please see [this tutorial](./create-react-agent.ipynb) for how to get started with the prebuilt ReAct agent\n",
"\n",
"You can add a a breakpoint before tools are called by passing `interrupt_before=[\"tools\"]` to `create_react_agent`. Note that you need to be using a checkpointer for this to work."
]
},
{
"cell_type": "markdown",
"id": "7be3889f-3c17-4fa1-bd2b-84114a2c7247",
"metadata": {},
"source": [
"## Setup"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "a213e11a-5c62-4ddb-a707-490d91add383",
"metadata": {},
"outputs": [],
"source": [
"%%capture --no-stderr\n",
"%pip install -U langgraph langchain-openai"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "23a1885c-04ab-4750-aefa-105891fddf3e",
"metadata": {},
"outputs": [
"cells": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"OPENAI_API_KEY: ········\n"
]
}
],
"source": [
"import getpass\n",
"import os\n",
"\n",
"\n",
"def _set_env(var: str):\n",
" if not os.environ.get(var):\n",
" os.environ[var] = getpass.getpass(f\"{var}: \")\n",
"\n",
"\n",
"_set_env(\"OPENAI_API_KEY\")\n",
"\n",
"# Recommended\n",
"_set_env(\"LANGCHAIN_API_KEY\")\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"os.environ[\"LANGCHAIN_PROJECT\"] = \"Create ReAct Agent Tutorial\""
]
},
{
"cell_type": "markdown",
"id": "03c0f089-070c-4cd4-87e0-6c51f2477b82",
"metadata": {},
"source": [
"## Code"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "7a154152-973e-4b5d-aa13-48c617744a4c",
"metadata": {},
"outputs": [],
"source": [
"# First we initialize the model we want to use.\n",
"from langchain_openai import ChatOpenAI\n",
"\n",
"model = ChatOpenAI(model=\"gpt-4o\", temperature=0)\n",
"\n",
"\n",
"# For this tutorial we will use custom tool that returns pre-defined values for weather in two cities (NYC & SF)\n",
"\n",
"from typing import Literal\n",
"\n",
"from langchain_core.tools import tool\n",
"\n",
"\n",
"@tool\n",
"def get_weather(city: Literal[\"nyc\", \"sf\"]):\n",
" \"\"\"Use this to get weather information.\"\"\"\n",
" if city == \"nyc\":\n",
" return \"It might be cloudy in nyc\"\n",
" elif city == \"sf\":\n",
" return \"It's always sunny in sf\"\n",
" else:\n",
" raise AssertionError(\"Unknown city\")\n",
"\n",
"\n",
"tools = [get_weather]\n",
"\n",
"# We need a checkpointer to enable human-in-the-loop patterns\n",
"from langgraph.checkpoint import MemorySaver\n",
"\n",
"memory = MemorySaver()\n",
"\n",
"# Define the graph\n",
"\n",
"from langgraph.prebuilt import create_react_agent\n",
"\n",
"graph = create_react_agent(\n",
" model, tools=tools, interrupt_before=[\"tools\"], checkpointer=memory\n",
")"
]
},
{
"cell_type": "markdown",
"id": "00407425-506d-4ffd-9c86-987921d8c844",
"metadata": {},
"source": [
"## Usage\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "16636975-5f2d-4dc7-ab8e-d0bea0830a28",
"metadata": {},
"outputs": [],
"source": [
"def print_stream(stream):\n",
" for s in stream:\n",
" message = s[\"messages\"][-1]\n",
" if isinstance(message, tuple):\n",
" print(message)\n",
" else:\n",
" message.pretty_print()"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "9ffff6c3-a4f5-47c9-b51d-97caaee85cd6",
"metadata": {},
"outputs": [
"cell_type": "markdown",
"id": "992c4695-ec4f-428d-bd05-fb3b5fbd70f4",
"metadata": {},
"source": [
"# How to add human-in-the-loop processes to the prebuilt ReAct agent\n",
"\n",
"This tutorial will show how to add human-in-the-loop processes to the prebuilt ReAct agent. Please see [this tutorial](./create-react-agent.ipynb) for how to get started with the prebuilt ReAct agent\n",
"\n",
"You can add a a breakpoint before tools are called by passing `interrupt_before=[\"tools\"]` to `create_react_agent`. Note that you need to be using a checkpointer for this to work."
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"================================\u001b[1m Human Message \u001b[0m=================================\n",
"\n",
"What's the weather in SF?\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"Tool Calls:\n",
" get_weather (call_0OMmuTLec9t8kxMVkllZCSxo)\n",
" Call ID: call_0OMmuTLec9t8kxMVkllZCSxo\n",
" Args:\n",
" city: sf\n"
]
}
],
"source": [
"config = {\"configurable\": {\"thread_id\": \"42\"}}\n",
"inputs = {\"messages\": [(\"user\", \"What's the weather in SF?\")]}\n",
"\n",
"print_stream(graph.stream(inputs, config, stream_mode=\"values\"))"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "3decf001-7228-4ed5-8779-2b9ed98a74ea",
"metadata": {},
"outputs": [
"cell_type": "markdown",
"id": "7be3889f-3c17-4fa1-bd2b-84114a2c7247",
"metadata": {},
"source": [
"## Setup"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Next step: ('tools',)\n"
]
}
],
"source": [
"snapshot = graph.get_state(config)\n",
"print(\"Next step: \", snapshot.next)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "83148e08-63e8-49e5-a08b-02dc907bed1d",
"metadata": {},
"outputs": [
"cell_type": "code",
"execution_count": 1,
"id": "a213e11a-5c62-4ddb-a707-490d91add383",
"metadata": {},
"outputs": [],
"source": [
"%%capture --no-stderr\n",
"%pip install -U langgraph langchain-openai"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"=================================\u001b[1m Tool Message \u001b[0m=================================\n",
"Name: get_weather\n",
"\n",
"It's always sunny in sf\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"\n",
"The weather in San Francisco is currently sunny.\n"
]
"cell_type": "code",
"execution_count": 2,
"id": "23a1885c-04ab-4750-aefa-105891fddf3e",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"OPENAI_API_KEY: ········\n"
]
}
],
"source": [
"import getpass\n",
"import os\n",
"\n",
"\n",
"def _set_env(var: str):\n",
" if not os.environ.get(var):\n",
" os.environ[var] = getpass.getpass(f\"{var}: \")\n",
"\n",
"\n",
"_set_env(\"OPENAI_API_KEY\")\n",
"\n",
"# Recommended\n",
"_set_env(\"LANGCHAIN_API_KEY\")\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"os.environ[\"LANGCHAIN_PROJECT\"] = \"Create ReAct Agent Tutorial\""
]
},
{
"cell_type": "markdown",
"id": "03c0f089-070c-4cd4-87e0-6c51f2477b82",
"metadata": {},
"source": [
"## Code"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "7a154152-973e-4b5d-aa13-48c617744a4c",
"metadata": {},
"outputs": [],
"source": [
"# First we initialize the model we want to use.\n",
"from langchain_openai import ChatOpenAI\n",
"\n",
"model = ChatOpenAI(model=\"gpt-4o\", temperature=0)\n",
"\n",
"\n",
"# For this tutorial we will use custom tool that returns pre-defined values for weather in two cities (NYC & SF)\n",
"\n",
"from typing import Literal\n",
"\n",
"from langchain_core.tools import tool\n",
"\n",
"\n",
"@tool\n",
"def get_weather(city: Literal[\"nyc\", \"sf\"]):\n",
" \"\"\"Use this to get weather information.\"\"\"\n",
" if city == \"nyc\":\n",
" return \"It might be cloudy in nyc\"\n",
" elif city == \"sf\":\n",
" return \"It's always sunny in sf\"\n",
" else:\n",
" raise AssertionError(\"Unknown city\")\n",
"\n",
"\n",
"tools = [get_weather]\n",
"\n",
"# We need a checkpointer to enable human-in-the-loop patterns\n",
"from langgraph.checkpoint.memory import MemorySaver\n",
"\n",
"memory = MemorySaver()\n",
"\n",
"# Define the graph\n",
"\n",
"from langgraph.prebuilt import create_react_agent\n",
"\n",
"graph = create_react_agent(\n",
" model, tools=tools, interrupt_before=[\"tools\"], checkpointer=memory\n",
")"
]
},
{
"cell_type": "markdown",
"id": "00407425-506d-4ffd-9c86-987921d8c844",
"metadata": {},
"source": [
"## Usage\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "16636975-5f2d-4dc7-ab8e-d0bea0830a28",
"metadata": {},
"outputs": [],
"source": [
"def print_stream(stream):\n",
" for s in stream:\n",
" message = s[\"messages\"][-1]\n",
" if isinstance(message, tuple):\n",
" print(message)\n",
" else:\n",
" message.pretty_print()"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "9ffff6c3-a4f5-47c9-b51d-97caaee85cd6",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"================================\u001b[1m Human Message \u001b[0m=================================\n",
"\n",
"What's the weather in SF?\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"Tool Calls:\n",
" get_weather (call_0OMmuTLec9t8kxMVkllZCSxo)\n",
" Call ID: call_0OMmuTLec9t8kxMVkllZCSxo\n",
" Args:\n",
" city: sf\n"
]
}
],
"source": [
"config = {\"configurable\": {\"thread_id\": \"42\"}}\n",
"inputs = {\"messages\": [(\"user\", \"What's the weather in SF?\")]}\n",
"\n",
"print_stream(graph.stream(inputs, config, stream_mode=\"values\"))"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "3decf001-7228-4ed5-8779-2b9ed98a74ea",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Next step: ('tools',)\n"
]
}
],
"source": [
"snapshot = graph.get_state(config)\n",
"print(\"Next step: \", snapshot.next)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "83148e08-63e8-49e5-a08b-02dc907bed1d",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"=================================\u001b[1m Tool Message \u001b[0m=================================\n",
"Name: get_weather\n",
"\n",
"It's always sunny in sf\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"\n",
"The weather in San Francisco is currently sunny.\n"
]
}
],
"source": [
"print_stream(graph.stream(None, config, stream_mode=\"values\"))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6f6f8965-b016-4e25-be63-31c00fc0a6de",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.1"
}
],
"source": [
"print_stream(graph.stream(None, config, stream_mode=\"values\"))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6f6f8965-b016-4e25-be63-31c00fc0a6de",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.1"
}
},
"nbformat": 4,
"nbformat_minor": 5
"nbformat": 4,
"nbformat_minor": 5
}
+248 -248
View File
@@ -1,255 +1,255 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "992c4695-ec4f-428d-bd05-fb3b5fbd70f4",
"metadata": {},
"source": [
"# How to add memory to the prebuilt ReAct agent\n",
"\n",
"This tutorial will show how to add memory to the prebuilt ReAct agent. Please see [this tutorial](./create-react-agent.ipynb) for how to get started with the prebuilt ReAct agent\n",
"\n",
"All we need to do to enable memory is pass in a checkpointer to `create_react_agents`"
]
},
{
"cell_type": "markdown",
"id": "7be3889f-3c17-4fa1-bd2b-84114a2c7247",
"metadata": {},
"source": [
"## Setup"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "a213e11a-5c62-4ddb-a707-490d91add383",
"metadata": {},
"outputs": [],
"source": [
"%%capture --no-stderr\n",
"%pip install -U langgraph langchain-openai"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "23a1885c-04ab-4750-aefa-105891fddf3e",
"metadata": {},
"outputs": [
"cells": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"OPENAI_API_KEY: ········\n"
]
}
],
"source": [
"import getpass\n",
"import os\n",
"\n",
"\n",
"def _set_env(var: str):\n",
" if not os.environ.get(var):\n",
" os.environ[var] = getpass.getpass(f\"{var}: \")\n",
"\n",
"\n",
"_set_env(\"OPENAI_API_KEY\")\n",
"\n",
"# Recommended\n",
"_set_env(\"LANGCHAIN_API_KEY\")\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"os.environ[\"LANGCHAIN_PROJECT\"] = \"Create ReAct Agent Tutorial\""
]
},
{
"cell_type": "markdown",
"id": "03c0f089-070c-4cd4-87e0-6c51f2477b82",
"metadata": {},
"source": [
"## Code"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "7a154152-973e-4b5d-aa13-48c617744a4c",
"metadata": {},
"outputs": [],
"source": [
"# First we initialize the model we want to use.\n",
"from langchain_openai import ChatOpenAI\n",
"\n",
"model = ChatOpenAI(model=\"gpt-4o\", temperature=0)\n",
"\n",
"\n",
"# For this tutorial we will use custom tool that returns pre-defined values for weather in two cities (NYC & SF)\n",
"\n",
"from typing import Literal\n",
"\n",
"from langchain_core.tools import tool\n",
"\n",
"\n",
"@tool\n",
"def get_weather(city: Literal[\"nyc\", \"sf\"]):\n",
" \"\"\"Use this to get weather information.\"\"\"\n",
" if city == \"nyc\":\n",
" return \"It might be cloudy in nyc\"\n",
" elif city == \"sf\":\n",
" return \"It's always sunny in sf\"\n",
" else:\n",
" raise AssertionError(\"Unknown city\")\n",
"\n",
"\n",
"tools = [get_weather]\n",
"\n",
"# We can add \"chat memory\" to the graph with LangGraph's checkpointer\n",
"# to retain the chat context between interactions\n",
"from langgraph.checkpoint import MemorySaver\n",
"\n",
"memory = MemorySaver()\n",
"\n",
"# Define the graph\n",
"\n",
"from langgraph.prebuilt import create_react_agent\n",
"\n",
"graph = create_react_agent(model, tools=tools, checkpointer=memory)"
]
},
{
"cell_type": "markdown",
"id": "00407425-506d-4ffd-9c86-987921d8c844",
"metadata": {},
"source": [
"## Usage\n",
"\n",
"Let's interact with it multiple times to show that it can remember"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "16636975-5f2d-4dc7-ab8e-d0bea0830a28",
"metadata": {},
"outputs": [],
"source": [
"def print_stream(stream):\n",
" for s in stream:\n",
" message = s[\"messages\"][-1]\n",
" if isinstance(message, tuple):\n",
" print(message)\n",
" else:\n",
" message.pretty_print()"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "9ffff6c3-a4f5-47c9-b51d-97caaee85cd6",
"metadata": {},
"outputs": [
"cell_type": "markdown",
"id": "992c4695-ec4f-428d-bd05-fb3b5fbd70f4",
"metadata": {},
"source": [
"# How to add memory to the prebuilt ReAct agent\n",
"\n",
"This tutorial will show how to add memory to the prebuilt ReAct agent. Please see [this tutorial](./create-react-agent.ipynb) for how to get started with the prebuilt ReAct agent\n",
"\n",
"All we need to do to enable memory is pass in a checkpointer to `create_react_agents`"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"================================\u001b[1m Human Message \u001b[0m=================================\n",
"\n",
"What's the weather in NYC?\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"Tool Calls:\n",
" get_weather (call_mdovy4yXSSYrmSlnlVSUacVn)\n",
" Call ID: call_mdovy4yXSSYrmSlnlVSUacVn\n",
" Args:\n",
" city: nyc\n",
"=================================\u001b[1m Tool Message \u001b[0m=================================\n",
"Name: get_weather\n",
"\n",
"It might be cloudy in nyc\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"\n",
"The weather in NYC might be cloudy.\n"
]
}
],
"source": [
"config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
"inputs = {\"messages\": [(\"user\", \"What's the weather in NYC?\")]}\n",
"\n",
"print_stream(graph.stream(inputs, config=config, stream_mode=\"values\"))"
]
},
{
"cell_type": "markdown",
"id": "838a043f-90ad-4e69-9d1d-6e22db2c346c",
"metadata": {},
"source": [
"Notice that when we pass the same the same thread ID, the chat history is preserved"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "187479f9-32fa-4611-9487-cf816ba2e147",
"metadata": {},
"outputs": [
"cell_type": "markdown",
"id": "7be3889f-3c17-4fa1-bd2b-84114a2c7247",
"metadata": {},
"source": [
"## Setup"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"================================\u001b[1m Human Message \u001b[0m=================================\n",
"\n",
"What's it known for?\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"\n",
"New York City (NYC) is known for many things, including:\n",
"\n",
"1. **Landmarks and Attractions**: The Statue of Liberty, Times Square, Central Park, Empire State Building, and Brooklyn Bridge.\n",
"2. **Cultural Institutions**: Broadway theaters, Metropolitan Museum of Art, Museum of Modern Art (MoMA), and the American Museum of Natural History.\n",
"3. **Diverse Neighborhoods**: Areas like Chinatown, Little Italy, Harlem, and Greenwich Village.\n",
"4. **Financial Hub**: Wall Street and the New York Stock Exchange.\n",
"5. **Cuisine**: A melting pot of global cuisines, famous for its pizza, bagels, and street food.\n",
"6. **Media and Entertainment**: Home to major media companies, TV networks, and film studios.\n",
"7. **Fashion**: A global fashion capital, hosting New York Fashion Week.\n",
"8. **Sports**: Teams like the New York Yankees, New York Mets, New York Knicks, and New York Rangers.\n",
"9. **Public Transportation**: An extensive subway system and iconic yellow taxis.\n",
"10. **Events**: New Year's Eve celebration in Times Square, Macy's Thanksgiving Day Parade, and various cultural festivals.\n"
]
"cell_type": "code",
"execution_count": 1,
"id": "a213e11a-5c62-4ddb-a707-490d91add383",
"metadata": {},
"outputs": [],
"source": [
"%%capture --no-stderr\n",
"%pip install -U langgraph langchain-openai"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "23a1885c-04ab-4750-aefa-105891fddf3e",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"OPENAI_API_KEY: ········\n"
]
}
],
"source": [
"import getpass\n",
"import os\n",
"\n",
"\n",
"def _set_env(var: str):\n",
" if not os.environ.get(var):\n",
" os.environ[var] = getpass.getpass(f\"{var}: \")\n",
"\n",
"\n",
"_set_env(\"OPENAI_API_KEY\")\n",
"\n",
"# Recommended\n",
"_set_env(\"LANGCHAIN_API_KEY\")\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"os.environ[\"LANGCHAIN_PROJECT\"] = \"Create ReAct Agent Tutorial\""
]
},
{
"cell_type": "markdown",
"id": "03c0f089-070c-4cd4-87e0-6c51f2477b82",
"metadata": {},
"source": [
"## Code"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "7a154152-973e-4b5d-aa13-48c617744a4c",
"metadata": {},
"outputs": [],
"source": [
"# First we initialize the model we want to use.\n",
"from langchain_openai import ChatOpenAI\n",
"\n",
"model = ChatOpenAI(model=\"gpt-4o\", temperature=0)\n",
"\n",
"\n",
"# For this tutorial we will use custom tool that returns pre-defined values for weather in two cities (NYC & SF)\n",
"\n",
"from typing import Literal\n",
"\n",
"from langchain_core.tools import tool\n",
"\n",
"\n",
"@tool\n",
"def get_weather(city: Literal[\"nyc\", \"sf\"]):\n",
" \"\"\"Use this to get weather information.\"\"\"\n",
" if city == \"nyc\":\n",
" return \"It might be cloudy in nyc\"\n",
" elif city == \"sf\":\n",
" return \"It's always sunny in sf\"\n",
" else:\n",
" raise AssertionError(\"Unknown city\")\n",
"\n",
"\n",
"tools = [get_weather]\n",
"\n",
"# We can add \"chat memory\" to the graph with LangGraph's checkpointer\n",
"# to retain the chat context between interactions\n",
"from langgraph.checkpoint.memory import MemorySaver\n",
"\n",
"memory = MemorySaver()\n",
"\n",
"# Define the graph\n",
"\n",
"from langgraph.prebuilt import create_react_agent\n",
"\n",
"graph = create_react_agent(model, tools=tools, checkpointer=memory)"
]
},
{
"cell_type": "markdown",
"id": "00407425-506d-4ffd-9c86-987921d8c844",
"metadata": {},
"source": [
"## Usage\n",
"\n",
"Let's interact with it multiple times to show that it can remember"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "16636975-5f2d-4dc7-ab8e-d0bea0830a28",
"metadata": {},
"outputs": [],
"source": [
"def print_stream(stream):\n",
" for s in stream:\n",
" message = s[\"messages\"][-1]\n",
" if isinstance(message, tuple):\n",
" print(message)\n",
" else:\n",
" message.pretty_print()"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "9ffff6c3-a4f5-47c9-b51d-97caaee85cd6",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"================================\u001b[1m Human Message \u001b[0m=================================\n",
"\n",
"What's the weather in NYC?\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"Tool Calls:\n",
" get_weather (call_mdovy4yXSSYrmSlnlVSUacVn)\n",
" Call ID: call_mdovy4yXSSYrmSlnlVSUacVn\n",
" Args:\n",
" city: nyc\n",
"=================================\u001b[1m Tool Message \u001b[0m=================================\n",
"Name: get_weather\n",
"\n",
"It might be cloudy in nyc\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"\n",
"The weather in NYC might be cloudy.\n"
]
}
],
"source": [
"config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
"inputs = {\"messages\": [(\"user\", \"What's the weather in NYC?\")]}\n",
"\n",
"print_stream(graph.stream(inputs, config=config, stream_mode=\"values\"))"
]
},
{
"cell_type": "markdown",
"id": "838a043f-90ad-4e69-9d1d-6e22db2c346c",
"metadata": {},
"source": [
"Notice that when we pass the same the same thread ID, the chat history is preserved"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "187479f9-32fa-4611-9487-cf816ba2e147",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"================================\u001b[1m Human Message \u001b[0m=================================\n",
"\n",
"What's it known for?\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"\n",
"New York City (NYC) is known for many things, including:\n",
"\n",
"1. **Landmarks and Attractions**: The Statue of Liberty, Times Square, Central Park, Empire State Building, and Brooklyn Bridge.\n",
"2. **Cultural Institutions**: Broadway theaters, Metropolitan Museum of Art, Museum of Modern Art (MoMA), and the American Museum of Natural History.\n",
"3. **Diverse Neighborhoods**: Areas like Chinatown, Little Italy, Harlem, and Greenwich Village.\n",
"4. **Financial Hub**: Wall Street and the New York Stock Exchange.\n",
"5. **Cuisine**: A melting pot of global cuisines, famous for its pizza, bagels, and street food.\n",
"6. **Media and Entertainment**: Home to major media companies, TV networks, and film studios.\n",
"7. **Fashion**: A global fashion capital, hosting New York Fashion Week.\n",
"8. **Sports**: Teams like the New York Yankees, New York Mets, New York Knicks, and New York Rangers.\n",
"9. **Public Transportation**: An extensive subway system and iconic yellow taxis.\n",
"10. **Events**: New Year's Eve celebration in Times Square, Macy's Thanksgiving Day Parade, and various cultural festivals.\n"
]
}
],
"source": [
"inputs = {\"messages\": [(\"user\", \"What's it known for?\")]}\n",
"print_stream(graph.stream(inputs, config=config, stream_mode=\"values\"))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3decf001-7228-4ed5-8779-2b9ed98a74ea",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.1"
}
],
"source": [
"inputs = {\"messages\": [(\"user\", \"What's it known for?\")]}\n",
"print_stream(graph.stream(inputs, config=config, stream_mode=\"values\"))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3decf001-7228-4ed5-8779-2b9ed98a74ea",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.1"
}
},
"nbformat": 4,
"nbformat_minor": 5
"nbformat": 4,
"nbformat_minor": 5
}
@@ -9,7 +9,7 @@
"\n",
"This tutorial will show how to add a custom system prompt to the prebuilt ReAct agent. Please see [this tutorial](./create-react-agent.ipynb) for how to get started with the prebuilt ReAct agent\n",
"\n",
"You can add a custom system prompt by passing a string to the `messages_modifier` param."
"You can add a custom system prompt by passing a string to the `state_modifier` param."
]
},
{
@@ -112,7 +112,7 @@
"\n",
"from langgraph.prebuilt import create_react_agent\n",
"\n",
"graph = create_react_agent(model, tools=tools, messages_modifier=prompt)"
"graph = create_react_agent(model, tools=tools, state_modifier=prompt)"
]
},
{
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 MiB

After

Width:  |  Height:  |  Size: 616 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 MiB

After

Width:  |  Height:  |  Size: 523 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 MiB

After

Width:  |  Height:  |  Size: 562 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 MiB

After

Width:  |  Height:  |  Size: 422 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 MiB

After

Width:  |  Height:  |  Size: 613 KiB

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 it is too large Load Diff
File diff suppressed because one or more lines are too long
+93
View File
@@ -0,0 +1,93 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "f262985e-e973-4a27-9c9e-dbb3a06a35b7",
"metadata": {},
"source": [
"# How to define input/output schema for your graph\n",
"\n",
"By default, `StateGraph` takes in a single schema and all nodes are expected to communicate with that schema. However, it is also possible to define explicit input and output schemas for a graph. This is helpful if you want to draw a distinction between input and output keys.\n",
"\n",
"In this notebook we'll walk through an example of this. At a high level, in order to do this you simply have to pass in `input=..., output=...` when defining the graph. Let's see an example below!"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "6ec0eb77-874e-443e-8c73-93125b515106",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'answer': 'bye'}"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from langgraph.graph import StateGraph, START, END\n",
"from typing import TypedDict\n",
"\n",
"class InputState(TypedDict):\n",
" question: str\n",
"\n",
"class OutputState(TypedDict):\n",
" answer: str\n",
"\n",
"def answer_node(state: InputState):\n",
" return {\"answer\": \"bye\"}\n",
"\n",
"check = SqliteSaver.from_conn_string(\":memory:\")\n",
"graph = StateGraph(input=InputState, output=OutputState)\n",
"graph.add_node(answer_node)\n",
"graph.add_edge(START, \"answer_node\")\n",
"graph.add_edge(\"answer_node\", END)\n",
"graph = graph.compile()\n",
"\n",
"graph.invoke({\"question\": \"hi\"})"
]
},
{
"cell_type": "markdown",
"id": "6a68836f-98e1-4684-a8a6-c1473c73460c",
"metadata": {},
"source": [
"Notice that the output of invoke only includes the output schema."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b952a554-f2a4-4be3-81ab-2e08f0f441c2",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.1"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
+20 -7
View File
@@ -84,8 +84,9 @@
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "ef7bcad1-1274-4b7c-a2e9-365180ef3a31",
"id": "9c374e41-f9b7-439e-a520-6d8c853c5220",
"metadata": {},
"source": [
"## Part 1: Build a Basic Chatbot\n",
@@ -120,13 +121,24 @@
"graph_builder = StateGraph(State)"
]
},
{
"cell_type": "markdown",
"id": "31c755cd-8994-4867-bdff-96a55d7beae7",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Note</p>\n",
" <p>\n",
" The first thing you do when you define a graph is define the <code>State</code> of the graph. The <code>State</code> consists of the schema of the graph as well as reducer functions which specify how to apply updates to the state. In our example <code>State</code> is a <code>TypedDict</code> with a single key: <code>messages</code>. The <code>messages</code> key is annotated with the <a href=\"https://langchain-ai.github.io/langgraph/reference/graphs/?h=add+messages#add_messages\"><code>add_messages</code></a> reducer function, which tells LangGraph to append new messages to the existing list, rather than overwriting it. State keys without an annotation will be overwritten by each update, storing the most recent value. Check out <a href=\"https://langchain-ai.github.io/langgraph/reference/graphs/?h=add+messages#add_messages\">this conceptual guide</a> to learn more about state, reducers and other low-level concepts.\n",
" </p>\n",
"</div>"
]
},
{
"cell_type": "markdown",
"id": "4137feed-746e-4c72-a34a-f7a699ad5dcf",
"metadata": {},
"source": [
"**Notice** that we've defined our `State` as a TypedDict with a single key: `messages`. The `messages` key is annotated with the [`add_messages`](https://langchain-ai.github.io/langgraph/reference/graphs/?h=add+messages#add_messages) function, which tells LangGraph to append new messages to the existing list, rather than overwriting it.\n",
"\n",
"So now our graph knows two things:\n",
"\n",
"1. Every `node` we define will receive the current `State` as input and return a value that updates that state.\n",
@@ -1224,7 +1236,8 @@
"graph = graph_builder.compile(checkpointer=memory)\n",
"```\n",
"</pre>\n",
"</pre>details"
"</pre>\n",
"</details>"
]
},
{
@@ -3055,9 +3068,9 @@
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"display_name": "langgraph",
"language": "python",
"name": "python3"
"name": "langgraph"
},
"language_info": {
"codemirror_mode": {
@@ -3069,7 +3082,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.1"
"version": "3.11.9"
}
},
"nbformat": 4,
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 554 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 25 KiB

File diff suppressed because one or more lines are too long
Binary file not shown.

Before

Width:  |  Height:  |  Size: 248 KiB

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 863 KiB

After

Width:  |  Height:  |  Size: 371 KiB

File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -110,23 +110,26 @@
"\n",
"memory = SqliteSaver.from_conn_string(\":memory:\")\n",
"\n",
"\n",
"# We will add a `summary` attribute (in addition to `messages` key,\n",
"# which MessagesState already has)\n",
"class State(MessagesState):\n",
" summary: str\n",
"\n",
"\n",
"# We will use this model for both the conversation and the summarization\n",
"model = ChatAnthropic(model_name=\"claude-3-haiku-20240307\")\n",
"\n",
"\n",
"# Define the logic to call the model\n",
"def call_model(state: State):\n",
" # If a summary exists, we add this in as a system message\n",
" summary = state.get('summary', '')\n",
" summary = state.get(\"summary\", \"\")\n",
" if summary:\n",
" system_message = f\"Summary of conversation earlier: {summary}\"\n",
" messages = [SystemMessage(content=system_message)] + state['messages']\n",
" messages = [SystemMessage(content=system_message)] + state[\"messages\"]\n",
" else:\n",
" messages = state['messages']\n",
" messages = state[\"messages\"]\n",
" response = model.invoke(messages)\n",
" # We return a list, because this will get added to the existing list\n",
" return {\"messages\": [response]}\n",
@@ -145,7 +148,7 @@
"\n",
"def summarize_conversation(state: State):\n",
" # First, we summarize the conversation\n",
" summary = state.get('summary', '')\n",
" summary = state.get(\"summary\", \"\")\n",
" if summary:\n",
" # If a summary already exists, we use a different system prompt\n",
" # to summarize it than if one didn't\n",
@@ -155,17 +158,13 @@
" )\n",
" else:\n",
" summary_message = \"Create a summary of the conversation above:\"\n",
" \n",
" messages = state['messages'] + [HumanMessage(content=summary_message)]\n",
"\n",
" messages = state[\"messages\"] + [HumanMessage(content=summary_message)]\n",
" response = model.invoke(messages)\n",
" # We now need to delete messages that we no longer want to show up\n",
" # I will delete all but the last two messages, but you can change this\n",
" delete_messages = [RemoveMessage(id=m.id) for m in state['messages'][:-2]]\n",
" return {\n",
" \"summary\": response.content,\n",
" \"messages\": delete_messages\n",
" }\n",
" \n",
" delete_messages = [RemoveMessage(id=m.id) for m in state[\"messages\"][:-2]]\n",
" return {\"summary\": response.content, \"messages\": delete_messages}\n",
"\n",
"\n",
"# Define a new graph\n",
@@ -212,10 +211,10 @@
"source": [
"def print_update(update):\n",
" for k, v in update.items():\n",
" for m in v['messages']:\n",
" for m in v[\"messages\"]:\n",
" m.pretty_print()\n",
" if 'summary' in v:\n",
" print(v['summary'])"
" if \"summary\" in v:\n",
" print(v[\"summary\"])"
]
},
{
+62 -51
View File
@@ -28,7 +28,7 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 1,
"id": "af4ce0ba-7596-4e5f-8bf8-0b0bd6e62833",
"metadata": {},
"outputs": [],
@@ -50,7 +50,15 @@
"execution_count": 2,
"id": "c903a1cf-2977-4e2d-ad7d-8b3946821d89",
"metadata": {},
"outputs": [],
"outputs": [
{
"name": "stdin",
"output_type": "stream",
"text": [
"ANTHROPIC_API_KEY: ········\n"
]
}
],
"source": [
"import getpass\n",
"import os\n",
@@ -94,7 +102,7 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": 4,
"id": "378899a9-3b9a-4748-95b6-eb00e0828677",
"metadata": {},
"outputs": [],
@@ -176,7 +184,7 @@
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": 5,
"id": "57b27553-21be-43e5-ac48-d1d0a3aa0dca",
"metadata": {},
"outputs": [
@@ -189,7 +197,7 @@
"hi! I'm bob\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"\n",
"Hello Bob! It's nice to meet you. How can I assist you today?\n",
"It's nice to meet you, Bob! I'm Claude, an AI assistant created by Anthropic. How can I assist you today?\n",
"================================\u001b[1m Human Message \u001b[0m=================================\n",
"\n",
"what's my name?\n",
@@ -225,26 +233,26 @@
},
{
"cell_type": "code",
"execution_count": 7,
"execution_count": 6,
"id": "8a850529-d038-48f7-b5a2-8d4d2923f83a",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[HumanMessage(content=\"hi! I'm bob\", id='3e1098f8-2657-42d3-b58a-7c2f46930b8c'),\n",
" AIMessage(content=\"Hello Bob! It's nice to meet you. How can I assist you today?\", response_metadata={'id': 'msg_01HT8MUEN4p16wbYv9Xm7kfr', 'model': 'claude-3-haiku-20240307', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 12, 'output_tokens': 20}}, id='run-86348912-72c4-42b0-b3e0-a47c4ebd1e52-0'),\n",
" HumanMessage(content=\"what's my name?\", id='9c3ef235-ec5c-4e57-a3b2-c17502de496d'),\n",
" AIMessage(content='Your name is Bob, as you introduced yourself at the beginning of our conversation.', response_metadata={'id': 'msg_01LVhb56f6RpAAoxASZrLzmK', 'model': 'claude-3-haiku-20240307', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 40, 'output_tokens': 19}}, id='run-e3d7447f-046a-4dfa-8813-38134dbcd1ef-0')]"
"[HumanMessage(content=\"hi! I'm bob\", id='bc1c6dd2-3bb9-4aa9-b7af-3c6af7e173ea'),\n",
" AIMessage(content=\"It's nice to meet you, Bob! I'm Claude, an AI assistant created by Anthropic. How can I assist you today?\", response_metadata={'id': 'msg_01XPSAenmSqK8rX2WgPZHfz7', 'model': 'claude-3-haiku-20240307', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 12, 'output_tokens': 32}}, id='run-1c69af09-adb1-412d-9010-2456e5a555fb-0', usage_metadata={'input_tokens': 12, 'output_tokens': 32, 'total_tokens': 44}),\n",
" HumanMessage(content=\"what's my name?\", id='f3c71afe-8ce2-4ed0-991e-65021f03b0a5'),\n",
" AIMessage(content='Your name is Bob, as you introduced yourself at the beginning of our conversation.', response_metadata={'id': 'msg_01BPZdwsjuMAbC1YAkqawXaF', 'model': 'claude-3-haiku-20240307', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 52, 'output_tokens': 19}}, id='run-b2eb9137-2f4e-446f-95f5-3d5f621a2cf8-0', usage_metadata={'input_tokens': 52, 'output_tokens': 19, 'total_tokens': 71})]"
]
},
"execution_count": 7,
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"messages = app.get_state(config).values['messages']\n",
"messages = app.get_state(config).values[\"messages\"]\n",
"messages"
]
},
@@ -258,24 +266,33 @@
},
{
"cell_type": "code",
"execution_count": 9,
"execution_count": 7,
"id": "df1a0970-7e64-4170-beef-2855d10eef42",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/Users/vadymbarda/.virtualenvs/langgraph/lib/python3.11/site-packages/langchain_core/_api/beta_decorator.py:87: LangChainBetaWarning: The class `RemoveMessage` is in beta. It is actively being worked on, so the API may change.\n",
" warn_beta(\n"
]
},
{
"data": {
"text/plain": [
"{'configurable': {'thread_id': '2',\n",
" 'thread_ts': '1ef3d750-5bc4-67c6-8005-9490a1b276f5'}}"
" 'thread_ts': '1ef42d00-d9ad-6f24-8005-feb089654def'}}"
]
},
"execution_count": 9,
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from langchain_core.messages import RemoveMessage\n",
"\n",
"app.update_state(config, {\"messages\": RemoveMessage(id=messages[0].id)})"
]
},
@@ -289,25 +306,25 @@
},
{
"cell_type": "code",
"execution_count": 11,
"execution_count": 8,
"id": "8bfe4ffa-e170-43bc-aec4-6e36ac620931",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[AIMessage(content=\"Hello Bob! It's nice to meet you. How can I assist you today?\", response_metadata={'id': 'msg_01HT8MUEN4p16wbYv9Xm7kfr', 'model': 'claude-3-haiku-20240307', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 12, 'output_tokens': 20}}, id='run-86348912-72c4-42b0-b3e0-a47c4ebd1e52-0'),\n",
" HumanMessage(content=\"what's my name?\", id='9c3ef235-ec5c-4e57-a3b2-c17502de496d'),\n",
" AIMessage(content='Your name is Bob, as you introduced yourself at the beginning of our conversation.', response_metadata={'id': 'msg_01LVhb56f6RpAAoxASZrLzmK', 'model': 'claude-3-haiku-20240307', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 40, 'output_tokens': 19}}, id='run-e3d7447f-046a-4dfa-8813-38134dbcd1ef-0')]"
"[AIMessage(content=\"It's nice to meet you, Bob! I'm Claude, an AI assistant created by Anthropic. How can I assist you today?\", response_metadata={'id': 'msg_01XPSAenmSqK8rX2WgPZHfz7', 'model': 'claude-3-haiku-20240307', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 12, 'output_tokens': 32}}, id='run-1c69af09-adb1-412d-9010-2456e5a555fb-0', usage_metadata={'input_tokens': 12, 'output_tokens': 32, 'total_tokens': 44}),\n",
" HumanMessage(content=\"what's my name?\", id='f3c71afe-8ce2-4ed0-991e-65021f03b0a5'),\n",
" AIMessage(content='Your name is Bob, as you introduced yourself at the beginning of our conversation.', response_metadata={'id': 'msg_01BPZdwsjuMAbC1YAkqawXaF', 'model': 'claude-3-haiku-20240307', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 52, 'output_tokens': 19}}, id='run-b2eb9137-2f4e-446f-95f5-3d5f621a2cf8-0', usage_metadata={'input_tokens': 52, 'output_tokens': 19, 'total_tokens': 71})]"
]
},
"execution_count": 11,
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"messages = app.get_state(config).values['messages']\n",
"messages = app.get_state(config).values[\"messages\"]\n",
"messages"
]
},
@@ -323,7 +340,7 @@
},
{
"cell_type": "code",
"execution_count": 14,
"execution_count": 9,
"id": "bb22ede0-e153-4fd0-a4c0-f9af2f7663b1",
"metadata": {},
"outputs": [],
@@ -333,10 +350,11 @@
"\n",
"\n",
"def delete_messages(state):\n",
" messages = state['messages']\n",
" messages = state[\"messages\"]\n",
" if len(messages) > 3:\n",
" return {\"messages\": [RemoveMessage(id=m.id) for m in messages[:-3]]}\n",
"\n",
"\n",
"# We need to modify the logic to call delete_messages rather than end right away\n",
"def should_continue(state: MessagesState) -> Literal[\"action\", \"delete_messages\"]:\n",
" \"\"\"Return the next node to execute.\"\"\"\n",
@@ -358,7 +376,10 @@
"\n",
"\n",
"workflow.add_edge(START, \"agent\")\n",
"workflow.add_conditional_edges(\"agent\", should_continue,)\n",
"workflow.add_conditional_edges(\n",
" \"agent\",\n",
" should_continue,\n",
")\n",
"workflow.add_edge(\"action\", \"agent\")\n",
"\n",
"# This is the new edge we're adding: after we delete messages, we finish\n",
@@ -376,7 +397,7 @@
},
{
"cell_type": "code",
"execution_count": 16,
"execution_count": 10,
"id": "3975f34c-c243-40ea-b9d2-424d50a48dc9",
"metadata": {},
"outputs": [
@@ -384,21 +405,11 @@
"name": "stdout",
"output_type": "stream",
"text": [
"================================\u001b[1m Human Message \u001b[0m=================================\n",
"\n",
"hi! I'm bob\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"\n",
"It's nice to meet you, Bob! How can I assist you today?\n",
"================================\u001b[1m Human Message \u001b[0m=================================\n",
"\n",
"what's my name?\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"\n",
"You said your name is Bob.\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"\n",
"You said your name is Bob.\n"
"[('human', \"hi! I'm bob\")]\n",
"[('human', \"hi! I'm bob\"), ('ai', \"Hello Bob! It's nice to meet you. I'm an AI assistant created by Anthropic. I'm here to help with any questions or tasks you might have. Please let me know how I can assist you.\")]\n",
"[('human', \"hi! I'm bob\"), ('ai', \"Hello Bob! It's nice to meet you. I'm an AI assistant created by Anthropic. I'm here to help with any questions or tasks you might have. Please let me know how I can assist you.\"), ('human', \"what's my name?\")]\n",
"[('human', \"hi! I'm bob\"), ('ai', \"Hello Bob! It's nice to meet you. I'm an AI assistant created by Anthropic. I'm here to help with any questions or tasks you might have. Please let me know how I can assist you.\"), ('human', \"what's my name?\"), ('ai', 'You said your name is Bob, so that is the name I have for you.')]\n",
"[('ai', \"Hello Bob! It's nice to meet you. I'm an AI assistant created by Anthropic. I'm here to help with any questions or tasks you might have. Please let me know how I can assist you.\"), ('human', \"what's my name?\"), ('ai', 'You said your name is Bob, so that is the name I have for you.')]\n"
]
}
],
@@ -408,12 +419,12 @@
"config = {\"configurable\": {\"thread_id\": \"3\"}}\n",
"input_message = HumanMessage(content=\"hi! I'm bob\")\n",
"for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n",
" event[\"messages\"][-1].pretty_print()\n",
" print([(message.type, message.content) for message in event[\"messages\"]])\n",
"\n",
"\n",
"input_message = HumanMessage(content=\"what's my name?\")\n",
"for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n",
" event[\"messages\"][-1].pretty_print()"
" print([(message.type, message.content) for message in event[\"messages\"]])"
]
},
{
@@ -426,25 +437,25 @@
},
{
"cell_type": "code",
"execution_count": 17,
"execution_count": 11,
"id": "a3e15abb-81d8-4072-9f10-61ae0fd61dac",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[AIMessage(content=\"It's nice to meet you, Bob! How can I assist you today?\", response_metadata={'id': 'msg_01QMoxepDiCcKQ6XFgge1QQT', 'model': 'claude-3-haiku-20240307', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 12, 'output_tokens': 19}}, id='run-de13ba05-095d-4fd1-907a-6766ef3bf57b-0'),\n",
" HumanMessage(content=\"what's my name?\", id='8292e725-8fc4-487e-a9b6-75f8b136bec2'),\n",
" AIMessage(content='You said your name is Bob.', response_metadata={'id': 'msg_01DfWfaxavdMCqtoQRmC3mc4', 'model': 'claude-3-haiku-20240307', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 39, 'output_tokens': 10}}, id='run-28167c82-e126-47e4-854c-623e50c8af22-0')]"
"[AIMessage(content=\"Hello Bob! It's nice to meet you. I'm an AI assistant created by Anthropic. I'm here to help with any questions or tasks you might have. Please let me know how I can assist you.\", response_metadata={'id': 'msg_01XPEgPPbcnz5BbGWUDWTmzG', 'model': 'claude-3-haiku-20240307', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 12, 'output_tokens': 48}}, id='run-eded3820-b6a9-4d66-9210-03ca41787ce6-0', usage_metadata={'input_tokens': 12, 'output_tokens': 48, 'total_tokens': 60}),\n",
" HumanMessage(content=\"what's my name?\", id='a0ea2097-3280-402b-92e1-67177b807ae8'),\n",
" AIMessage(content='You said your name is Bob, so that is the name I have for you.', response_metadata={'id': 'msg_01JGT62pxhrhN4SykZ57CSjW', 'model': 'claude-3-haiku-20240307', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 68, 'output_tokens': 20}}, id='run-ace3519c-81f8-45fe-a777-91f42d48b3a3-0', usage_metadata={'input_tokens': 68, 'output_tokens': 20, 'total_tokens': 88})]"
]
},
"execution_count": 17,
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"messages = app.get_state(config).values['messages']\n",
"messages = app.get_state(config).values[\"messages\"]\n",
"messages"
]
},
@@ -467,9 +478,9 @@
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"display_name": "langgraph",
"language": "python",
"name": "python3"
"name": "langgraph"
},
"language_info": {
"codemirror_mode": {
@@ -481,7 +492,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.1"
"version": "3.11.9"
}
},
"nbformat": 4,
@@ -138,7 +138,7 @@
"\n",
"# Define the function that calls the model\n",
"def call_model(state: MessagesState):\n",
" response = model.invoke(state[\"messages\"])\n",
" response = bound_model.invoke(state[\"messages\"])\n",
" # We return a list, because this will get added to the existing list\n",
" return {\"messages\": response}\n",
"\n",
@@ -275,7 +275,7 @@
"# Define the function that calls the model\n",
"def call_model(state: MessagesState):\n",
" messages = filter_messages(state[\"messages\"])\n",
" response = model.invoke(messages)\n",
" response = bound_model.invoke(messages)\n",
" # We return a list, because this will get added to the existing list\n",
" return {\"messages\": response}\n",
"\n",
Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 193 KiB

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 25 KiB

+120
View File
@@ -0,0 +1,120 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# How to add node retry policies\n",
"\n",
"There are many use cases where you may wish for your node to have a custom retry policy, for example if you are calling an API, querying a database, or calling an LLM, etc. \n",
"\n",
"In order to configure the retry policy, you have to pass the `retry` parameter to the `add_node` function. The `retry` parameter takes in a `RetryPolicy` named tuple object. Below we instantiate a `RetryPolicy` object with the default parameters:"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"RetryPolicy(initial_interval=0.5, backoff_factor=2.0, max_interval=128.0, max_attempts=3, jitter=True, retry_on=<function default_retry_on at 0x1157419e0>)"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from langgraph.pregel import RetryPolicy\n",
"\n",
"RetryPolicy()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you want more information on what each of the parameters does, be sure to read the [reference](https://langchain-ai.github.io/langgraph/reference/graphs/#retrypolicy).\n",
"\n",
"## Passing a retry policy to a node\n",
"\n",
"Lastly, we can pass `RetryPolicy` objects when we call the `add_node` function. In the example below we pass two different retry policies to each of our nodes:"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [],
"source": [
"import operator\n",
"import sqlite3\n",
"from typing import Annotated, Sequence, TypedDict\n",
"\n",
"from langchain_anthropic import ChatAnthropic\n",
"from langchain_core.messages import BaseMessage\n",
"\n",
"from langgraph.graph import END, StateGraph, START\n",
"from langchain_community.utilities import SQLDatabase\n",
"from langchain_core.messages import AIMessage\n",
"\n",
"db = SQLDatabase.from_uri(\"sqlite:///:memory:\")\n",
"\n",
"model = ChatAnthropic(model_name=\"claude-2.1\")\n",
"\n",
"\n",
"class AgentState(TypedDict):\n",
" messages: Annotated[Sequence[BaseMessage], operator.add]\n",
"\n",
"\n",
"def query_database(state):\n",
" query_result = db.run(\"SELECT * FROM Artist LIMIT 10;\")\n",
" return {\"messages\": [AIMessage(content=query_result)]}\n",
"\n",
"\n",
"def call_model(state):\n",
" response = model.invoke(state[\"messages\"])\n",
" return {\"messages\": [response]}\n",
"\n",
"\n",
"# Define a new graph\n",
"workflow = StateGraph(AgentState)\n",
"workflow.add_node(\n",
" \"query_database\",\n",
" query_database,\n",
" retry=RetryPolicy(retry_on=sqlite3.OperationalError),\n",
")\n",
"workflow.add_node(\"model\", call_model, retry=RetryPolicy(max_attempts=5))\n",
"workflow.add_edge(START, \"model\")\n",
"workflow.add_edge(\"model\", \"query_database\")\n",
"workflow.add_edge(\"query_database\", END)\n",
"\n",
"app = workflow.compile()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "env",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.9"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+126
View File
@@ -0,0 +1,126 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "47ed5db3-bda5-49e1-bf75-23e08c9a3af0",
"metadata": {},
"source": [
"# How to pass private state\n",
"\n",
"Oftentimes, you may want nodes to be able to pass state to each other that should NOT be part of the main schema of the graph. This is often useful because there may be information that is not needed as input/output (and therefore doesn't really make sense to have in the main schema) but is ABSOLUTELY needed as part of the intermediate working logic.\n",
"\n",
"Let's take a look at an example below. In this example, we will create a RAG pipeline that:\n",
"1. Takes in a user question\n",
"2. Uses an LLM to generate a search query\n",
"3. Retrieves documents for that generated query\n",
"4. Generates a final answer based on those documents\n",
"\n",
"We will have a separate node for each step. We will only have the `question` and `answer` on the overall state. However, we will need separate states for the `search_query` and the `documents` - we will pass these as private state keys.\n",
"\n",
"Let's look at an example!"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "3114c3ad-0ade-47ba-9488-53d6f7671578",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'question': 'foo', 'answer': 'fo\\n\\nfo\\n\\nfoo'}"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from langgraph.graph import StateGraph, START, END\n",
"from typing import TypedDict\n",
"\n",
"\n",
"# The overall state of the graph\n",
"class OverallState(TypedDict):\n",
" question: str\n",
" answer: str\n",
"\n",
"\n",
"# This is what the node that generates the query will return\n",
"class QueryOutputState(TypedDict):\n",
" query: str\n",
"\n",
"\n",
"# This is what the node that retrieves the documents will return\n",
"class DocumentOutputState(TypedDict):\n",
" docs: list[str]\n",
"\n",
"\n",
"# This is what the node that generates the final answer will take in\n",
"class GenerateInputState(OverallState, DocumentOutputState):\n",
" pass\n",
"\n",
"\n",
"# Node to generate query\n",
"def generate_query(state: OverallState) -> QueryOutputState:\n",
" # Replace this with real logic\n",
" return {\"query\": state[\"question\"][:2]}\n",
"\n",
"\n",
"# Node to retrieve documents\n",
"def retrieve_documents(state: QueryOutputState) -> DocumentOutputState:\n",
" # Replace this with real logic\n",
" return {\"docs\": [state['query']] * 2}\n",
"\n",
"\n",
"# Node to generate answer\n",
"def generate(state: GenerateInputState) -> OverallState:\n",
" return {\"answer\": \"\\n\\n\".join(state['docs'] + [state['question']])}\n",
"\n",
"\n",
"graph = StateGraph(OverallState)\n",
"graph.add_node(generate_query)\n",
"graph.add_node(retrieve_documents)\n",
"graph.add_node(generate)\n",
"graph.add_edge(START, \"generate_query\")\n",
"graph.add_edge(\"generate_query\", \"retrieve_documents\")\n",
"graph.add_edge(\"retrieve_documents\", \"generate\")\n",
"graph.add_edge(\"generate\", END)\n",
"graph = graph.compile()\n",
"\n",
"graph.invoke({\"question\": \"foo\"})"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3ffc2d8c-717f-42c9-b0aa-15b178a5cc8b",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.1"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
+579 -428
View File
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 354 KiB

+1 -1
View File
@@ -252,7 +252,7 @@
"id": "5324ea49-5745-47b5-a0a5-bf58c8babe46",
"metadata": {},
"outputs": [],
"source": ["### Nodes\n\n\ndef retrieve(state):\n \"\"\"\n Retrieve documents\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): New key added to state, documents, that contains retrieved documents\n \"\"\"\n print(\"---RETRIEVE---\")\n question = state[\"question\"]\n\n # Retrieval\n documents = retriever.get_relevant_documents(question)\n return {\"documents\": documents, \"question\": question}\n\n\ndef generate(state):\n \"\"\"\n Generate answer\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): New key added to state, generation, that contains LLM generation\n \"\"\"\n print(\"---GENERATE---\")\n question = state[\"question\"]\n documents = state[\"documents\"]\n\n # RAG generation\n generation = rag_chain.invoke({\"context\": documents, \"question\": question})\n return {\"documents\": documents, \"question\": question, \"generation\": generation}\n\n\ndef grade_documents(state):\n \"\"\"\n Determines whether the retrieved documents are relevant to the question.\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): Updates documents key with only filtered relevant documents\n \"\"\"\n\n print(\"---CHECK DOCUMENT RELEVANCE TO QUESTION---\")\n question = state[\"question\"]\n documents = state[\"documents\"]\n\n # Score each doc\n filtered_docs = []\n for d in documents:\n score = retrieval_grader.invoke(\n {\"question\": question, \"document\": d.page_content}\n )\n grade = score[\"score\"]\n if grade == \"yes\":\n print(\"---GRADE: DOCUMENT RELEVANT---\")\n filtered_docs.append(d)\n else:\n print(\"---GRADE: DOCUMENT NOT RELEVANT---\")\n continue\n return {\"documents\": filtered_docs, \"question\": question}\n\n\ndef transform_query(state):\n \"\"\"\n Transform the query to produce a better question.\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): Updates question key with a re-phrased question\n \"\"\"\n\n print(\"---TRANSFORM QUERY---\")\n question = state[\"question\"]\n documents = state[\"documents\"]\n\n # Re-write question\n better_question = question_rewriter.invoke({\"question\": question})\n return {\"documents\": documents, \"question\": better_question}\n\n\n### Edges\n\n\ndef decide_to_generate(state):\n \"\"\"\n Determines whether to generate an answer, or re-generate a question.\n\n Args:\n state (dict): The current graph state\n\n Returns:\n str: Binary decision for next node to call\n \"\"\"\n\n print(\"---ASSESS GRADED DOCUMENTS---\")\n state[\"question\"]\n filtered_documents = state[\"documents\"]\n\n if not filtered_documents:\n # All documents have been filtered check_relevance\n # We will re-generate a new query\n print(\n \"---DECISION: ALL DOCUMENTS ARE NOT RELEVANT TO QUESTION, TRANSFORM QUERY---\"\n )\n return \"transform_query\"\n else:\n # We have relevant documents, so generate answer\n print(\"---DECISION: GENERATE---\")\n return \"generate\"\n\n\ndef grade_generation_v_documents_and_question(state):\n \"\"\"\n Determines whether the generation is grounded in the document and answers question.\n\n Args:\n state (dict): The current graph state\n\n Returns:\n str: Decision for next node to call\n \"\"\"\n\n print(\"---CHECK HALLUCINATIONS---\")\n question = state[\"question\"]\n documents = state[\"documents\"]\n generation = state[\"generation\"]\n\n score = hallucination_grader.invoke(\n {\"documents\": documents, \"generation\": generation}\n )\n grade = score[\"score\"]\n\n # Check hallucination\n if grade == \"yes\":\n print(\"---DECISION: GENERATION IS GROUNDED IN DOCUMENTS---\")\n # Check question-answering\n print(\"---GRADE GENERATION vs QUESTION---\")\n score = answer_grader.invoke({\"question\": question, \"generation\": generation})\n grade = score[\"score\"]\n if grade == \"yes\":\n print(\"---DECISION: GENERATION ADDRESSES QUESTION---\")\n return \"useful\"\n else:\n print(\"---DECISION: GENERATION DOES NOT ADDRESS QUESTION---\")\n return \"not useful\"\n else:\n pprint(\"---DECISION: GENERATION IS NOT GROUNDED IN DOCUMENTS, RE-TRY---\")\n return \"not supported\""]
"source": ["### Nodes\n\n\ndef retrieve(state):\n \"\"\"\n Retrieve documents\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): New key added to state, documents, that contains retrieved documents\n \"\"\"\n print(\"---RETRIEVE---\")\n question = state[\"question\"]\n\n # Retrieval\n documents = retriever.get_relevant_documents(question)\n return {\"documents\": documents, \"question\": question}\n\n\ndef generate(state):\n \"\"\"\n Generate answer\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): New key added to state, generation, that contains LLM generation\n \"\"\"\n print(\"---GENERATE---\")\n question = state[\"question\"]\n documents = state[\"documents\"]\n\n # RAG generation\n generation = rag_chain.invoke({\"context\": documents, \"question\": question})\n return {\"documents\": documents, \"question\": question, \"generation\": generation}\n\n\ndef grade_documents(state):\n \"\"\"\n Determines whether the retrieved documents are relevant to the question.\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): Updates documents key with only filtered relevant documents\n \"\"\"\n\n print(\"---CHECK DOCUMENT RELEVANCE TO QUESTION---\")\n question = state[\"question\"]\n documents = state[\"documents\"]\n\n # Score each doc\n filtered_docs = []\n for d in documents:\n score = retrieval_grader.invoke(\n {\"question\": question, \"document\": d.page_content}\n )\n grade = score[\"score\"]\n if grade == \"yes\":\n print(\"---GRADE: DOCUMENT RELEVANT---\")\n filtered_docs.append(d)\n else:\n print(\"---GRADE: DOCUMENT NOT RELEVANT---\")\n continue\n return {\"documents\": filtered_docs, \"question\": question}\n\n\ndef transform_query(state):\n \"\"\"\n Transform the query to produce a better question.\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): Updates question key with a re-phrased question\n \"\"\"\n\n print(\"---TRANSFORM QUERY---\")\n question = state[\"question\"]\n documents = state[\"documents\"]\n\n # Re-write question\n better_question = question_rewriter.invoke({\"question\": question})\n return {\"documents\": documents, \"question\": better_question}\n\n\n### Edges\n\n\ndef decide_to_generate(state):\n \"\"\"\n Determines whether to generate an answer, or re-generate a question.\n\n Args:\n state (dict): The current graph state\n\n Returns:\n str: Binary decision for next node to call\n \"\"\"\n\n print(\"---ASSESS GRADED DOCUMENTS---\")\n state[\"question\"]\n filtered_documents = state[\"documents\"]\n\n if not filtered_documents:\n # All documents have been filtered check_relevance\n # We will re-generate a new query\n print(\n \"---DECISION: ALL DOCUMENTS ARE NOT RELEVANT TO QUESTION, TRANSFORM QUERY---\"\n )\n return \"transform_query\"\n else:\n # We have relevant documents, so generate answer\n print(\"---DECISION: GENERATE---\")\n return \"generate\"\n\n\ndef grade_generation_v_documents_and_question(state):\n \"\"\"\n Determines whether the generation is grounded in the document and answers question.\n\n Args:\n state (dict): The current graph state\n\n Returns:\n str: Decision for next node to call\n \"\"\"\n\n print(\"---CHECK HALLUCINATIONS---\")\n question = state[\"question\"]\n documents = state[\"documents\"]\n generation = state[\"generation\"]\n\n score = hallucination_grader.invoke(\n {\"documents\": documents, \"generation\": generation}\n )\n grade = score[\"score\"]\n\n # Check hallucination\n if grade == \"yes\":\n print(\"---DECISION: GENERATION IS GROUNDED IN DOCUMENTS---\")\n # Check question-answering\n print(\"---GRADE GENERATION vs QUESTION---\")\n score = answer_grader.invoke({\"question\": question, \"generation\": generation})\n grade = score[\"score\"]\n if grade == \"yes\":\n print(\"---DECISION: GENERATION ADDRESSES QUESTION---\")\n return \"useful\"\n else:\n print(\"---DECISION: GENERATION DOES NOT ADDRESS QUESTION---\")\n return \"not useful\"\n else:\n print(\"---DECISION: GENERATION IS NOT GROUNDED IN DOCUMENTS, RE-TRY---\")\n return \"not supported\""]
},
{
"cell_type": "markdown",
Binary file not shown.

Before

Width:  |  Height:  |  Size: 914 KiB

After

Width:  |  Height:  |  Size: 301 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1003 KiB

After

Width:  |  Height:  |  Size: 345 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 234 KiB

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 829 KiB

After

Width:  |  Height:  |  Size: 341 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

After

Width:  |  Height:  |  Size: 914 KiB

+983 -899
View File
File diff suppressed because one or more lines are too long
@@ -42,7 +42,7 @@
"metadata": {},
"outputs": [
{
"name": "stdout",
"name": "stdin",
"output_type": "stream",
"text": [
"OPENAI_API_KEY: ········\n"
@@ -80,7 +80,7 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 3,
"id": "d59234f9-173e-469d-a725-c13e0979663e",
"metadata": {},
"outputs": [],
@@ -88,40 +88,36 @@
"from openai import AsyncOpenAI\n",
"from langchain_core.language_models.chat_models import ChatGenerationChunk\n",
"from langchain_core.messages import AIMessageChunk\n",
"from langchain_core.runnables.config import ensure_config, get_callback_manager_for_config\n",
"from langchain_core.runnables.config import (\n",
" ensure_config,\n",
" get_callback_manager_for_config,\n",
")\n",
"\n",
"openai_client = AsyncOpenAI()\n",
"# define tool schema for openai tool calling\n",
"\n",
"tool = {\n",
" \"type\": \"function\",\n",
" \"function\": {\n",
" \"name\": \"get_items\",\n",
" \"description\": \"Use this tool to look up which items are in the given place.\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"place\": {\n",
" \"type\": \"string\"\n",
" }\n",
" },\n",
" \"required\": [\n",
" \"place\"\n",
" ]\n",
" }\n",
" }\n",
" \"type\": \"function\",\n",
" \"function\": {\n",
" \"name\": \"get_items\",\n",
" \"description\": \"Use this tool to look up which items are in the given place.\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\"place\": {\"type\": \"string\"}},\n",
" \"required\": [\"place\"],\n",
" },\n",
" },\n",
"}\n",
"\n",
"\n",
"async def call_model(state, config=None):\n",
" config = ensure_config(config | {\"tags\": [\"agent_llm\"]})\n",
" callback_manager = get_callback_manager_for_config(config)\n",
" messages = state[\"messages\"]\n",
" \n",
"\n",
" llm_run_manager = callback_manager.on_chat_model_start({}, [messages])[0]\n",
" response = await openai_client.chat.completions.create(\n",
" messages=messages,\n",
" model=\"gpt-3.5-turbo\",\n",
" tools=[tool],\n",
" stream=True\n",
" messages=messages, model=\"gpt-3.5-turbo\", tools=[tool], stream=True\n",
" )\n",
"\n",
" response_content = \"\"\n",
@@ -147,7 +143,10 @@
"\n",
" # note: we're wrapping the tools calls in ChatGenerationChunk so that the events from .astream_events in the graph can render tool calls correctly\n",
" tool_call_chunk = ChatGenerationChunk(\n",
" message=AIMessageChunk(content=\"\", additional_kwargs={\"tool_calls\": [delta.tool_calls[0].dict()]})\n",
" message=AIMessageChunk(\n",
" content=\"\",\n",
" additional_kwargs={\"tool_calls\": [delta.tool_calls[0].dict()]},\n",
" )\n",
" )\n",
" llm_run_manager.on_llm_new_token(\"\", chunk=tool_call_chunk)\n",
" tool_call_function_arguments += delta.tool_calls[0].function.arguments\n",
@@ -156,8 +155,11 @@
" tool_calls = [\n",
" {\n",
" \"id\": tool_call_id,\n",
" \"function\": {\"name\": tool_call_function_name, \"arguments\": tool_call_function_arguments},\n",
" \"type\": \"function\"\n",
" \"function\": {\n",
" \"name\": tool_call_function_name,\n",
" \"arguments\": tool_call_function_arguments,\n",
" },\n",
" \"type\": \"function\",\n",
" }\n",
" ]\n",
" else:\n",
@@ -166,7 +168,7 @@
" response_message = {\n",
" \"role\": role,\n",
" \"content\": response_content,\n",
" \"tool_calls\": tool_calls\n",
" \"tool_calls\": tool_calls,\n",
" }\n",
" return {\"messages\": [response_message]}"
]
@@ -181,18 +183,17 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 4,
"id": "b90941d8-afe4-42ec-9262-9c3b87c3b1ec",
"metadata": {},
"outputs": [],
"source": [
"import json\n",
"from langchain_core.runnables import RunnableLambda\n",
"from langchain_core.callbacks import adispatch_custom_event\n",
"\n",
"\n",
"async def get_items(place: str) -> str:\n",
" \"\"\"Use this tool to look up which items are in the given place.\"\"\"\n",
" # NOTE: we need to define a special langchain runnable that we'll be using for logging the streaming outputs from within a tool\n",
" tool_logger = RunnableLambda(lambda inputs: inputs).with_config({\"tags\": [\"tool_call\"]})\n",
"\n",
" # this can be replaced with any actual streaming logic that you might have\n",
" def stream(place: str):\n",
@@ -205,15 +206,25 @@
"\n",
" tokens = []\n",
" for token in stream(place):\n",
" tool_logger.invoke(token)\n",
" await adispatch_custom_event(\n",
" # this will allow you to filter events by name\n",
" \"tool_call_token_stream\",\n",
" {\n",
" \"function_name\": \"get_items\",\n",
" \"arguments\": {\"place\": place},\n",
" \"tool_output_token\": token,\n",
" },\n",
" # this will allow you to filter events by tags\n",
" config={\"tags\": [\"tool_call\"]},\n",
" )\n",
" tokens.append(token)\n",
"\n",
" return \", \".join(tokens)\n",
"\n",
"\n",
"# define mapping to look up functions when running tools\n",
"function_name_to_function = {\n",
" \"get_items\": get_items\n",
"}\n",
"function_name_to_function = {\"get_items\": get_items}\n",
"\n",
"\n",
"async def call_tools(state):\n",
" messages = state[\"messages\"]\n",
@@ -222,17 +233,15 @@
" function_name = tool_call[\"function\"][\"name\"]\n",
" function_arguments = tool_call[\"function\"][\"arguments\"]\n",
" arguments = json.loads(function_arguments)\n",
" \n",
" function_response = await function_name_to_function[function_name](**arguments) \n",
"\n",
" function_response = await function_name_to_function[function_name](**arguments)\n",
" tool_message = {\n",
" \"tool_call_id\": tool_call[\"id\"],\n",
" \"role\": \"tool\",\n",
" \"name\": function_name,\n",
" \"content\": function_response,\n",
" }\n",
" return {\n",
" \"messages\": [tool_message]\n",
" }"
" return {\"messages\": [tool_message]}"
]
},
{
@@ -245,7 +254,7 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 5,
"id": "228260be-1f9a-4195-80e0-9604f8a5dba6",
"metadata": {},
"outputs": [],
@@ -255,16 +264,19 @@
"\n",
"from langgraph.graph import StateGraph, END\n",
"\n",
"\n",
"class State(TypedDict):\n",
" messages: Annotated[list, operator.add]\n",
"\n",
"\n",
"def should_continue(state) -> Literal[\"tools\", END]:\n",
" messages = state['messages']\n",
" messages = state[\"messages\"]\n",
" last_message = messages[-1]\n",
" if last_message[\"tool_calls\"]:\n",
" return \"tools\"\n",
" return END\n",
"\n",
"\n",
"workflow = StateGraph(State)\n",
"workflow.set_entry_point(\"model\")\n",
"workflow.add_node(\"model\", call_model) # i.e. our \"agent\"\n",
@@ -284,10 +296,18 @@
},
{
"cell_type": "code",
"execution_count": 16,
"execution_count": 6,
"id": "45c96a79-4147-42e3-89fd-d942b2b49f6c",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/Users/vadymbarda/.virtualenvs/langgraph/lib/python3.11/site-packages/langchain_core/_api/beta_decorator.py:87: LangChainBetaWarning: This API is in beta and may change in the future.\n",
" warn_beta(\n"
]
},
{
"name": "stdout",
"output_type": "stream",
@@ -299,10 +319,12 @@
}
],
"source": [
"async for event in graph.astream_events({\"messages\": [{\"role\": \"user\", \"content\": \"what's in the bedroom\"}]}, version=\"v2\"):\n",
"async for event in graph.astream_events(\n",
" {\"messages\": [{\"role\": \"user\", \"content\": \"what's in the bedroom\"}]}, version=\"v2\"\n",
"):\n",
" tags = event.get(\"tags\", [])\n",
" if event[\"event\"] == \"on_chain_end\" and \"tool_call\" in tags:\n",
" print(\"Tool token\", event[\"data\"][\"output\"])"
" if event[\"event\"] == \"on_custom_event\" and \"tool_call\" in tags:\n",
" print(\"Tool token\", event[\"data\"][\"tool_output_token\"])"
]
}
],
@@ -88,40 +88,36 @@
"from openai import AsyncOpenAI\n",
"from langchain_core.language_models.chat_models import ChatGenerationChunk\n",
"from langchain_core.messages import AIMessageChunk\n",
"from langchain_core.runnables.config import ensure_config, get_callback_manager_for_config\n",
"from langchain_core.runnables.config import (\n",
" ensure_config,\n",
" get_callback_manager_for_config,\n",
")\n",
"\n",
"openai_client = AsyncOpenAI()\n",
"# define tool schema for openai tool calling\n",
"\n",
"tool = {\n",
" \"type\": \"function\",\n",
" \"function\": {\n",
" \"name\": \"get_items\",\n",
" \"description\": \"Use this tool to look up which items are in the given place.\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"place\": {\n",
" \"type\": \"string\"\n",
" }\n",
" },\n",
" \"required\": [\n",
" \"place\"\n",
" ]\n",
" }\n",
" }\n",
" \"type\": \"function\",\n",
" \"function\": {\n",
" \"name\": \"get_items\",\n",
" \"description\": \"Use this tool to look up which items are in the given place.\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\"place\": {\"type\": \"string\"}},\n",
" \"required\": [\"place\"],\n",
" },\n",
" },\n",
"}\n",
"\n",
"\n",
"async def call_model(state, config=None):\n",
" config = ensure_config(config | {\"tags\": [\"agent_llm\"]})\n",
" callback_manager = get_callback_manager_for_config(config)\n",
" messages = state[\"messages\"]\n",
" \n",
"\n",
" llm_run_manager = callback_manager.on_chat_model_start({}, [messages])[0]\n",
" response = await openai_client.chat.completions.create(\n",
" messages=messages,\n",
" model=\"gpt-3.5-turbo\",\n",
" tools=[tool],\n",
" stream=True\n",
" messages=messages, model=\"gpt-3.5-turbo\", tools=[tool], stream=True\n",
" )\n",
"\n",
" response_content = \"\"\n",
@@ -147,7 +143,10 @@
"\n",
" # note: we're wrapping the tools calls in ChatGenerationChunk so that the events from .astream_events in the graph can render tool calls correctly\n",
" tool_call_chunk = ChatGenerationChunk(\n",
" message=AIMessageChunk(content=\"\", additional_kwargs={\"tool_calls\": [delta.tool_calls[0].dict()]})\n",
" message=AIMessageChunk(\n",
" content=\"\",\n",
" additional_kwargs={\"tool_calls\": [delta.tool_calls[0].dict()]},\n",
" )\n",
" )\n",
" llm_run_manager.on_llm_new_token(\"\", chunk=tool_call_chunk)\n",
" tool_call_function_arguments += delta.tool_calls[0].function.arguments\n",
@@ -156,8 +155,11 @@
" tool_calls = [\n",
" {\n",
" \"id\": tool_call_id,\n",
" \"function\": {\"name\": tool_call_function_name, \"arguments\": tool_call_function_arguments},\n",
" \"type\": \"function\"\n",
" \"function\": {\n",
" \"name\": tool_call_function_name,\n",
" \"arguments\": tool_call_function_arguments,\n",
" },\n",
" \"type\": \"function\",\n",
" }\n",
" ]\n",
" else:\n",
@@ -166,7 +168,7 @@
" response_message = {\n",
" \"role\": role,\n",
" \"content\": response_content,\n",
" \"tool_calls\": tool_calls\n",
" \"tool_calls\": tool_calls,\n",
" }\n",
" return {\"messages\": [response_message]}"
]
@@ -188,6 +190,7 @@
"source": [
"import json\n",
"\n",
"\n",
"async def get_items(place: str) -> str:\n",
" \"\"\"Use this tool to look up which items are in the given place.\"\"\"\n",
" if \"bed\" in place: # For under the bed\n",
@@ -197,10 +200,10 @@
" else: # if the agent decides to ask about a different place\n",
" return \"cat snacks\"\n",
"\n",
"\n",
"# define mapping to look up functions when running tools\n",
"function_name_to_function = {\n",
" \"get_items\": get_items\n",
"}\n",
"function_name_to_function = {\"get_items\": get_items}\n",
"\n",
"\n",
"async def call_tools(state):\n",
" messages = state[\"messages\"]\n",
@@ -209,17 +212,15 @@
" function_name = tool_call[\"function\"][\"name\"]\n",
" function_arguments = tool_call[\"function\"][\"arguments\"]\n",
" arguments = json.loads(function_arguments)\n",
" \n",
" function_response = await function_name_to_function[function_name](**arguments) \n",
"\n",
" function_response = await function_name_to_function[function_name](**arguments)\n",
" tool_message = {\n",
" \"tool_call_id\": tool_call[\"id\"],\n",
" \"role\": \"tool\",\n",
" \"name\": function_name,\n",
" \"content\": function_response,\n",
" }\n",
" return {\n",
" \"messages\": [tool_message]\n",
" }"
" return {\"messages\": [tool_message]}"
]
},
{
@@ -242,16 +243,19 @@
"\n",
"from langgraph.graph import StateGraph, END\n",
"\n",
"\n",
"class State(TypedDict):\n",
" messages: Annotated[list, operator.add]\n",
"\n",
"\n",
"def should_continue(state) -> Literal[\"tools\", END]:\n",
" messages = state['messages']\n",
" messages = state[\"messages\"]\n",
" last_message = messages[-1]\n",
" if last_message[\"tool_calls\"]:\n",
" return \"tools\"\n",
" return END\n",
"\n",
"\n",
"workflow = StateGraph(State)\n",
"workflow.set_entry_point(\"model\")\n",
"workflow.add_node(\"model\", call_model) # i.e. our \"agent\"\n",
@@ -325,7 +329,9 @@
}
],
"source": [
"async for event in graph.astream_events({\"messages\": [{\"role\": \"user\", \"content\": \"what's in the bedroom\"}]}, version=\"v2\"):\n",
"async for event in graph.astream_events(\n",
" {\"messages\": [{\"role\": \"user\", \"content\": \"what's in the bedroom\"}]}, version=\"v2\"\n",
"):\n",
" tags = event.get(\"tags\", [])\n",
" if event[\"event\"] == \"on_chat_model_stream\" and \"agent_llm\" in tags:\n",
" print(\"LLM token\", event[\"data\"][\"chunk\"].dict())"
+674 -463
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long

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