Compare commits

..
Author SHA1 Message Date
Vadym BardaandGitHub d45253cee8 checkpoint: release libraries (#3412) 2025-02-12 22:44:44 -05:00
Vadym BardaandGitHub 1377e3b6ba checkpoint: combine metadata when writing checkpoints (#3404) 2025-02-13 03:24:41 +00:00
148cf52981 chore(docs): notebook convert script + 1 conversion (#3390)
* Adds a conversion script from ipython notebook to markdown.
* Replaces one ipython notebook (create react agent) with a markdown file for testing.

---------

Co-authored-by: Ben Burns <803016+benjamincburns@users.noreply.github.com>
2025-02-13 02:38:15 +00:00
Eugene YurtsevandGitHub 9010303245 docs: add update prebuilt on docs deploy (#3410)
logic executed on every PR which involves a network request per package
to download stats from pypi.
2025-02-12 20:59:15 -05:00
3397d8908f docs: adding Breeze Agent for third party agent page (#3352)
Adding open source web researcher agent to the third party page

---------

Co-authored-by: Eugene Yurtsev <eyurtsev@gmail.com>
Co-authored-by: Eugene Yurtsev <eugene@langchain.dev>
2025-02-12 20:15:22 -05:00
Nuno Campos e005f0472b sdk-js 0.0.40 2025-02-12 16:53:02 -08:00
David DuongandGitHub 18a1d60e45 feat(sdk-js): further 2x improvement when SSE (#3408) 2025-02-12 16:09:18 -08:00
Tat Dat Duong 5091d5e9fe Remove ts-node 2025-02-12 15:56:01 -08:00
Tat Dat Duong da1fae0c72 Cleanup 2025-02-12 15:55:10 -08:00
Tat Dat Duong 7db81d72dc Cleanup 2025-02-12 15:55:10 -08:00
Tat Dat Duong 4ce387ee28 test 2025-02-12 15:55:09 -08:00
Nuno CamposandTat Dat Duong b902db686d Speed up! 2025-02-12 15:55:07 -08:00
Nuno CamposandGitHub 16be48079e fix(sdk-js): improve SSE parsing performance by 10x (#3401) 2025-02-12 10:08:46 -08:00
Eugene YurtsevandGitHub cf3a9ad63e docs: add timeout to docs delpoy workflow (#3402) 2025-02-12 13:04:06 -05:00
David DuongandGitHub 547830ef24 feat(sdk-js): add experimental useStream hook (#3361)
Experimental `useStream` React hook to make streaming as simple as
possible. Supports branching / forking, message-tuple stream mode for
rendering messages.

Usage: https://github.com/langchain-ai/langchain-nextjs-template/pull/62
2025-02-12 09:39:02 -08:00
Tat Dat Duong d333e52fce Simplify types for additional kwargs 2025-02-12 09:16:07 -08:00
Tat Dat Duong 15fe44ddf8 Update deps 2025-02-12 09:13:10 -08:00
Tat Dat Duong b6aff521e5 Make event listeners a callback 2025-02-12 09:11:44 -08:00
Eugene YurtsevandGitHub 35ff9f5211 docs: add script to add typescript translations (#3394)
A script to add typescript translations. We can improve by automatically
adding markdown tabs appropriately and parallelizing the llm calls
2025-02-12 12:07:05 -05:00
Tat Dat Duong d690fc3e00 Bump to 0.0.39 2025-02-12 08:52:25 -08:00
Tat Dat Duong 3325787af7 fix(sdk-js): improve SSE parsing performance by 10x 2025-02-12 08:51:53 -08:00
Tat Dat Duong 77a3cffa7f Allow omitting client 2025-02-12 08:50:01 -08:00
Ben BurnsandGitHub d1f2a6c518 chore(docs): update markdown-exec to end sessions after each page (#3391) 2025-02-11 21:28:30 +00:00
Eugene YurtsevandGitHub b984584851 docs: revert accidental changes to glossary (#3389) 2025-02-11 21:09:03 +00:00
bce4545021 docs: vcr only set up for markdown (#3339)
- PR branch for testing w/ typescript (@benjamincburns )
- implementation needs better cache invalidation for the cassettes
(@eyurtsev)

---------

Co-authored-by: Ben Burns <803016+benjamincburns@users.noreply.github.com>
2025-02-11 15:38:17 -05:00
Vadym BardaandGitHub 29c317887d langgraph: release to 0.2.71 (#3388) 2025-02-11 14:06:04 -05:00
Vadym BardaandGitHub 84a0eca935 langgraph: allow passing destinations to .add_node (#3384) 2025-02-11 19:01:45 +00:00
Tat Dat Duong d9ed1ef52e Branching 2025-02-11 10:10:54 -08:00
Vadym BardaandGitHub fda79e00ce docs: update typo in a tool (#3385)
Fixes #3383
2025-02-11 13:08:33 -05:00
Tat Dat Duong 3163a60466 Fix typo 2025-02-11 09:58:19 -08:00
Tat Dat Duong ac05955222 Update peer dependencies 2025-02-11 09:09:08 -08:00
Tat Dat Duong f3fe30380c Make sure we clear when switching threads 2025-02-11 09:01:53 -08:00
Tat Dat Duong 2c5ddceeb1 Add submit / stop 2025-02-11 09:01:53 -08:00
Tat Dat Duong 98976e016a Prevent double Error: Error 2025-02-11 09:01:52 -08:00
Tat Dat Duong ea8025b719 Add other stream parameters 2025-02-11 09:01:52 -08:00
Tat Dat Duong 735a76a16c Clean up the API 2025-02-11 09:01:52 -08:00
Tat Dat Duong a33437964c Add debugger, that is not exported 2025-02-11 09:01:52 -08:00
Tat Dat Duong cb9405bcee Allow update: null 2025-02-11 09:01:52 -08:00
Tat Dat Duong bd2268404c Update types 2025-02-11 09:01:51 -08:00
Tat Dat Duong 25f88740c1 feat(sdk-js): add experimental useStream hook 2025-02-11 09:01:51 -08:00
David DuongandGitHub e9809ae9c1 feat(sdk-js): strongly-typed messages, state/update-type, stream mode (#3360)
StateType, UpdateType is set on the Client rather than on
`RunsClient.stream` because of lack of partial type arguments
application.

This PR also describes the message serialization format emitted by
LangGraph Server (which may change ie. converting `type` to `role`).
Avoiding direct import of `@langchain/core` for the core LangGraph SDK
client, thus these types were copied from `@langchain/core` (a script is
used to aid with keeping track with core)
2025-02-11 08:56:02 -08:00
Tat Dat Duong 69311a4135 Add feedback stream event 2025-02-11 08:45:15 -08:00
Tat Dat Duong bb3193c83e fix(sdk-js): non-ok response is not throwing anymore 2025-02-11 08:45:15 -08:00
Tat Dat Duong 7a2eb614dc Add ErrorStreamEvent 2025-02-11 08:45:14 -08:00
Tat Dat Duong 7fd6931200 Cleanup types 2025-02-11 08:45:14 -08:00
Tat Dat Duong cbca07e3db Rename to more sane event type 2025-02-11 08:45:14 -08:00
Tat Dat Duong 066525b335 Update content 2025-02-11 08:45:14 -08:00
Tat Dat Duong a1dad43602 feat(sdk-js): strongly-typed messages, state/update-type, stream mode 2025-02-11 08:45:14 -08:00
Vadym BardaandGitHub 1e8f097656 docs: update prebuilt page w/ autogenerated info (#3381) 2025-02-11 09:27:01 -05:00
Brace SproulandGitHub db26c915a9 fix(sdk-js): Export types (#3377) 2025-02-10 18:02:42 -08:00
bracesproul 9b62280fc5 bump dep 2025-02-10 17:47:07 -08:00
bracesproul 515ad8d7a6 fix(sdk-js): Export types 2025-02-10 17:30:19 -08:00
David DuongandGitHub d378f0e06a fix(sdk-js): use type instead of interface to avoid TS errors (#3358)
Allow assigning `threadState?.checkpoint` to the `configurable` object
without TSC throwing "Index signature for type 'string' is missing in
type".
2025-02-10 13:30:25 -08:00
Vadym BardaandGitHub 740870df65 docs: ignore more links in url checker (#3374) 2025-02-10 15:27:58 -05:00
Vadym BardaandGitHub 6e20c9f3f9 docs: update codespell config (#3373) 2025-02-10 14:35:07 -05:00
Vadym BardaandGitHub 530544234a docs: add missing file (#3371) 2025-02-10 12:49:10 -05:00
Vadym BardaandGitHub f11d241482 docs: add adopters page (#3370) 2025-02-10 12:32:14 -05:00
William FHandGitHub e81979827f Support index embed specification via string (#3317)
So you can do

```
from langgraph.store.memory|posgres|etc. import InMemoryStore

InMemoryStore(index={"embed": "openai:text-embedding-3-small"})
```
2025-02-09 02:09:10 +00:00
Tat Dat Duong 2064ea4793 fix(sdk-js): use type instead of interface to avoid TS errors
Allow assigning `threadState?.checkpoint` to the `configurable` object without TSC throwing "Index signature for type 'string' is missing in type".
2025-02-08 10:32:28 -08:00
Brace SproulandGitHub 16cfeff78c release(sdk-js): 0.0.37 (#3354) 2025-02-07 12:44:55 -08:00
bracesproul 4321d337d6 release(sdk-js): 0.0.37 2025-02-07 12:35:51 -08:00
Brace SproulandGitHub d56e2545a6 fix(sdk-js): Remove default fetch timeout (#3353) 2025-02-07 12:34:32 -08:00
bracesproul 9a3f96c459 fix(sdk-js): Remove default fetch timeout 2025-02-07 12:17:34 -08:00
LaelandGitHub cb509ad6a5 Update application_structure.md (#3278)
moved requirements.txt to root of the project in the recommended
structure
2025-02-07 15:14:51 -05:00
Vadym BardaandGitHub e479a2c643 docs: update packages.yml (#3351) 2025-02-07 15:12:00 -05:00
Vadym BardaandGitHub 0caae32a40 docs: expose langgraph-supervisor (#3350) 2025-02-07 14:29:41 -05:00
Vadym BardaandGitHub a6b8098548 cli: release 0.1.71 (#3342) 2025-02-06 17:51:48 -05:00
Vadym BardaandGitHub 0631f19ef9 langgraph: release 0.2.70 (#3341) 2025-02-06 17:50:08 -05:00
Eugene YurtsevandGitHub e9b0bc4d3b cli: patch to convert graphs paths to posix (#3318)
* Convert graph paths to POSIX for docker.
* Will set up windows build in a separate PR (some issue w/ pulling down
the image) https://github.com/langchain-ai/langgraph/pull/3271
* Fix for https://github.com/langchain-ai/langgraph/issues/2061
2025-02-06 17:29:57 -05:00
Vadym BardaandGitHub eb19b80d13 langgraph: add agent name to AI messages in create_react_agent (#3340) 2025-02-06 17:24:53 -05:00
Nuno CamposandGitHub e5ccdd91c2 fix(langgraph): Dedupe input (right-side) messages in add_messages (#3338)
Port: https://github.com/langchain-ai/langgraphjs/pull/846
2025-02-06 13:25:20 -08:00
jacoblee93 871f15a630 Merge branch 'jacob/dedupe' of github.com:langchain-ai/langgraph into jacob/dedupe 2025-02-06 11:52:29 -08:00
jacoblee93 32aa87d4b2 Add test 2025-02-06 11:52:13 -08:00
Nuno Campos 55a3352e9b One more 2025-02-06 11:34:45 -08:00
Nuno Campos 6adaa6cf78 Fix up 2025-02-06 11:33:54 -08:00
Vadym BardaandGitHub 28172a2767 langgraph: allow setting custom names for compiled graph / compiled state graph (#3337) 2025-02-06 19:33:02 +00:00
jacoblee93 028137a51a Format 2025-02-06 11:28:10 -08:00
jacoblee93 c3847fae6c Dedupe input (right-side) messages in add_messages 2025-02-06 11:26:15 -08:00
William FHandGitHub 3daa4ae466 Update name to InMemorySaver (#2044)
(Keep MemorySaver around for backwards compatibility)

"MemorySaver" is ambiguous: is it saving memories? Where is it saving
memories to?

InMemorySaver aligns naming InMemoryStore as well as similar LangChain
objects (InMemoryVectorStore, etc.)
2025-02-05 22:04:25 -08:00
Doros Doru-LucianandGitHub 6a36f4bf91 docs: add forgotten MemorySaver in full code (#3321) 2025-02-05 20:58:17 -05:00
Eugene YurtsevandGitHub 04a0443742 ci: swap order of llms full and site build (#3315) 2025-02-04 22:16:39 +00:00
Eugene YurtsevandGitHub 3b224df566 publish llms text (#3313)
just includes llms-full
2025-02-04 21:56:33 +00:00
Vadym BardaandGitHub 0a2e2b4796 docs: fix typo (#3312) 2025-02-04 16:22:55 -05:00
Eugene YurtsevandGitHub 0e0b3ffac5 Update plans.md (#3311) 2025-02-04 20:45:54 +00:00
Eugene YurtsevandGitHub 75509bd221 docs: generate llms text (#3243)
```shell
python docs/_scripts/generate_llms_text.py llms_text.md
```
2025-02-04 20:30:26 +00:00
William FHandGitHub f997e5eafd Update inv file (#3308)
https://mkdocstrings.github.io/reference/handlers/base/
2025-02-04 11:45:59 -08:00
Eugene YurtsevandGitHub 6337c62dc7 Update prebuilt.md (#3306) 2025-02-04 18:46:57 +00:00
Eugene YurtsevandGitHub 59992fac7c Update create_third_party_page.py (#3307) 2025-02-04 18:44:18 +00:00
Eugene YurtsevandGitHub 28a7ca03a9 docs: publish prebuilt page (#3305)
Publish prebuilt page
2025-02-04 18:26:25 +00:00
TanushreeandGitHub 9fe61a42d2 Adding interrupt banner (#3295) 2025-02-03 18:57:17 -08:00
Marcello MaugeriandGitHub 59ae3b873c Fix typo in quick_start.md (#3297) 2025-02-03 18:51:27 -05:00
Vadym BardaandGitHub 31ba5c41ac docs: use full path for the upload artifact (#3293) 2025-02-03 15:33:50 -05:00
Vadym BardaandGitHub 6ddbdd0638 docs: add vercel build (#3290) 2025-02-03 20:13:58 +00:00
William FHandGitHub 3f7f52ccdc Docs links fix (#3292) 2025-02-03 10:57:38 -08:00
William FHandGitHub ee9a34f726 Update license key troubleshooting (#3157) 2025-02-03 18:09:05 +00:00
Eugene YurtsevandGitHub 72ce34e3e6 docs: add 3rd party packages scaffold (#3263)
* Adds scaffolding for 3rd party packages. Not published yet.
2025-02-03 17:21:30 +00:00
Vadym BardaandGitHub 098d5ec403 langgraph: update RunnableLike to support injected kwargs (#3288)
Fixes #3257
2025-02-03 11:25:20 -05:00
Vadym BardaandGitHub 45ef425d85 docs: update command how-to (#3280) 2025-02-03 09:29:45 -05:00
8c192414a2 docs: Update Hierarchial Multi Agent Example (#3282)
Was trying to learn the Multi Agent Workflow examples and encountered
some errors, which I fixed by editing these:

* Added missing state for Team1, and importing `Command`
* `ValueError: Node `LangGraph` already present.`: Seems to happen we
add the `team_1_graph` node without giving it a name, it will default to
the name `LangGraph`. Solved by giving the sub-graph a name when
building the top-level supervisor.
* Added the edges for the graph to feedback to the top level supervisor
to decide whether it still needs to relegate the task to other nodes or
end from there

---------

Co-authored-by: Vadym Barda <vadim.barda@gmail.com>
2025-02-03 09:29:19 -05:00
Vadym BardaandGitHub c0db7f4d09 docs: revamp streaming how-to guides + update redirects (#3274) 2025-01-31 18:04:51 -05:00
072f2d43eb docs: add graph API basics section (#3228)
![Screenshot 2025-01-30 at 3 53
05 PM](https://github.com/user-attachments/assets/c97018ea-38ff-4b38-96df-55c6d7f926f3)

---------

Co-authored-by: Vadym Barda <vadym@langchain.dev>
2025-01-31 17:41:56 -05:00
Andrew NguonlyandGitHub c02a74e221 docs: Add Excalidraw file for LangGraph Cloud architecture diagram (#3272) 2025-01-31 14:07:12 -08:00
Vadym BardaandGitHub 15af8a9e6c Revert "docs: revamp streaming how-to guides (#3239)" (#3270)
This reverts commit d5b0ad2cf6.
2025-01-31 15:52:58 -05:00
Vadym BardaandGitHub 1b257b4d0a ci: disable autogen notebook in the runner (#3269) 2025-01-31 15:44:03 -05:00
Vadym BardaandGitHub 384814036e docs: update other frameworks how-to (#3264) 2025-01-31 20:23:41 +00:00
Vadym BardaandGitHub d5b0ad2cf6 docs: revamp streaming how-to guides (#3239) 2025-01-31 15:14:26 -05:00
ccurmeandGitHub 8d8e514924 langgraph[patch]: rename parameter (#3268)
Rename `tool_call_parallelism` (has not been released yet).
2025-01-31 19:36:55 +00:00
a37c4d6f49 langgraph[patch]: allow ToolNode to accept ToolCalls (#3126)
Alternative to https://github.com/langchain-ai/langgraph/pull/3124

Currently if a tool interrupts, the entire tool node executes again
after resuming. So tools can get executed twice if parallel tool calls
are generated. Here we allow ToolNode to accept tool calls, so we can
use the `Send` API to distribute the tool calls to multiple instances of
the tool node.

```python
from langchain_anthropic import ChatAnthropic
from langchain_core.tools import tool
from langgraph.checkpoint.memory import MemorySaver
from langgraph.prebuilt import create_react_agent
from langgraph.types import Command, Send, interrupt


@tool
def human_assistance(query: str) -> str:
    """Request assistance from a human."""
    human_response = interrupt({"query": query})
    return human_response["data"]


@tool
def get_weather(location: str) -> str:
    """Use this tool to get the weather."""
    return "It's sunny!"


tools = [get_weather, human_assistance]
llm = ChatAnthropic(model="claude-3-5-sonnet-20240620")

agent = create_react_agent(
    llm,
    tools,
    checkpointer=MemorySaver(),
    tool_call_parallelism="parallel_tool_nodes",
)


user_input = (
    "Could you please (1) request assistance for building an AI agent "
    "from a human, and (2) search for the weather in Boston, MA? "
    "Generate two tool calls at once."
)

config = {"configurable": {"thread_id": "1"}}

for event in agent.stream(
    {"messages": [{"role": "user", "content": user_input}]},
    config,
    stream_mode="values",
):
    event["messages"][-1].pretty_print()
```
```
...
```
```python
human_response = "You should check out LangGraph to build your agent."
human_command = Command(resume={"data": human_response})

for event in agent.stream(human_command, config, stream_mode="values"):
    event["messages"][-1].pretty_print()
```

---------

Co-authored-by: Vadym Barda <vadym@langchain.dev>
2025-01-31 17:20:59 +00:00
Vadym BardaandGitHub 4b3e07b67a langgraph: release 0.2.69 (#3256) 2025-01-30 19:56:20 -05:00
Nuno CamposandGitHub 6ba61cc768 Guard and cache calls to find_subgraph_pregel (#3255)
- The result of these doesnt change once a node is created, and it's
fairly expensive to run, so great thing to cache
- There's a variety of errors that can come from inspecting the source
code of a function (part of what this does) so adding a catch-all
try-except block as this should be best-effort, not crash your graph
2025-01-30 16:37:42 -08:00
Nuno Campos 5a79210904 Lint 2025-01-30 16:28:03 -08:00
Nuno Campos 4a60eaf32f Guard and cache calls to find_subgraph_pregel
- The result of these doesnt change once a node is created, and it's fairly expensive to run, so great thing to cache
- There's a variety of errors that can come from inspecting the source code of a function (part of what this does) so adding a catch-all try-except block as this should be best-effort, not crash your graph
2025-01-30 16:26:23 -08:00
Vadym BardaandGitHub cf7c3e7fd1 docs: update README to include built w/ langgraph (#3254) 2025-01-30 22:13:06 +00:00
Vadym BardaandGitHub 141b53ee6e docs: add reference file for config (#3253) 2025-01-30 16:58:00 -05:00
37e8e00f1f When using Command.PARENT also pass existing subgraph state to parent graph (#3134)
Co-authored-by: Vadym Barda <vadym@langchain.dev>
2025-01-30 15:54:44 -05:00
Vadym BardaandGitHub a0ec9017f2 langgraph: add get_stream_writer() (#3251)
Alternative to #3250
2025-01-30 20:45:44 +00:00
Eugene YurtsevandGitHub 80cef60405 docs: concepts update (#3248) 2025-01-30 16:32:17 +00:00
Vadym BardaandGitHub 46cba763be docs: small README update (#3244) 2025-01-29 17:46:32 -05:00
Vadym BardaandGitHub 830e5f4550 docs: update README (#3242) 2025-01-29 22:33:32 +00:00
Vadym BardaandGitHub 55e9409b6e docs: update multi-agent multi-turn how-to (#3241) 2025-01-29 22:28:03 +00:00
Vadym BardaandGitHub 39e65a1a62 langgraph: expose tags in the metadata for streamed message chunks (#3238) 2025-01-29 20:24:32 +00:00
Vadym BardaandGitHub 82148e9bf2 docs: update streaming docstring for Pregel and expose in api ref (#3229) 2025-01-29 12:07:07 -05:00
Harsh NevseandGitHub ed09a77d9f Update multi-agent-collaboration.ipynb (#3233)
grammar
2025-01-29 11:48:54 -05:00
William FHandGitHub 7e267897c9 Update auth file paths in build/up (#3231) 2025-01-29 05:09:10 -08:00
Andrew NguonlyandGitHub d279902156 docs: Add LangSmith Integration section for Cloud SaaS concepts (#3230) 2025-01-28 13:27:51 -08:00
Eugene YurtsevandGitHub 440158b969 Update functional_api.md (#3227) 2025-01-28 17:57:01 +00:00
0cf3a64d66 Do not inject args in RunnableCallable if arg already exists (#3185)
Co-authored-by: Eugene Yurtsev <eyurtsev@gmail.com>
2025-01-28 12:48:33 -05:00
Vadym BardaandGitHub 953e2907d4 docs: remove MessageGraph from concepts doc (#3226) 2025-01-28 10:16:54 -05:00
Vadym BardaandGitHub 0a861815b7 docs: replace 'state_modifier' with 'prompt' (#3190) 2025-01-28 02:51:40 +00:00
Eugene YurtsevandGitHub 928126f2d7 docs: functional api concepts paraphrase (#3223) 2025-01-27 21:26:13 -05:00
Vadym BardaandGitHub 0d8fea847a langgraph: release 0.2.68 (#3224) 2025-01-27 21:08:06 -05:00
Eugene YurtsevandGitHub b4dc3a851f functional api: remove generator support (#3220)
Remove generator support in entrypoint.
2025-01-27 19:42:35 -05:00
Vadym BardaandGitHub 7568862013 langgraph: more unittests for functional API (#3221) 2025-01-28 00:41:15 +00:00
Andrew NguonlyandGitHub 69dc5f326e docs: Add section for Automatic Deletion of LangGraph Cloud SaaS deployments (#3222) 2025-01-27 15:42:36 -08:00
90f1e66748 docs: functional api (#3125)
* Concepts page for the functional API
* How-to guides that show functional API implementations
* API reference for entrypoint, task, entrypoint.final
* Add functional API version to the workflows

---------

Co-authored-by: Vadym Barda <vadym@langchain.dev>
Co-authored-by: ccurme <chester.curme@gmail.com>
2025-01-27 16:31:43 -05:00
Vadym BardaandGitHub a910a1a341 langgraph: actually fix flaky test (#3219) 2025-01-27 16:06:31 -05:00
Vadym BardaandGitHub 554e763994 langgraph: use 'prompt' param for model input preprocessing in create_react_agent (#3173) 2025-01-27 19:03:01 +00:00
Vadym BardaandGitHub adff1df813 langgraph: fix flaky test (#3218) 2025-01-27 13:11:17 -05:00
Vadym BardaandGitHub 64b828def6 langgraph: handle task naming for reused class methods (#3216) 2025-01-27 17:05:39 +00:00
Jacob LeeandGitHub 9196cc2da8 docs: Fix broken links in README (#3211) 2025-01-27 09:15:17 -05:00
David DuongandGitHub e13a8bc320 fix: missing pgvector in CLI (#3165)
Fixes https://github.com/langchain-ai/langgraph/issues/3164
2025-01-25 04:27:42 +01:00
Tat Dat Duong 4b5b309152 Bump to 0.1.69 2025-01-25 04:18:18 +01:00
Tat Dat Duong 9d5166a0d3 Fix tests 2025-01-25 04:16:52 +01:00
Nuno CamposandGitHub 5b11683f4e Fix type hints for entrypoint decorator (#3198) 2025-01-24 15:07:21 -08:00
Vadym BardaandGitHub 3fae8d47c3 langgraph: handle more callable types in tasks (#3203) 2025-01-24 22:50:42 +00:00
Nuno Campos be9cb03dfa Lint 2025-01-24 13:51:16 -08:00
Vadym BardaandGitHub 6926c4bcc0 langgraph: add names for tasks (#3202) 2025-01-24 21:42:54 +00:00
Nuno Campos c04802a344 Fix type hints for entrypoint decorator 2025-01-24 09:31:27 -08:00
Nuno CamposandGitHub cf7f6691cf Improve typings for task, it now returns a mixed sync/async future protocol (#3186)
- note this type is never instantiated, it is only used for typing (we
cannot make it a protocol as it inherits from concurrent.futures.Future)
2025-01-24 09:19:51 -08:00
Vadym BardaandGitHub e1140f4fad ci: always test notebooks on all library versions (#3191) 2025-01-23 22:26:32 -05:00
Eugene YurtsevandGitHub ae8afb7677 reference: document entrypoint more (#3189)
add more documentation to the entrypoint primitive
2025-01-23 22:17:20 -05:00
Vadym BardaandGitHub 48040d8ea5 ci: run notebooks in 'development' for PRs (#3188) 2025-01-23 21:59:03 -05:00
Vadym BardaandGitHub ad51bfdf71 ci: only test changed notebooks using 'development' version of the library (#3187) 2025-01-23 21:41:47 -05:00
Nuno Campos 996b120613 Undo 2025-01-23 16:59:27 -08:00
Nuno Campos 1093dd55c8 Improve typings for task, it now returns a mixed sync/async future protocol
- note this type is never instantiated, it is only used for typing (we cannot make it a protocol as it inherits from concurrent.futures.Future)
2025-01-23 16:57:34 -08:00
Nuno CamposandGitHub d794875b32 Undo change to test watch mode (#3184) 2025-01-23 16:54:03 -08:00
Nuno Campos 6948cf5eb8 Undo change to test watch mode 2025-01-23 16:18:53 -08:00
Nuno CamposandGitHub bacc2955ae Remove implementation of get_graph for @entrypoint (#3183) 2025-01-23 15:48:22 -08:00
Nuno Campos f014d96d1c Remove implementation of get_graph for @entrypoint 2025-01-23 15:39:36 -08:00
RadiandGitHub 7b552ebf4b Update tool-calling.ipynb (#3178)
Typo fix
2025-01-23 18:02:43 -05:00
Vadym BardaandGitHub 1e61ddfdbe langgraph: release 0.2.67 (#3182) 2025-01-23 18:01:43 -05:00
Vadym BardaandGitHub d34846dc08 docs: fix a hub prompt (#3180) 2025-01-23 17:21:37 -05:00
Vadym BardaandGitHub 06823e327f langgraph: update docstrings/api ref for functional api (#3176) 2025-01-23 16:48:55 -05:00
Vadym BardaandGitHub 1059ef55d1 langgraph: handle node return annotations with unions (#3170) 2025-01-23 14:41:22 -05:00
39552255c8 langgraph: add support for BaseModel updates to Command (#2747)
Simple update that adds support for the `update` attribute of the
`Command` class to support Pydantic `BaseModel` type.

LangGraph already supports [Pydantic models for graph
states](https://langchain-ai.github.io/langgraph/how-tos/state-model/).

Extending support to the `update` attribute allows users to pass custom
BaseModel instances. Additionally, updates defined as `BaseModel` types
are type-validated when created.

https://github.com/langchain-ai/langgraph/issues/2804

---------

Co-authored-by: vbarda <vadym@langchain.dev>
2025-01-23 14:31:44 -05:00
Vadym BardaandGitHub 38bbe67469 langgraph: remove print (#3167) 2025-01-23 11:24:18 -05:00
Nuno CamposandGitHub 6335963674 Make scratchpad counters thread-safe (#3158)
- Same solution as used in python stdlib to name threads and asyncio
tasks
2025-01-23 07:37:04 -08:00
Nuno Campos 8a4c452317 Make test less flaky 2025-01-23 07:27:34 -08:00
Eugene YurtsevandGitHub 5dc5853161 Add entrypoint.final to decouple return value from save value (#3135)
* Introduce `entrypoint.final` that allows decoupling what's returned
from the state update.
* moving decorator to class object w/ call to support defining `final`
as a property on it -- this should play nicely w/ IDE tooling / type
checking.


```python
    previous_ = None

    @entrypoint(checkpointer=MemorySaver())
    def foo(msg: str, *, previous: Any) -> entrypoint.final[int, list[str]]:
        nonlocal previous_
        previous_ = previous
        previous = previous or []
        return entrypoint.final(value=len(previous), save=previous + [msg])

    assert foo.get_output_schema().model_json_schema() == {
        "title": "LangGraphOutput",
        "type": "integer",
    }

    config = {"configurable": {"thread_id": "1"}}
    assert foo.invoke("hello", config) == 0
    assert previous_ is None
    assert foo.invoke("goodbye", config) == 1
    assert previous_ == ["hello"]
    assert foo.invoke("definitely", config) == 2
    assert previous_ == ["hello", "goodbye"]

```
2025-01-23 10:18:23 -05:00
Vadym BardaandGitHub 9e066554ba langgraph: allow async state modifier in create_react_agent (#3161)
Fixes https://github.com/langchain-ai/langgraph/issues/2875
2025-01-23 09:29:25 -05:00
Tat Dat Duong 6ce9354ee4 fix: missing pgvector in CLI
Fixes https://github.com/langchain-ai/langgraph/issues/3164
2025-01-23 15:27:39 +01:00
Nuno Campos 211fd4337d Undo 2025-01-22 16:44:13 -08:00
Nuno Campos 44bf97ac0e Fix 2025-01-22 16:32:59 -08:00
Nuno Campos 23c73ae719 Update tests 2025-01-22 16:30:43 -08:00
Nuno CamposandGitHub 4165d479e9 langgraph: add test for interrupting multiple times from a task (#3148) 2025-01-22 16:25:03 -08:00
Nuno Campos c43a9a4bd0 Make scratchpad counters thread-safe
- Same solution as used in python stdlib to name threads and asyncio tasks
2025-01-22 16:07:59 -08:00
Nuno Campos c9613927dc Lint 2025-01-22 16:05:34 -08:00
Nuno Campos c697c2aa04 Undo 2025-01-22 16:02:09 -08:00
Nuno Campos 3f2557c9c9 Fix 2025-01-22 16:02:09 -08:00
Nuno Campos cbad17fa7d Fix 2025-01-22 16:02:09 -08:00
Nuno Campos 17dacb83a2 Fix 2025-01-22 16:02:09 -08:00
Nuno Campos 3a997be088 Fix 2025-01-22 16:02:09 -08:00
Chester CurmeandNuno Campos 020d10138d add test case 2025-01-22 16:02:09 -08:00
Vadym BardaandGitHub 303587c4ff docs: bring back image for concept doc (#3152) 2025-01-22 16:08:24 -05:00
Andrew NguonlyandGitHub 7d4e636313 docs: Add note about how to share Postgres instance for self-hosted deployments (#3139)
### Screenshot

![image](https://github.com/user-attachments/assets/1caa7581-9dda-4ffc-9fee-ffb0f72487ea)
2025-01-22 12:14:24 -08:00
Lance MartinandGitHub 86913caf89 Add agents and workflows overview (#3040) 2025-01-22 12:07:47 -08:00
Nuno CamposandGitHub 041faefe29 Remove write to RETURN channel for entrypoint func (#3149) 2025-01-22 11:46:14 -08:00
Nuno Campos b704cf30cc Lint 2025-01-22 11:34:30 -08:00
Nuno Campos 3915b44180 Format 2025-01-22 11:32:09 -08:00
Nuno Campos ac1407b23c Remove write to RETURN channel for entrypoint func 2025-01-22 09:44:52 -08:00
Vadym BardaandGitHub 44840aa23f docs: add missing api key to cross-thread persistence howto (#3141) 2025-01-21 21:24:01 -05:00
Vadym BardaandGitHub 51242e2a32 ci: pin codespell (#3140) 2025-01-21 21:22:23 -05:00
Vadym BardaandGitHub b358e2e7cd docs: fix typo in persistence howto (#3138) 2025-01-21 21:03:00 -05:00
Nuno CamposandGitHub 0d91ab1474 Disable recursing on runnables for task/entrypoint decorated funcs (#3136) 2025-01-21 15:47:51 -08:00
Nuno Campos 12ae297194 Disable recursing on runnables for task/entrypoint decorated funcs 2025-01-21 15:36:09 -08:00
Nuno Campos 0177565c6b Spell check 2025-01-21 15:14:00 -08:00
Bhavya DhimanandGitHub c48d495031 fix(requirements.txt): Set requirements path to posix expression so that it can work in windows OS as well. (#3101)
When I cloned langgraph example, I was not able to run the code because
of the requirements.txt file. The path was set to posix expression which
was not working in windows OS.
I have updated the path to posix expression so that it can work in
windows OS as well.

Try running `langgraph-example` in windows using the langgraph-cli in
windows OS. It was working for linux not in windows.
2025-01-21 14:30:13 -05:00
Vadym BardaandGitHub 22e6468af5 langgraph: release 0.2.66 (#3128) 2025-01-21 13:26:29 -05:00
ccurmeandGitHub 24bc0c0630 docs: update readme / docs intro page (#3082) 2025-01-21 13:19:30 -05:00
Nuno CamposandGitHub 802e6df8df Enable async tests that were being skipped (#3109)
- async tests are placed in test_pregel_async, not in test_pregel
- to avoid tests placed in wrong file being accidentally skipped i've
added the auto-async mark to sync test file
2025-01-21 10:16:20 -08:00
Nuno CamposandGitHub 3ec55b008d Fix timing issue where a sync task would finish before the other one was registered in futures dict (#3110)
- this was not possible in async where all done callbacks are called in
next tick
- in sync case this would manifest as the first task done callback
seeing counter == 1 and thus setting event
- the fix is to unset the event whenever a task is scheduled
2025-01-21 10:16:09 -08:00
Nuno CamposandGitHub e10b7c1391 Re-enable support for running sync tasks from async entrypoints (#3108)
- When using an async entrypoint you can now freely mix and match sync
and async tasks with a uniform api (ie all tasks return a sync or async
future depending on context)
- Fix issues with scheduling deeply nested tasks (use threadsafe methods
to schedule coroutines and create futures)
2025-01-21 10:15:49 -08:00
Nuno CamposandGitHub 31cc6b9f1d Fix tracing of args for @task decorated functions (#3107)
- now using same logic as in langsmith sdk, treating as single args
dict, based on function signature
2025-01-21 10:09:13 -08:00
Nuno Campos 2cafb4905b Lint 2025-01-21 10:06:02 -08:00
Nuno Campos fe46576d98 Fix 2025-01-21 09:59:49 -08:00
Nuno Campos 16c86c9de6 Add test 2025-01-21 09:49:46 -08:00
fca0d2d5bb Update libs/langgraph/langgraph/utils/future.py
Co-authored-by: William FH <13333726+hinthornw@users.noreply.github.com>
2025-01-21 09:43:12 -08:00
Nuno Campos c7c62f5587 Fix 2025-01-21 09:42:25 -08:00
Vadym BardaandGitHub 956c5f68fc docs: add a how-to guide for structured outputs in react agent (#3121) 2025-01-21 17:30:32 +00:00
ccurmeandGitHub 4908caf522 docs: how-to guides nits (#3123) 2025-01-21 12:17:37 -05:00
Chester Curme f926eada24 fix warnings 2025-01-21 12:06:42 -05:00
Chester Curme adc1e47028 nits 2025-01-21 12:00:12 -05:00
Vadym BardaandGitHub 7820b5c765 docs: add docs for Command.PARENT (#3081) 2025-01-21 16:37:39 +00:00
Chester Curme ca6f8e2042 Merge branch 'main' into cc/update_readme 2025-01-21 10:31:29 -05:00
Chester Curme ee61d06f8d add hyperlink 2025-01-21 10:31:27 -05:00
Roy BarberandGitHub 6eeb9de46a DOCS: Incorrect reference to JS/TS SDK as Python SDK (#3103)
Small typo in the Local Server page:
https://langchain-ai.github.io/langgraph/tutorials/langgraph-platform/local-server/
2025-01-21 09:34:57 -05:00
viren-viiandGitHub 19a91f4677 Update persistence_postgres.ipynb to remove typo in markdown cell. (#3119)
Fixed a typo that was interrupting the markdown.
2025-01-21 09:33:31 -05:00
BagaturandGitHub 6087b1969e python[patch]: call create_react_agent model node without is_last_step (#3114)
So that you can call agent.nodes['agent'].invoke({'messages': []})
without needing to specify is_last_step. very helpful for evaluating
just the model node of the agent
2025-01-20 18:28:57 -08:00
Nuno CamposandGitHub 6cbc7e8b67 Add get_store function (#3112) 2025-01-20 16:12:39 -08:00
Nuno Campos b2213e523e Add get_store function 2025-01-20 16:03:27 -08:00
David DuongandGitHub ac64f50383 feat(cli): add detection for bun.lockb (#3111) 2025-01-21 00:30:22 +01:00
Tat Dat Duong 25239891bc feat(cli): add detection for bun.lockb 2025-01-21 00:17:12 +01:00
Nuno Campos b7c3ac4501 Fix tracing output 2025-01-20 13:55:13 -08:00
Nuno Campos 09e8516689 Fix test 2025-01-20 11:57:19 -08:00
Nuno Campos 3ca75d69b8 Lint 2025-01-20 11:57:19 -08:00
Nuno Campos d48dec5452 Enable async tests that were being skipped
- async tests are placed in test_pregel_async, not in test_pregel
- to avoid tests placed in wrong file being accidentally skipped i've added the auto-async mark to sync test file
2025-01-20 11:57:19 -08:00
Nuno Campos d48b25420b Fix timing issue where a sync task would finish before the other one was registered in futures dict
- this was not possible in async where all done callbacks are called in next tick
- in sync case this would manifest as the first task done callback seeing counter == 1 and thus setting event
- the fix is to unset the event whenever a task is scheduled
2025-01-20 11:40:54 -08:00
Nuno Campos 12be3fac33 Lint 2025-01-20 11:13:37 -08:00
Nuno Campos aed1f0ba18 Re-enable support for running sync tasks from async entrypoints
- When using an async entrypoint you can now freely mix and match sync and async tasks with a uniform api (ie all tasks return a sync or async future depending on context)
- Fix issues with scheduling deeply nested tasks (use threadsafe methods to schedule coroutines and create futures)
2025-01-20 11:03:51 -08:00
Nuno Campos 07695f5c5a Lint 2025-01-20 10:53:37 -08:00
Nuno Campos 204c9c83f8 Fix tracing of args for @task decorated functions
- now using same logic as in langsmith sdk, treating as single args dict, based on function signature
2025-01-20 10:49:27 -08:00
David DuongandGitHub 0bdd27ade4 fix(cli): warn users to use the JS cli for JS graphs (#3086) 2025-01-18 02:29:45 +01:00
David DuongandGitHub ab0048981a docs: add mention of JS CLI (#3077) 2025-01-18 01:52:00 +01:00
Tat Dat DuongandNuno Campos e18f2b3795 fix(cli): warn users to use the JS cli for JS graphs 2025-01-17 16:07:31 -08:00
Chester CurmeandNuno Campos 31578cbe0e move code block 2025-01-17 16:07:16 -08:00
Chester CurmeandNuno Campos 44970640d5 cr 2025-01-17 16:07:16 -08:00
Chester CurmeandNuno Campos 0f4e42474f cr 2025-01-17 16:07:16 -08:00
Chester CurmeandNuno Campos 832f9ad64e copy changes to libs/langgraph/README.md 2025-01-17 16:07:16 -08:00
Chester CurmeandNuno Campos 318de5bb81 update readme 2025-01-17 16:07:16 -08:00
William FHandGitHub a11f9b3535 Fix docstring in pg store init (#3094) 2025-01-18 00:03:51 +00:00
Nuno CamposandGitHub 29db5a8672 Implement get_graph for imperative api (#3076) 2025-01-17 15:53:50 -08:00
Nuno CamposandGitHub 444faec6e6 Fix two issues with task/stream timing (#3095)
- both issues are related to the fact that waiters for futures are
notified of completion before "done" callbacks are called
- 1st issue manifested as interrupt stream event being emitted before
the result of a task that logically finished first (it's in the line
above in body of the entrypoint function) -> this is solved by always
returning to use code a fresh future chained on the original future,
because chaining is done via done callbacks (therefore the chained
future will only resolve after done callbacks of the original feature
are called)
- 2nd issue mainfested as sometimes (very rarely) the last stream event
not being printed before stream() finishes. this is solved by ensuring
we only return out of PregelRunner.tick() once all "done" callbacks are
called, previously we were approximating this through use of
asyncio.sleep(0) / time.sleep(0). The new solution instead waits on a
threading/asyncio.Event which will only be set by the last "done"
callback to fire
- this PR also disables incomplete support for calling sync tasks from
async entrypoints
2025-01-17 15:44:52 -08:00
Nuno Campos e4a5c8fd28 Fix two issues with task/stream timing
- both issues are related to the fact that waiters for futures are notified of completion before "done" callbacks are called
- 1st issue manifested as interrupt stream event being emitted before the result of a task that logically finished first (it's in the line above in body of the entrypoint function) -> this is solved by always returning to use code a fresh future chained on the original future, because chaining is done via done callbacks (therefore the chained future will only resolve after done callbacks of the original feature are called)
- 2nd issue mainfested as sometimes (very rarely) the last stream event not being printed before stream() finishes. this is solved by ensuring we only return out of PregelRunner.tick() once all "done" callbacks are called, previously we were approximating this through use of asyncio.sleep(0) / time.sleep(0). The new solution instead waits on a threading/asyncio.Event which will only be set by the last "done" callback to fire
2025-01-17 15:35:08 -08:00
Tat Dat Duong 0f1b0bfba3 typo 2025-01-17 23:40:58 +01:00
Tat Dat Duong 0e79407973 Last pass 2025-01-17 23:40:58 +01:00
Tat Dat Duong efa51fe22c Update README.md 2025-01-17 23:40:58 +01:00
Tat Dat Duong 11f2501c98 docs: add mention of JS CLI 2025-01-17 23:40:58 +01:00
Eugene YurtsevandGitHub 71e6002a3c docs: update broken api reference (#3093) 2025-01-17 17:04:54 -05:00
Andrew NguonlyandGitHub 26e3ded701 docs: Add note about cron jobs not available in LangGraph Platform Self-Hosted Lite (#3091) 2025-01-17 12:52:46 -08:00
Eugene YurtsevandGitHub 4fcd1690f9 docs: expose functional api reference (#3089)
The API reference is marked as experimental right now, so this is OK to expose to make it easier to share w/ beta users.
2025-01-17 15:18:43 -05:00
Vadym BardaandGitHub eb33a873c8 langgraph: release 0.2.64 (#3090) 2025-01-17 15:18:34 -05:00
91aa66f4cf tests: add tests for calling multiple subgraphs in a parent node (#3070)
Co-authored-by: Nuno Campos <nuno@langchain.dev>
2025-01-17 15:16:01 -05:00
Eugene Yurtsev 4476640702 x 2025-01-17 15:08:32 -05:00
Eugene YurtsevandGitHub 727e3f1730 functional api: first pass at api reference (#3083)
First pass at the API reference for the functional API
2025-01-17 14:46:22 -05:00
Vadym BardaandGitHub 943dd28863 docs: update state to include 'next' for supervisor notebooks (#3087) 2025-01-17 14:32:15 -05:00
ccurmeandGitHub aa9d253978 docs: highlight lines in troubleshooting doc (#3084)
![Screenshot 2025-01-17 at 1 44
07 PM](https://github.com/user-attachments/assets/b7009c77-cb2e-43e0-92a7-3978884b7f3e)
2025-01-17 18:53:21 +00:00
Eugene YurtsevandGitHub a11c6cfe6b Update docs/mkdocs.yml 2025-01-17 13:36:44 -05:00
Eugene Yurtsev 15c79dc3c3 x 2025-01-17 13:34:49 -05:00
Eugene Yurtsev a65c9f1e04 x 2025-01-17 13:11:23 -05:00
9912ae1053 docs: add high level example into the readme, make examples collapsible (#3052)
Co-authored-by: Chester Curme <chester.curme@gmail.com>
2025-01-17 17:10:22 +00:00
ccurmeandGitHub 7db29042b4 docs[patch]: update quickstart tutorial to use interrupt() (#3053)
Need to update cassettes
2025-01-17 11:24:40 -05:00
Chester Curme f8033a20c1 update cassettes 2025-01-17 11:15:33 -05:00
Chester Curme 5e3aa495d9 update 2025-01-17 11:05:55 -05:00
Chester Curme a54689affd ruff 2025-01-17 11:02:58 -05:00
Chester Curme 02ea523897 udpate 2025-01-17 11:02:25 -05:00
Chester Curme 18c6637e6c use message dicts instead of tuples 2025-01-17 10:34:21 -05:00
Chester Curme 4c02a4c501 Revert "disable parallel tool use"
This reverts commit 784b6afa11.
2025-01-17 10:19:17 -05:00
Chester Curme 784b6afa11 disable parallel tool use 2025-01-17 10:15:06 -05:00
Chester Curme b35b892d74 Merge branch 'main' into cc/update_quickstart 2025-01-17 10:02:29 -05:00
Chester Curme e7aaf21121 update 2025-01-17 10:02:18 -05:00
Eugene Yurtsev 54115ac796 x 2025-01-17 09:58:06 -05:00
ccurmeandGitHub 76be64adcb docs[patch]: fix builds (#3074) 2025-01-17 02:41:47 +00:00
Nuno Campos 2cf98725c4 Lint 2025-01-16 16:32:39 -08:00
Nuno Campos a33c59626a Lint 2025-01-16 16:01:57 -08:00
Nuno Campos 56d3b759c7 Add one more test 2025-01-16 15:46:25 -08:00
Nuno Campos 32bf81c559 Lint 2025-01-16 14:10:16 -08:00
Eugene Yurtsev de332ec31c x 2025-01-16 17:08:12 -05:00
Eugene YurtsevandGitHub ce30965df4 functional api: Add ability to request previous output (#3025)
1. The inputs into foo do not affect any state behavior
2. `previous` always reflects the previous return value from the
function
3. Anything can be returned and that will be the new state for the
function on the next iteration
4. This API is not meant to support reducers in the inputs/state

```python
  from langgraph.func import entrypoint

  states = []

  # In this version reducers do not work
  @entrypoint(checkpointer=MemorySaver())
  def foo(inputs, *, previous: Any) -> Any:
      states.append(previous)
      return {"previous": previous, "current": inputs}

  config = {"configurable": {"thread_id": "1"}}

  foo.invoke({"a": "1"}, config)
  foo.invoke({"a": "2"}, config)
  foo.invoke({"a": "3"}, config)
  assert states == [
      None,
      {"current": {"a": "1"}, "previous": None},
      {"current": {"a": "2"}, "previous": {"current": {"a": "1"}, "previous": None}},
  ]
```
2025-01-16 17:02:41 -05:00
Nuno Campos b98a7b09a3 Update 2025-01-16 13:44:19 -08:00
Nuno Campos 86b6afc982 Implement get_graph for imperative api 2025-01-16 13:44:18 -08:00
Nuno CamposandGitHub 47122ce88b docs: Add status field to Project object for Control Plane API (#3075) 2025-01-16 13:42:48 -08:00
Andrew Nguonly 002b048674 Add status field to Project object. 2025-01-16 13:27:23 -08:00
Eugene Yurtsev a58f5dacca x 2025-01-16 16:26:54 -05:00
Chester Curme cc397b1629 update cassettes 2025-01-16 16:24:05 -05:00
Eugene Yurtsev 3a48049194 x 2025-01-16 16:19:23 -05:00
Eugene Yurtsev e5f0db0af3 x 2025-01-16 16:09:11 -05:00
Chester Curme 93854ed721 fix link 2025-01-16 15:12:53 -05:00
Eugene Yurtsev 83a7acc57c Merge branch 'main' into eugene/expose_previous_state 2025-01-16 14:56:46 -05:00
Eugene Yurtsev b218cc76a7 type ignore for now 2025-01-16 14:50:40 -05:00
Eugene Yurtsev d021f476db x 2025-01-16 14:23:32 -05:00
Chester Curme d978cb0392 part 7 -> part 6 2025-01-16 14:21:40 -05:00
Chester Curme 605fe4ea11 simplify hitl, condense part 5 2025-01-16 14:20:05 -05:00
Eugene Yurtsev 46907b6cf9 x 2025-01-16 14:04:00 -05:00
Eugene Yurtsev 582856b30c x 2025-01-16 14:00:42 -05:00
Eugene Yurtsev e476897177 fix merge error 2025-01-16 13:58:55 -05:00
Eugene Yurtsev 421f7c0238 x 2025-01-16 13:55:41 -05:00
Eugene Yurtsev 3095bfce9c Merge branch 'main' into eugene/expose_previous_state 2025-01-16 13:54:05 -05:00
Nuno CamposandGitHub 4cfe0b2d4c tests: add a test for interrupt() w/ functional API (#3065) 2025-01-16 10:04:17 -08:00
Nuno Campos a16def5140 Lint 2025-01-16 09:55:04 -08:00
Nuno CamposandGitHub 1e730d124c Make config schema configurable for imperative api (#3067) 2025-01-16 09:52:55 -08:00
ccurmeandGitHub 21a7105655 docs[patch]: enable navigation back to index pages from child pages via sidebar (#3069)
Currently, if you're viewing a how-to guide and you click "How-to
Guides" in the sidebar, you aren't navigated back to the index page (it
will work if you click on a different guides section). To get back to
the index page, you need to scroll up and click the breadcrumbs.

After this change, clicking the link in the sidebar should navigate you
to the index page regardless of the page you are viewing.

Only side-effect from what I can tell is that "Home > Introduction" just
becomes "**Home**", which I think is fine (maybe preferable).

Before:
![Screenshot 2025-01-16 at 11 16
26 AM](https://github.com/user-attachments/assets/e4bdedfc-ed52-4a66-b0b2-194383ce2043)

After:
![Screenshot 2025-01-16 at 11 16
00 AM](https://github.com/user-attachments/assets/4d1d935b-5b11-43ac-8b63-c5aaff871188)
2025-01-16 12:52:16 -05:00
Nuno Campos 8c88e203bc Fix 2025-01-16 09:51:46 -08:00
Nuno CamposandGitHub 2a1224966c Implement input/output schemas for imperative api (#3066) 2025-01-16 08:45:02 -08:00
vbarda c4460e5dd2 add another test 2025-01-16 11:20:09 -05:00
Nuno Campos 46056363b3 Make config schema configurable for imperative api 2025-01-16 08:00:08 -08:00
Nuno Campos ba672604a6 Implement input/output schemas for imperative api 2025-01-16 07:57:00 -08:00
vbarda 8b1597a385 tests: add a test for interrupt() w/ functional API 2025-01-16 10:16:26 -05:00
Vadym BardaandGitHub adff439d4e langgraph: release 0.2.63 (#3064) 2025-01-16 09:19:25 -05:00
Vadym BardaandGitHub 89b0be3a7d checkpoint-postgres: release 2.0.13 (#3063) 2025-01-16 09:17:43 -05:00
Vadym BardaandGitHub 7ecda42b42 checkpoint-postgres: bring back missing migration (#3058) 2025-01-16 03:16:28 +00:00
Nuno CamposandGitHub bd99471705 tests: Add test for catching bunching in streaming (#3057) 2025-01-15 18:57:07 -08:00
Nuno Campos 0e4dbb4c62 Guard 2025-01-15 18:36:49 -08:00
Nuno Campos 145220f2a8 Fix 2025-01-15 18:29:34 -08:00
Eugene Yurtsev f9c25bba07 x 2025-01-15 21:15:38 -05:00
Eugene Yurtsev 7ecad39ecb x 2025-01-15 21:15:05 -05:00
Nuno CamposandGitHub 6bfc6be307 Add support for multiple subgraphs called in a single node (#3056) 2025-01-15 18:01:31 -08:00
Nuno Campos 6b9369876f Fix sync 2025-01-15 17:52:19 -08:00
Nuno Campos c26b0e78b6 Fix kafka lib 2025-01-15 17:15:58 -08:00
Nuno CamposandGitHub 1763dd69a7 Add checkpointer=True mode for subgraphs that want to keep state between turns (#3055) 2025-01-15 16:08:03 -08:00
Nuno Campos f4bd023ab1 Fix 2025-01-15 16:04:14 -08:00
Nuno Campos d402bf7379 Remove flag 2025-01-15 15:36:16 -08:00
Nuno Campos e8a73e1505 Remove flag 2025-01-15 15:36:10 -08:00
Nuno Campos d6492ef048 Add support for multiple subgraphs called in a single node 2025-01-15 15:36:10 -08:00
Nuno Campos 38d9b39f6e Add flag 2025-01-15 15:36:03 -08:00
Nuno Campos be8b4a1d7f Lint 2025-01-15 15:30:29 -08:00
Nuno Campos 71fbd6a8b4 Lint 2025-01-15 15:29:34 -08:00
Nuno Campos 5375af7827 Add checkpointer=True mode for subgraphs that want to keep state betweenn turns 2025-01-15 15:15:44 -08:00
Chester Curme 1654062957 enable navigation.indexes 2025-01-15 18:14:37 -05:00
Eugene Yurtsev 6b707cbfc5 x 2025-01-15 17:15:56 -05:00
Nuno CamposandGitHub dd7ac00953 Fix unexpected re-use of null resume value by subgraphs (#3054)
- Also stop exposing writes in config, in favor of scratchpad
2025-01-15 13:59:13 -08:00
Nuno Campos cd64075928 Lint 2025-01-15 13:49:33 -08:00
Nuno Campos 144ee31546 Lint 2025-01-15 13:43:14 -08:00
Nuno CamposandGitHub 8bb84c7096 Fix ignored goto when a mixed list of command and state updates is returned from a node (#3038) 2025-01-15 13:39:59 -08:00
Nuno Campos e83660885b Lint 2025-01-15 13:39:44 -08:00
Nuno Campos c2a57385c0 Update tests 2025-01-15 13:35:51 -08:00
Nuno Campos 3626478029 Fix unexpected re-use of null resume value by subgraphs
- Also stop exposing writes in config, in favor of scratchpad
2025-01-15 13:31:49 -08:00
Chester Curme 30311f3fb9 ruff 2025-01-15 16:12:01 -05:00
Chester Curme 0f458fbaff update 2025-01-15 16:06:55 -05:00
Brace SproulandGitHub 13c9bfa282 feat(langgraph): Add interrupt schema to library (#2947) 2025-01-15 12:55:47 -08:00
Andrew NguonlyandGitHub b6fe3937fc docs: Add section about Persistence to Cloud SaaS concepts page (#3051) 2025-01-15 12:27:53 -08:00
Chester Curme d7bae594f0 update hitl 2025-01-15 15:04:43 -05:00
Nuno Campos 5805e5709a Fix ignored goto when a mixed list of command and state updates is returned from a node 2025-01-15 11:42:28 -08:00
Nuno CamposandGitHub aab6fdf3f3 Fix Send order after interrupt/resume (#3037)
- order was incorrectly based on task id, instead of the correct task
path
- this requires storing task paths on checkpointers
- addition of task_path to put_writes is made backwards compatible by
checking signature on call, and treating it as an optional arg
2025-01-15 11:42:08 -08:00
Nuno CamposandGitHub be1d035aba tests: add test for multiple interrupts and tasks (#2941) 2025-01-15 11:41:30 -08:00
Nuno Campos 6e228f8a9c Lint 2025-01-15 11:32:42 -08:00
Nuno Campos 0adbd89d9a Bump checkpoint 2025-01-15 11:28:55 -08:00
Nuno Campos 47c37d140f Fix Send order after interrupt/resume
- order was incorrectly based on task id, instead of the correct task path
- this requires storing task paths on checkpointers
- addition of task_path to put_writes is made backwards compatible by checking signature on call, and treating it as an optinal arg
2025-01-15 11:28:26 -08:00
Nuno Campos fc887f7a5d checkpoint-postgres/sqlite 2.0.12/2.0.3 2025-01-15 11:26:51 -08:00
ccurmeandGitHub bbfc2b7370 docs[patch]: add section headers to LangGraph concepts page (#3047)
![Screenshot 2025-01-15 at 1 03
45 PM](https://github.com/user-attachments/assets/c7a6d5b7-cc42-4616-bba0-4352c2f922bc)
2025-01-15 14:15:33 -05:00
Nuno Campos 2c984dfe4e checkpoint 2.0.10 2025-01-15 11:14:03 -08:00
Nuno CamposandGitHub 09c0d9cec3 Add optional task_path arg for put_writes() (#3049)
- Will be used for sorting pending_sends when available
2025-01-15 11:11:10 -08:00
Nuno Campos 31734fb792 Lint 2025-01-15 11:02:10 -08:00
Nuno Campos eed577ee2a Update sqlite signature 2025-01-15 10:58:46 -08:00
Nuno Campos bba00506ea Lint 2025-01-15 10:57:26 -08:00
Nuno Campos fa12538a4e Lint 2025-01-15 10:50:11 -08:00
Nuno Campos c1a7bb8902 Lint 2025-01-15 10:49:47 -08:00
Nuno Campos 14b500d8a8 Fix 2025-01-15 10:47:09 -08:00
Nuno Campos 053a501db3 Add optional task_path arg for put_writes()
- Will be used for sorting pending_sends when available
2025-01-15 10:42:54 -08:00
Andrew NguonlyandGitHub bbe8eacf8d docs: Update Studio FAQ (#3048)
### Screenshot

![image](https://github.com/user-attachments/assets/6da2faa6-1c62-44e4-96f8-3450f94cb15c)
2025-01-15 10:23:43 -08:00
Nuno Campos 931d39124c Fix 2025-01-15 10:23:30 -08:00
Nuno Campos a7bb96da98 Fix 2025-01-15 10:21:40 -08:00
Nuno Campos 1e9a372dd7 Fix multiple interrupt/task test 2025-01-15 10:11:31 -08:00
Chester Curme 5030f1e89c bold -> headers 2025-01-15 13:02:59 -05:00
Nuno CamposandGitHub 50cb387904 Remove duckdb checkpointer and store (#3046)
- duckdb is too buggy to be able to provide reliable checkpointer and
store
2025-01-15 09:23:49 -08:00
Nuno Campos 003b1883c9 Remove duckdb checkpointer and store
- duckdb is too buggy to be able to provide reliable checkpointer and store
2025-01-15 09:15:17 -08:00
Nuno Campos 17aebb6239 Fix flasy return from task 2025-01-15 09:14:36 -08:00
Nuno CamposandGitHub d12830e95c Fix tracing hierarchy for imperative api (#3036) 2025-01-15 09:10:45 -08:00
Eugene YurtsevandNuno Campos 29b70cbf39 x 2025-01-15 09:03:46 -08:00
Eugene YurtsevandNuno Campos 6b86fbb0a8 x 2025-01-15 09:03:46 -08:00
Nuno CamposandGitHub 4426552c26 Fix flaky test output order (#3045) 2025-01-15 09:02:05 -08:00
Nuno Campos 671f268651 Remove duckdb checkpointer and store
- duckdb is too buggy to be able to provide reliable checkpointer and store
2025-01-15 08:56:32 -08:00
Vadym BardaandGitHub e2554c9616 langgraph: fix non-empty value check in ensure_config (#3039)
Fixes https://github.com/langchain-ai/langgraph/issues/2890
2025-01-15 16:54:31 +00:00
Nuno CamposandGitHub f1ee650489 Remove CI job to test against core 0.2.x (#3044) 2025-01-15 08:52:58 -08:00
Nuno Campos b7e4656c91 Fix flaky test output order 2025-01-15 08:45:35 -08:00
Nuno Campos 65a41942ef Remove CI job to test against core 0.2.x 2025-01-15 08:39:09 -08:00
Nuno Campos c767d86c9c Remove ci job for removed flag 2025-01-15 08:37:24 -08:00
Nuno Campos 01331ef858 Lint 2025-01-15 08:33:09 -08:00
Nuno CamposandGitHub 24a4c67c52 Merge branch 'main' into nc/14jan/fix-tracing-hierarchy-imperative 2025-01-15 08:31:38 -08:00
Nuno CamposandGitHub c7e43f86d4 Remove send v2 (#3033) 2025-01-15 08:29:05 -08:00
Vadym BardaandGitHub b18d266f2c langgraph: allow model names as strings in create_react_agent (#3031)
```python
agent = create_react_agent("anthropic:claude-3-5-sonnet-latest", [add])
agent.invoke({"messages": [("user", "what's 3 + 5")]})
```
2025-01-15 09:43:37 -05:00
Eugene YurtsevandGitHub 536ee7b37a docs: document inject kwarg (#3026) 2025-01-14 21:41:27 -05:00
Nuno Campos 901273c0e2 Lint 2025-01-14 17:09:30 -08:00
Nuno Campos 7bdbd62611 Fix tracing hierarchy for imperative api 2025-01-14 17:03:45 -08:00
Nuno Campos 0b74e25f72 Remove send v2 2025-01-14 15:49:57 -08:00
Eugene YurtsevandGitHub ff843ab005 docs: internal doc nits (#2946)
Update internal documentation
2025-01-14 23:16:38 +00:00
Eugene Yurtsev 7603809a9f x 2025-01-14 18:06:09 -05:00
Vadym BardaandGitHub f7fae7e140 langgraph: fix ismethod check in add_node (#3032)
Fixes #2893 #2965
2025-01-14 17:17:50 -05:00
Eugene YurtsevandGitHub 651ee8bd24 Update local-server.md (#3030) 2025-01-14 16:22:26 -05:00
Eugene YurtsevandGitHub 54bdba2da9 Update introduction.ipynb (#3029) 2025-01-14 16:20:54 -05:00
Eugene YurtsevandGitHub 7e5806cb0b Update local-server.md 2025-01-14 16:13:57 -05:00
Eugene YurtsevandGitHub 66f674fead Update introduction.ipynb 2025-01-14 16:12:20 -05:00
Vadym BardaandGitHub f029d615e6 ci: fix docs build (#3028) 2025-01-14 15:58:03 -05:00
Eugene Yurtsev bd76773b31 x 2025-01-14 15:50:20 -05:00
Eugene Yurtsev 311fe3970c x 2025-01-14 15:39:57 -05:00
Eugene Yurtsev 67a800dd98 update 2025-01-14 15:37:10 -05:00
ccurmeandGitHub 9b6e6d67dc layout (#2972)
Some docs layout improvements to help guide user journey.

Currently we have `Home | Tutorials | How-tos | Concepts | Reference` in
top-level horizontal navigation bar.

Here we make these updates:
- Top-level horizontal navigation bar is just `Home | API Reference`
- Add vertical sidebar to `Home` with sections:
  - Introduction
  - Get started
  - Guides
  - Resources

`Get Started` contains quickstarts for LG and LG Platform / deployment.
These are tutorials in Diataxis terms.

`Guides` contains index pages for how-tos, concepts, tutorials.

Advantage of this organization is that users are directed naturally down
the sidebar from Intro -> Get started -> How-tos, which is roughly how
we expect them to proceed.

This also makes deployment info more accessible as it is highlighted in
the "Getting started" section.

![Screenshot 2025-01-14 at 1 37
01 PM](https://github.com/user-attachments/assets/76dcde25-874b-4def-ad71-5313e399b7d7)
2025-01-14 15:29:59 -05:00
ccurmeandGitHub ecfb3e3850 Merge branch 'main' into eugene/langgraph_nav 2025-01-14 15:21:26 -05:00
Chester Curme ec71cd6fc1 update 2025-01-14 15:09:46 -05:00
Vadym BardaandGitHub aab4f0b164 checkpoint-duckdb: release 2.0.2 (#3024) 2025-01-14 15:08:49 -05:00
Chester Curme 2db6d9d7b5 clean up 2025-01-14 15:08:48 -05:00
Vadym BardaandGitHub 5db71a32bf checkpoint-duckdb: handle calling .list on async checkpointer (#3022) 2025-01-14 20:00:58 +00:00
Chester Curme 0f5df797af fix link 2025-01-14 15:00:38 -05:00
Vadym BardaandGitHub 4c0f855329 ci: fix docs runner (#3010) 2025-01-14 14:58:34 -05:00
Chester Curme 5dc3f14e8b populate troubleshootings nav 2025-01-14 14:56:23 -05:00
Chester Curme f25ea9ecaf populate tutorials nav 2025-01-14 14:50:05 -05:00
Chester Curme 9940a9f833 populate how-tos nav 2025-01-14 14:47:42 -05:00
Chester Curme d175d5e4a8 populate concepts nav 2025-01-14 14:42:10 -05:00
Vadym BardaandGitHub 74adbb744b bump checkpoint sqlite, postgres (#3021) 2025-01-14 14:41:22 -05:00
Chester Curme fe63440d98 add deployment landing page 2025-01-14 14:40:45 -05:00
William Fu-Hinthorn 2f47d98b34 Update error message for missing dev command 2025-01-14 11:28:51 -08:00
Vadym BardaandGitHub b989502c24 checkpoint-sqlite/postgres: handle calling .list on async checkpointer (#3019) 2025-01-14 19:06:11 +00:00
Eugene YurtsevandGitHub 8507dc33f0 FIx docs: Update MULTIPLE_SUBGRAPHS.md (#3016) 2025-01-14 13:41:50 -05:00
bracesproul 0d50c62283 cr 2025-01-14 10:39:57 -08:00
vbarda a9073241b2 add css for navbar depth 2025-01-14 13:31:39 -05:00
Chester Curme 0a04262f05 add custom css 2025-01-14 12:46:00 -05:00
Chester Curme 381796c474 hack 2025-01-14 12:39:38 -05:00
Vadym BardaandGitHub a11ba2b38a docs: update replay in the concept docs (#3017) 2025-01-14 11:53:53 -05:00
Chester Curme 09ae5ce05d update 2025-01-14 11:26:49 -05:00
Chester Curme 59e7751a8c update 2025-01-14 10:48:50 -05:00
Cesar William AlvarengaandGitHub a61ea101f6 fix: add missing END constant import (#3011) 2025-01-13 19:40:40 -05:00
Chester Curme b9816e31ff update 2025-01-13 19:12:51 -05:00
Chester Curme 3ea7f9469a update 2025-01-13 18:26:24 -05:00
Chester Curme 24e61fafa9 update 2025-01-13 16:22:58 -05:00
bracesproul b52b32b38e format n lint 2025-01-13 13:14:36 -08:00
Brace SproulandGitHub 5aefa5dc8c Merge branch 'main' into brace/interrupt-schema 2025-01-13 13:08:37 -08:00
Andrew NguonlyandGitHub c86155d3d3 docs: Add note about LangGraph Platform UI not available for self-hosted deployments (#3009)
Example screenshot:

![image](https://github.com/user-attachments/assets/8304b08d-bc9b-4cd3-8ff4-ed2471d3d6c7)
2025-01-13 13:05:18 -08:00
Chester Curme 2b370cf3b7 template applications -> lg plat 2025-01-13 14:21:12 -05:00
Chester Curme 839bea0f33 fixup 2025-01-13 14:16:14 -05:00
William FHandGitHub a03f1f7469 Bullseye (#3008) 2025-01-13 10:01:36 -08:00
Jimmy SambuoandGitHub d7199e5874 docs: use InjectedStore in semantic search guide (#2995)
When I tried to follow the How-to guide for [How to add semantic search
to your agent's
memory](https://langchain-ai.github.io/langgraph/how-tos/memory/semantic-search/#using-in-create_react_agent)
using `create_react_agent`, I got this error message when my agent used
the tool:

```python
1 validation error for upsert_memory
store
  Field required [type=missing, input_value={'content': '@jimmy works...ny.', 'memory_id': None}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.10/v/missingTraceback (most recent call last):

  File "/usr/local/lib/python3.9/site-packages/langchain_core/tools/base.py", line 688, in run
    tool_args, tool_kwargs = self._to_args_and_kwargs(tool_input, tool_call_id)

  File "/usr/local/lib/python3.9/site-packages/langchain_core/tools/base.py", line 611, in _to_args_and_kwargs
    tool_input = self._parse_input(tool_input, tool_call_id)

  File "/usr/local/lib/python3.9/site-packages/langchain_core/tools/base.py", line 532, in _parse_input
    result = input_args.model_validate(tool_input)

  File "/usr/local/lib/python3.9/site-packages/pydantic/main.py", line 627, in model_validate
    return cls.__pydantic_validator__.validate_python(

pydantic_core._pydantic_core.ValidationError: 1 validation error for upsert_memory
store
  Field required [type=missing, input_value={'content': '@jimmy works...ny.', 'memory_id': None}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.10/v/missing
```

I believe it’s because the graph did not inject the store into the tool
if we use `InjectedToolArg`.

When looking at the guide for [How to pass runtime values to
tools](https://langchain-ai.github.io/langgraph/how-tos/pass-run-time-values-to-tools/),
it suggests to use `InjectedStore` with `create_react_agent`. After
changing my code to use `InjectedStore`, my agent was able to save to
the store.
2025-01-13 09:59:46 -05:00
Siddhesh dosiandGitHub 61f2151df7 Update customer-support.ipynb minor spell (#3002)
There was spelling mistake.
2025-01-13 09:52:19 -05:00
William FHandGitHub 713528ffc3 Add admonition regarding dockerfile usage (#3001) 2025-01-12 09:52:13 -08:00
Andrew NguonlyandGitHub 7bd79c2509 docs: Add docs for LANGCHAIN_ENDPOINT for self-hosted deployments (#2988)
@langchain-infra, is this correct? Is this needed or is it
redundant/unnecessary?
2025-01-10 15:24:38 -08:00
Andrew NguonlyandGitHub 9974787df6 docs: Add API docs for POST /v1/projects/{project_id}/revisions/{revision_id}/deploy endpoint (#2994) 2025-01-10 15:16:48 -08:00
William FHandGitHub 638712a73b Add support for custom fetch implementation (#2993) 2025-01-10 23:08:33 +00:00
Vadym BardaandGitHub b8a54f6294 langgraph: release 0.2.62 (#2990) 2025-01-10 14:33:54 -05:00
Brace SproulandGitHub f2913fbcb6 fix(sdk-js): Release 0.0.35 (#2989) 2025-01-10 10:52:32 -08:00
bracesproul cfb121ee8f cr 2025-01-10 10:45:45 -08:00
bracesproul d27beeed18 move to prebuilt 2025-01-10 10:45:22 -08:00
bracesproul 8045e89e09 fix(sdk-js): Release 0.0.35 2025-01-10 10:43:44 -08:00
Brace SproulandGitHub 8355a1720a fix: Cron response types (#2987)
technically a breaking change, however the old response type was
incorrect.
2025-01-10 10:15:03 -08:00
bracesproul c302724394 fix cron create for thread return type 2025-01-10 10:05:57 -08:00
bracesproul c624ff69e1 fix: Cron response types 2025-01-10 10:01:51 -08:00
Vadym BardaandGitHub 10d46acc60 langgraph: add structured output to create_react_agent (#2848)
```python
class WeatherResponse(BaseModel):
    """Respond to the user with this"""

    temperature: float = Field(description="The temperature in fahrenheit")
    wind_direction: str = Field(
        description="The direction of the wind in abbreviated form"
    )
    wind_speed: float = Field(description="The speed of the wind in mph")

@tool
def get_weather(city: Literal["nyc", "sf"]):
    """Use this to get weather information."""
    if city == "nyc":
        return "It is cloudy in NYC, with 5 mph winds in the North-East direction and a temperature of 70 degrees"
    elif city == "sf":
        return "It is 75 degrees and sunny in SF, with 3 mph winds in the South-East direction"
    else:
        raise AssertionError("Unknown city")

model = ChatOpenAI()
tools = [get_weather]
agent_with_structured_output = create_react_agent(model, tools, response_format=WeatherResponse)
agent_with_structured_output.invoke({"messages": [("user", "what's the weather in nyc?")]})
```

```pycon
{
    'messages': [...],
    'structured_response': WeatherResponse(temperature=70.0, wind_directon='NE', wind_speed=5.0)
}
```
2025-01-10 16:06:59 +00:00
Vadym BardaandGitHub 35c3ba0104 docs: update how to for passing config to tools (#2986) 2025-01-10 11:05:15 -05:00
Hongbin MaoandGitHub 0e2cd9e289 Fix typo (#2984) 2025-01-10 10:52:52 -05:00
William FHandGitHub f4bd02da72 Make admonition more admonitiony (#2982) 2025-01-10 01:46:42 +00:00
William FHandGitHub ecfbfa1b90 Update auth docstrings (#2977) 2025-01-09 16:58:19 -08:00
Eugene Yurtsev fba5ca7ab9 x 2025-01-09 12:24:30 -05:00
Eugene Yurtsev f141089811 x 2025-01-09 11:11:04 -05:00
Eugene Yurtsev 38487cb158 x 2025-01-09 11:11:04 -05:00
bracesproul 8534212a25 cr 2025-01-07 10:15:48 -08:00
bracesproul e39255a792 feat: Add interrupt schema to library 2025-01-07 10:00:38 -08:00
409 changed files with 37193 additions and 25594 deletions
+1 -16
View File
@@ -17,21 +17,11 @@ jobs:
- "3.11"
- "3.12"
- "3.13"
core-version:
- "latest"
ff-send-v2:
- "false"
include:
- python-version: "3.11"
core-version: ">=0.2.42,<0.3.0"
- python-version: "3.11"
core-version: "latest"
ff-send-v2: "true"
defaults:
run:
working-directory: libs/langgraph
name: "test #${{ matrix.python-version }} (langchain-core: ${{ matrix.core-version }}, ff-send-v2: ${{ matrix.ff-send-v2 }})"
name: "test #${{ matrix.python-version }}"
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }} + Poetry ${{ env.POETRY_VERSION }}
@@ -51,14 +41,9 @@ jobs:
shell: bash
run: |
poetry install --with dev
if [ "${{ matrix.core-version }}" != "latest" ]; then
poetry run pip install "langchain-core${{ matrix.core-version }}"
fi
- name: Run tests
shell: bash
env:
LANGGRAPH_FF_SEND_V2: ${{ matrix.ff-send-v2 }}
run: |
make test_parallel
+36 -6
View File
@@ -31,7 +31,6 @@ jobs:
"libs/cli",
"libs/checkpoint",
"libs/checkpoint-sqlite",
"libs/checkpoint-duckdb",
"libs/checkpoint-postgres",
"libs/scheduler-kafka",
]
@@ -44,12 +43,12 @@ jobs:
name: cd ${{ matrix.working-directory }}
strategy:
matrix:
working-directory: [
working-directory:
[
"libs/cli",
"libs/checkpoint",
"libs/checkpoint-sqlite",
"libs/checkpoint-duckdb",
"libs/checkpoint-postgres"
"libs/checkpoint-postgres",
]
uses: ./.github/workflows/_test.yml
with:
@@ -76,7 +75,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
python-version: "3.11"
- name: Run check_sdk_methods script
run: python .github/scripts/check_sdk_methods.py
@@ -109,9 +108,40 @@ jobs:
- name: Build
run: yarn build
test-js:
runs-on: ubuntu-latest
strategy:
matrix:
working-directory:
- "libs/sdk-js"
defaults:
run:
working-directory: ${{ matrix.working-directory }}
steps:
- uses: actions/checkout@v3
- name: Setup Node.js (LTS)
uses: actions/setup-node@v3
with:
node-version: "20"
cache: "yarn"
cache-dependency-path: ${{ matrix.working-directory }}/yarn.lock
- name: Install dependencies
run: yarn install
- name: Run tests
run: yarn test
ci_success:
name: "CI Success"
needs: [lint, lint-js, test, test-langgraph, test-scheduler-kafka, integration-test]
needs:
[
lint,
lint-js,
test,
test-langgraph,
test-scheduler-kafka,
integration-test,
test-js,
]
if: |
always()
runs-on: ubuntu-latest
+8 -4
View File
@@ -9,7 +9,11 @@
permissions:
contents: read
defaults:
run:
working-directory: docs
jobs:
codespell:
name: (Check for spelling errors)
@@ -21,18 +25,18 @@
- name: Install Dependencies
run: |
pip install toml codespell jupytext
pip install toml codespell==2.3.0 jupytext
- name: Extract Ignore Words List
run: |
# Use a Python script to extract the ignore words list from pyproject.toml
python .github/workflows/extract_ignored_words_list.py
python ../.github/workflows/extract_ignored_words_list.py
id: extract_ignore_words
- name: Codespell
uses: codespell-project/actions-codespell@v2
with:
skip: '*.ambr,*.lock,*.ipynb,*.yaml,*.zlib'
skip: '*.ambr,*.lock,*.ipynb,*.yaml,*.zlib,*.md'
ignore_words_list: ${{ steps.extract_ignore_words.outputs.ignore_words_list }}
# We do this to avoid spellchecking cell outputs
- name: Codespell Notebooks
+32 -8
View File
@@ -21,6 +21,10 @@ concurrency:
group: "pages"
cancel-in-progress: false
defaults:
run:
working-directory: docs
jobs:
get-changed-files:
runs-on: ubuntu-latest
@@ -44,6 +48,7 @@ jobs:
deploy:
# needs: run-changed-notebooks
runs-on: ubuntu-latest
timeout-minutes: 10 # Job will be cancelled if it runs for more than 10 minutes
env:
GITHUB_TOKEN: ${{ secrets.MKDOCS_GITHUB_TOKEN }}
steps:
@@ -58,26 +63,42 @@ jobs:
poetry-version: ${{ env.POETRY_VERSION }}
cache-key: docs
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: "22"
cache: "yarn"
cache-dependency-path: docs/yarn.lock
- name: Install dependencies
run: |
poetry install --with test --no-root
yarn
poetry install --with test --with docs --no-root
poetry run pip install -U \
pytest \
pytest-check-links \
langsmith \
langchain \
GitPython \
"git+https://${GITHUB_TOKEN}@github.com/langchain-ai/mkdocs-material-insiders.git"
"git+https://${GITHUB_TOKEN}@github.com/langchain-ai/mkdocs-material-insiders.git" \
"git+https://github.com/benjamincburns/markdown-exec.git@cc0d39d737e5ffd4b83d23cd8729d7ea16e363c8"
poetry run jupyter kernelspec list
poetry run python3 -m ipykernel install --user --name=python3
npm install -g tslab
poetry run tslab install --python=python3
poetry run jupyter kernelspec list
- name: Lint Docs
# This step lints the docs using the existing linting set up.
# It should be very fast and should not require any external services.
run: make lint-docs
- name: Build llms-text
run: make llms-text
- name: Build site
run: make build-docs
env:
MKDOCS_GIT_COMMITTERS_APIKEY: ${{ secrets.MKDOCS_GIT_COMMITTERS_APIKEY }}
OPENAI_API_KEY: sf-proj-1234567890 # fake placeholder, shouldn't actually be used
ANTHROPIC_API_KEY: sk-ant-api03-1234567890 # fake placeholder, shouldn't actually be used
- name: Check links in notebooks
env:
LANGCHAIN_API_KEY: test
@@ -86,6 +107,7 @@ jobs:
echo "Running link check on all HTML files matching notebooks in docs directory..."
poetry run pytest -v \
--check-links-ignore "https://(api|web|docs)\.smith\.langchain\.com/.*" \
--check-links-ignore "https://academy\.langchain\.com/.*" \
--check-links-ignore "https://x.com/.*" \
--check-links-ignore "https://github\.com/.*" \
--check-links-ignore "http://localhost:8123/.*" \
@@ -94,19 +116,21 @@ jobs:
--check-links-ignore "/.*\.(ipynb|html)$" \
--check-links-ignore "https://python\.langchain\.com/.*" \
--check-links-ignore "https://openai\.com/.*" \
--check-links-ignore "https://www\.uber\.com/.*" \
--check-links-ignore "https://pepy\.tech/.*" \
--check-links $(find docs/site -name "index.html" | grep -v 'storm/index.html')
--check-links $(find site -name "index.html" | grep -v 'storm/index.html')
else
echo "Fetching changes from origin/main..."
git fetch origin main
echo "Checking for changed notebook files..."
CHANGED_FILES=$(git diff --name-only --diff-filter=d origin/main | grep 'docs/docs/.*\.ipynb$' | grep -v 'storm.ipynb' | sed -E 's|^docs/docs/|docs/site/|; s/\.ipynb$/\/index.html/' || true)
CHANGED_FILES=$(git diff --name-only --diff-filter=d origin/main | grep 'docs/docs/.*\.ipynb$' | grep -v 'storm.ipynb' | sed -E 's|^docs/docs/|site/|; s/\.ipynb$/\/index.html/' || true)
echo "Changed files: ${CHANGED_FILES}"
if [ -n "${CHANGED_FILES}" ]; then
echo "Running link check on HTML files matching changed notebook files..."
poetry run pytest -v \
--check-links-ignore "https://(api|web|docs)\.smith\.langchain\.com/.*" \
--check-links-ignore "https://academy\.langchain\.com/.*" \
--check-links-ignore "http://localhost:8123/.*" \
--check-links-ignore "http://localhost:2024.*" \
--check-links-ignore "http://127.0.0.1:.*" \
@@ -125,7 +149,7 @@ jobs:
uses: actions/configure-pages@v4
- name: Upload Pages Artifact
if: github.ref == 'refs/heads/main'
# if: github.ref == 'refs/heads/main'
uses: actions/upload-pages-artifact@v3
with:
path: ./docs/site/
@@ -1,6 +1,6 @@
import toml
pyproject_toml = toml.load("libs/langgraph/pyproject.toml")
pyproject_toml = toml.load("pyproject.toml")
# Extract the ignore words list (adjust the key as per your TOML structure)
ignore_words_list = (
+10 -6
View File
@@ -11,6 +11,10 @@ on:
schedule:
- cron: '0 13 * * *'
defaults:
run:
working-directory: docs
jobs:
build:
runs-on: ubuntu-latest
@@ -39,14 +43,14 @@ jobs:
- name: Pre-download tiktoken files
run: |
poetry run python docs/_scripts/download_tiktoken.py
poetry run python _scripts/download_tiktoken.py
- name: Prepare notebooks
run: |
if [ "${{ matrix.lib-version }}" = "development" ]; then
poetry run python docs/_scripts/prepare_notebooks_for_ci.py --comment-install-cells
poetry run python _scripts/prepare_notebooks_for_ci.py --comment-install-cells
else
poetry run python docs/_scripts/prepare_notebooks_for_ci.py
poetry run python _scripts/prepare_notebooks_for_ci.py
fi
- name: Run notebooks
@@ -63,12 +67,12 @@ jobs:
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ] || [ "${{ github.event_name }}" = "schedule" ]; then
echo "Running all notebooks"
./docs/_scripts/execute_notebooks.sh
./_scripts/execute_notebooks.sh
else
CHANGED_FILES=$(echo '${{ inputs.changed-files }}' | tr ' ' '\n' | grep '\.ipynb$' || true)
CHANGED_FILES=$(echo '${{ inputs.changed-files }}' | tr ' ' '\n' | sed 's|^docs/docs/|docs/|' | grep '\.ipynb$' || true)
if [ -n "$CHANGED_FILES" ]; then
echo "Running changed notebooks: $CHANGED_FILES"
./docs/_scripts/execute_notebooks.sh $CHANGED_FILES
./_scripts/execute_notebooks.sh $CHANGED_FILES
else
echo "No notebook files changed, skipping execution"
fi
+2 -1
View File
@@ -178,4 +178,5 @@ Untitled*.ipynb
Chinook.db
libs/langgraph/out
.vercel
.turbo
-39
View File
@@ -1,39 +0,0 @@
.PHONY: lint-docs format-docs build-docs serve-docs serve-clean-docs clean-docs codespell build-typedoc
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 -m mkdocs build --clean -f docs/mkdocs.yml --strict
serve-clean-docs: clean-docs
poetry run python -m mkdocs serve -c -f docs/mkdocs.yml --strict -w ./libs/langgraph
serve-docs: build-typedoc
poetry run python -m mkdocs serve -f docs/mkdocs.yml -w ./libs/langgraph -w ./libs/checkpoint -w ./libs/sdk-py --dirty
clean-docs:
find ./docs/docs -name "*.ipynb" -type f -delete
rm -rf docs/site
## Run format against the project documentation.
format-docs:
poetry run ruff format docs/docs
poetry run ruff check --fix docs/docs
# Check the docs for linting violations
lint-docs:
poetry run ruff format --check docs/docs
poetry run ruff check docs/docs
codespell:
./docs/codespell_notebooks.sh .
start-services:
docker compose -f docs/test-compose.yml up -V --force-recreate --wait --remove-orphans
stop-services:
docker compose -f docs/test-compose.yml down
+176 -82
View File
@@ -12,25 +12,45 @@
## 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.
[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. Check out an introductory tutorial [here](https://langchain-ai.github.io/langgraph/tutorials/introduction/).
LangGraph is inspired by [Pregel](https://research.google/pubs/pub37252/) and [Apache Beam](https://beam.apache.org/). The public interface draws inspiration from [NetworkX](https://networkx.org/documentation/latest/). LangGraph is built by LangChain Inc, the creators of LangChain, but can be used without LangChain.
[LangGraph Platform](https://langchain-ai.github.io/langgraph/concepts/langgraph_platform) is infrastructure for deploying LangGraph agents. It is a commercial solution for deploying agentic applications to production, built on the open-source LangGraph framework. The LangGraph Platform consists of several components that work together to support the development, deployment, debugging, and monitoring of LangGraph applications: [LangGraph Server](https://langchain-ai.github.io/langgraph/concepts/langgraph_server) (APIs), [LangGraph SDKs](https://langchain-ai.github.io/langgraph/concepts/sdk) (clients for the APIs), [LangGraph CLI](https://langchain-ai.github.io/langgraph/concepts/langgraph_cli) (command line tool for building the server), [LangGraph Studio](https://langchain-ai.github.io/langgraph/concepts/langgraph_studio) (UI/debugger),
### Why use LangGraph?
To learn more about LangGraph, check out our first LangChain Academy course, *Introduction to LangGraph*, available for free [here](https://academy.langchain.com/courses/intro-to-langgraph).
LangGraph powers [production-grade agents](https://www.langchain.com/built-with-langgraph), trusted by Linkedin, Uber, Klarna, GitLab, and many more. LangGraph provides fine-grained control over both the flow and state of your agent applications. It implements a central [persistence layer](https://langchain-ai.github.io/langgraph/concepts/persistence/), enabling features that are common to most agent architectures:
### Key Features
- **Memory**: LangGraph persists arbitrary aspects of your application's state,
supporting memory of conversations and other updates within and across user
interactions;
- **Human-in-the-loop**: Because state is checkpointed, execution can be interrupted
and resumed, allowing for decisions, validation, and corrections at key stages via
human input.
- **Cycles and Branching**: Implement loops and conditionals in your apps.
- **Persistence**: Automatically save state after each step in the graph. Pause and resume the graph execution at any point to support error recovery, human-in-the-loop workflows, time travel and more.
- **Human-in-the-Loop**: Interrupt graph execution to approve or edit next action planned by the agent.
- **Streaming Support**: Stream outputs as they are produced by each node (including token streaming).
- **Integration with LangChain**: LangGraph integrates seamlessly with [LangChain](https://github.com/langchain-ai/langchain/) and [LangSmith](https://docs.smith.langchain.com/) (but does not require them).
Standardizing these components allows individuals and teams to focus on the behavior
of their agent, instead of its supporting infrastructure.
Through [LangGraph Platform](#langgraph-platform), LangGraph also provides tooling for
the development, deployment, debugging, and monitoring of your applications.
LangGraph integrates seamlessly with
[LangChain](https://python.langchain.com/docs/introduction/) and
[LangSmith](https://docs.smith.langchain.com/) (but does not require them).
To learn more about LangGraph, check out our first LangChain Academy
course, *Introduction to LangGraph*, available for free
[here](https://academy.langchain.com/courses/intro-to-langgraph).
### LangGraph Platform
LangGraph Platform is a commercial solution for deploying agentic applications to production, built on the open-source LangGraph framework.
[LangGraph Platform](https://langchain-ai.github.io/langgraph/concepts/langgraph_platform) is infrastructure for deploying LangGraph agents. It is a commercial solution for deploying agentic applications to production, built on the open-source LangGraph framework. The LangGraph Platform consists of several components that work together to support the development, deployment, debugging, and monitoring of LangGraph applications: [LangGraph Server](https://langchain-ai.github.io/langgraph/concepts/langgraph_server) (APIs), [LangGraph SDKs](https://langchain-ai.github.io/langgraph/concepts/sdk) (clients for the APIs), [LangGraph CLI](https://langchain-ai.github.io/langgraph/concepts/langgraph_cli) (command line tool for building the server), and [LangGraph Studio](https://langchain-ai.github.io/langgraph/concepts/langgraph_studio) (UI/debugger).
See deployment options [here](https://langchain-ai.github.io/langgraph/concepts/deployment_options/)
(includes a free tier).
Here are some common issues that arise in complex deployments, which LangGraph Platform addresses:
- **Streaming support**: LangGraph Server provides [multiple streaming modes](https://langchain-ai.github.io/langgraph/concepts/streaming) optimized for various application needs
@@ -47,9 +67,7 @@ pip install -U langgraph
## Example
One of the central concepts of LangGraph is state. Each graph execution creates a state that is passed between nodes in the graph as they execute, and each node updates this internal state with its return value after it executes. The way that the graph updates its internal state is defined by either the type of graph chosen or a custom function.
Let's take a look at a simple example of an agent that can use a search tool.
Let's build a tool-calling [ReAct-style](https://langchain-ai.github.io/langgraph/concepts/agentic_concepts/#react-implementation) agent that uses a search tool!
```shell
pip install langchain-anthropic
@@ -66,10 +84,72 @@ export LANGSMITH_TRACING=true
export LANGSMITH_API_KEY=lsv2_sk_...
```
```python
from typing import Annotated, Literal, TypedDict
The simplest way to create a tool-calling agent in LangGraph is to use `create_react_agent`:
<details open>
<summary>High-level implementation</summary>
```python
from langgraph.prebuilt import create_react_agent
from langgraph.checkpoint.memory import MemorySaver
from langchain_anthropic import ChatAnthropic
from langchain_core.tools import tool
# Define the tools for the agent to use
@tool
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."
tools = [search]
model = ChatAnthropic(model="claude-3-5-sonnet-latest", temperature=0)
# Initialize memory to persist state between graph runs
checkpointer = MemorySaver()
app = create_react_agent(model, tools, checkpointer=checkpointer)
# Use the agent
final_state = app.invoke(
{"messages": [{"role": "user", "content": "what is the weather in sf"}]},
config={"configurable": {"thread_id": 42}}
)
final_state["messages"][-1].content
```
```
"Based on the search results, I can tell you that the current weather in San Francisco is:\n\nTemperature: 60 degrees Fahrenheit\nConditions: Foggy\n\nSan Francisco is known for its microclimates and frequent fog, especially during the summer months. The temperature of 60°F (about 15.5°C) is quite typical for the city, which tends to have mild temperatures year-round. The fog, often referred to as "Karl the Fog" by locals, is a characteristic feature of San Francisco\'s weather, particularly in the mornings and evenings.\n\nIs there anything else you\'d like to know about the weather in San Francisco or any other location?"
```
Now when we pass the same <code>"thread_id"</code>, the conversation context is retained via the saved state (i.e. stored list of messages)
```python
final_state = app.invoke(
{"messages": [{"role": "user", "content": "what about ny"}]},
config={"configurable": {"thread_id": 42}}
)
final_state["messages"][-1].content
```
```
"Based on the search results, I can tell you that the current weather in New York City is:\n\nTemperature: 90 degrees Fahrenheit (approximately 32.2 degrees Celsius)\nConditions: Sunny\n\nThis weather is quite different from what we just saw in San Francisco. New York is experiencing much warmer temperatures right now. Here are a few points to note:\n\n1. The temperature of 90°F is quite hot, typical of summer weather in New York City.\n2. The sunny conditions suggest clear skies, which is great for outdoor activities but also means it might feel even hotter due to direct sunlight.\n3. This kind of weather in New York often comes with high humidity, which can make it feel even warmer than the actual temperature suggests.\n\nIt's interesting to see the stark contrast between San Francisco's mild, foggy weather and New York's hot, sunny conditions. This difference illustrates how varied weather can be across different parts of the United States, even on the same day.\n\nIs there anything else you'd like to know about the weather in New York or any other location?"
```
</details>
> [!TIP]
> LangGraph is a **low-level** framework that allows you to implement any custom agent
architectures. Click on the low-level implementation below to see how to implement a
tool-calling agent from scratch.
<details>
<summary>Low-level implementation</summary>
```python
from typing import Literal
from langchain_core.messages import HumanMessage
from langchain_anthropic import ChatAnthropic
from langchain_core.tools import tool
from langgraph.checkpoint.memory import MemorySaver
@@ -91,7 +171,7 @@ tools = [search]
tool_node = ToolNode(tools)
model = ChatAnthropic(model="claude-3-5-sonnet-20240620", temperature=0).bind_tools(tools)
model = ChatAnthropic(model="claude-3-5-sonnet-latest", temperature=0).bind_tools(tools)
# Define the function that determines whether to continue or not
def should_continue(state: MessagesState) -> Literal["tools", END]:
@@ -145,92 +225,102 @@ checkpointer = MemorySaver()
# Note that we're (optionally) passing the memory when compiling the graph
app = workflow.compile(checkpointer=checkpointer)
# Use the Runnable
# Use the agent
final_state = app.invoke(
{"messages": [HumanMessage(content="what is the weather in sf")]},
{"messages": [{"role": "user", "content": "what is the weather in sf"}]},
config={"configurable": {"thread_id": 42}}
)
final_state["messages"][-1].content
```
```
"Based on the search results, I can tell you that the current weather in San Francisco is:\n\nTemperature: 60 degrees Fahrenheit\nConditions: Foggy\n\nSan Francisco is known for its microclimates and frequent fog, especially during the summer months. The temperature of 60°F (about 15.5°C) is quite typical for the city, which tends to have mild temperatures year-round. The fog, often referred to as "Karl the Fog" by locals, is a characteristic feature of San Francisco\'s weather, particularly in the mornings and evenings.\n\nIs there anything else you\'d like to know about the weather in San Francisco or any other location?"
```
<b>Step-by-step Breakdown</b>:
Now when we pass the same `"thread_id"`, the conversation context is retained via the saved state (i.e. stored list of messages)
<details>
<summary>Initialize the model and tools.</summary>
<ul>
<li>
We use <code>ChatAnthropic</code> as our LLM. <strong>NOTE:</strong> we need to make sure the model knows that it has these tools available to call. We can do this by converting the LangChain tools into the format for OpenAI tool calling using the <code>.bind_tools()</code> method.
</li>
<li>
We define the tools we want to use - a search tool in our case. It is really easy to create your own tools - see documentation here on how to do that <a href="https://python.langchain.com/docs/how_to/custom_tools/">here</a>.
</li>
</ul>
</details>
```python
final_state = app.invoke(
{"messages": [HumanMessage(content="what about ny")]},
config={"configurable": {"thread_id": 42}}
)
final_state["messages"][-1].content
```
<details>
<summary>Initialize graph with state.</summary>
```
"Based on the search results, I can tell you that the current weather in New York City is:\n\nTemperature: 90 degrees Fahrenheit (approximately 32.2 degrees Celsius)\nConditions: Sunny\n\nThis weather is quite different from what we just saw in San Francisco. New York is experiencing much warmer temperatures right now. Here are a few points to note:\n\n1. The temperature of 90°F is quite hot, typical of summer weather in New York City.\n2. The sunny conditions suggest clear skies, which is great for outdoor activities but also means it might feel even hotter due to direct sunlight.\n3. This kind of weather in New York often comes with high humidity, which can make it feel even warmer than the actual temperature suggests.\n\nIt's interesting to see the stark contrast between San Francisco's mild, foggy weather and New York's hot, sunny conditions. This difference illustrates how varied weather can be across different parts of the United States, even on the same day.\n\nIs there anything else you'd like to know about the weather in New York or any other location?"
```
<ul>
<li>We initialize graph (<code>StateGraph</code>) by passing state schema (in our case <code>MessagesState</code>)</li>
<li><code>MessagesState</code> is a prebuilt state schema that has one attribute -- a list of LangChain <code>Message</code> objects, as well as logic for merging the updates from each node into the state.</li>
</ul>
</details>
### Step-by-step Breakdown
<details>
<summary>Define graph nodes.</summary>
1. <details>
<summary>Initialize the model and tools.</summary>
There are two main nodes we need:
- we use `ChatAnthropic` as our LLM. **NOTE:** we need make sure the model knows that it has these tools available to call. We can do this by converting the LangChain tools into the format for OpenAI tool calling using the `.bind_tools()` method.
- we define the tools we want to use - a search tool in our case. It is really easy to create your own tools - see documentation here on how to do that [here](https://python.langchain.com/docs/modules/agents/tools/custom_tools).
</details>
<ul>
<li>The <code>agent</code> node: responsible for deciding what (if any) actions to take.</li>
<li>The <code>tools</code> node that invokes tools: if the agent decides to take an action, this node will then execute that action.</li>
</ul>
</details>
2. <details>
<summary>Initialize graph with state.</summary>
<details>
<summary>Define entry point and graph edges.</summary>
- we initialize graph (`StateGraph`) by passing state schema (in our case `MessagesState`)
- `MessagesState` is a prebuilt state schema that has one attribute -- a list of LangChain `Message` objects, as well as logic for merging the updates from each node into the state
</details>
First, we need to set the entry point for graph execution - <code>agent</code> node.
3. <details>
<summary>Define graph nodes.</summary>
Then we define one normal and one conditional edge. Conditional edge means that the destination depends on the contents of the graph's state (<code>MessagesState</code>). In our case, the destination is not known until the agent (LLM) decides.
There are two main nodes we need:
<ul>
<li>Conditional edge: after the agent is called, we should either:
<ul>
<li>a. Run tools if the agent said to take an action, OR</li>
<li>b. Finish (respond to the user) if the agent did not ask to run tools</li>
</ul>
</li>
<li>Normal edge: after the tools are invoked, the graph should always return to the agent to decide what to do next</li>
</ul>
</details>
- The `agent` node: responsible for deciding what (if any) actions to take.
- The `tools` node that invokes tools: if the agent decides to take an action, this node will then execute that action.
</details>
<details>
<summary>Compile the graph.</summary>
4. <details>
<summary>Define entry point and graph edges.</summary>
<ul>
<li>
When we compile the graph, we turn it into a LangChain
<a href="https://python.langchain.com/docs/concepts/runnables/">Runnable</a>,
which automatically enables calling <code>.invoke()</code>, <code>.stream()</code> and <code>.batch()</code>
with your inputs
</li>
<li>
We can also optionally pass checkpointer object for persisting state between graph runs, and enabling memory,
human-in-the-loop workflows, time travel and more. In our case we use <code>MemorySaver</code> -
a simple in-memory checkpointer
</li>
</ul>
</details>
First, we need to set the entry point for graph execution - `agent` node.
<details>
<summary>Execute the graph.</summary>
Then we define one normal and one conditional edge. Conditional edge means that the destination depends on the contents of the graph's state (`MessageState`). In our case, the destination is not known until the agent (LLM) decides.
- Conditional edge: after the agent is called, we should either:
- a. Run tools if the agent said to take an action, OR
- b. Finish (respond to the user) if the agent did not ask to run tools
- Normal edge: after the tools are invoked, the graph should always return to the agent to decide what to do next
</details>
5. <details>
<summary>Compile the graph.</summary>
- When we compile the graph, we turn it into a LangChain [Runnable](https://python.langchain.com/v0.2/docs/concepts/#runnable-interface), which automatically enables calling `.invoke()`, `.stream()` and `.batch()` with your inputs
- We can also optionally pass checkpointer object for persisting state between graph runs, and enabling memory, human-in-the-loop workflows, time travel and more. In our case we use `MemorySaver` - a simple in-memory checkpointer
</details>
6. <details>
<summary>Execute the graph.</summary>
1. LangGraph adds the input message to the internal state, then passes the state to the entrypoint node, `"agent"`.
2. The `"agent"` node executes, invoking the chat model.
3. The chat model returns an `AIMessage`. LangGraph adds this to the state.
4. Graph cycles the following steps until there are no more `tool_calls` on `AIMessage`:
- If `AIMessage` has `tool_calls`, `"tools"` node executes
- The `"agent"` node executes again and returns `AIMessage`
5. Execution progresses to the special `END` value and outputs the final state.
And as a result, we get a list of all our chat messages as output.
</details>
<ol>
<li>LangGraph adds the input message to the internal state, then passes the state to the entrypoint node, <code>"agent"</code>.</li>
<li>The <code>"agent"</code> node executes, invoking the chat model.</li>
<li>The chat model returns an <code>AIMessage</code>. LangGraph adds this to the state.</li>
<li>Graph cycles the following steps until there are no more <code>tool_calls</code> on <code>AIMessage</code>:
<ul>
<li>If <code>AIMessage</code> has <code>tool_calls</code>, <code>"tools"</code> node executes</li>
<li>The <code>"agent"</code> node executes again and returns <code>AIMessage</code></li>
</ul>
</li>
<li>Execution progresses to the special <code>END</code> value and outputs the final state. And as a result, we get a list of all our chat messages as output.</li>
</ol>
</details>
</details>
## Documentation
@@ -240,6 +330,10 @@ final_state["messages"][-1].content
* [API Reference](https://langchain-ai.github.io/langgraph/reference/graphs/): Review important classes and methods, simple examples of how to use the graph and checkpointing APIs, higher-level prebuilt components and more.
* [LangGraph Platform](https://langchain-ai.github.io/langgraph/concepts/#langgraph-platform): LangGraph Platform is a commercial solution for deploying agentic applications in production, built on the open-source LangGraph framework.
## Resources
* [Built with LangGraph](https://www.langchain.com/built-with-langgraph): Hear how industry leaders use LangGraph to ship powerful, production-ready AI applications.
## Contributing
For more information on how to contribute, see [here](https://github.com/langchain-ai/langgraph/blob/main/CONTRIBUTING.md).
+2
View File
@@ -1,2 +1,4 @@
site/
docs/cloud/reference/sdk/js_ts_sdk_ref.md
.vercel
+67
View File
@@ -0,0 +1,67 @@
.PHONY: lint-docs format-docs build-docs serve-docs serve-clean-docs clean-docs codespell build-typedoc llms-text build-prebuilt
build-typedoc:
cd ../libs/sdk-js && yarn install --include-dev && yarn typedoc
cd ../libs/sdk-js && yarn --silent concat-md --decrease-title-levels --ignore=js_ts_sdk_ref.md --start-title-level-at 2 docs > ../../docs/docs/cloud/reference/sdk/js_ts_sdk_ref.md 2>/dev/null
# Add links to the monorepo
sed -e '1,10s|@langchain/langgraph-sdk|[@langchain/langgraph-sdk](https://github.com/langchain-ai/langgraph/tree/main/libs/sdk-js)|g' docs/cloud/reference/sdk/js_ts_sdk_ref.md > temp_file && mv temp_file docs/cloud/reference/sdk/js_ts_sdk_ref.md
build-prebuilt:
# Use to create an update to date prebuilt page.
# Looks up download stats for each of the prebuilt packages and
# generates the final prebuilt page.
poetry run python -m _scripts.third_party_page.get_download_stats stats.yml
poetry run python -m _scripts.third_party_page.create_third_party_page stats.yml docs/prebuilt.md --language python
build-docs: build-typedoc build-prebuilt
poetry run python -m mkdocs build --clean -f mkdocs.yml --strict
llms-text:
poetry run python _scripts/generate_llms_text.py docs/llms-full.txt
install-vercel-deps:
dnf install -y python3.11
curl -sSL https://install.python-poetry.org | python3 -
poetry self update 1.8.5
# don't use vercel's python - it wasn't compiled with sqlite support, and it fails when installing ipython's kernel
poetry env use /usr/bin/python3.11
poetry install --with docs --with test --no-root
poetry run pip install "git+https://github.com/benjamincburns/markdown-exec.git@cc0d39d737e5ffd4b83d23cd8729d7ea16e363c8"
poetry run python3 -m ipykernel install --name=python3
npm install -g tslab
poetry run tslab install --python=python3
poetry run jupyter kernelspec list
vercel-build-docs: install-vercel-deps
make build-docs
serve-clean-docs: clean-docs
poetry run python -m mkdocs serve -c -f mkdocs.yml --strict -w ../libs/langgraph
serve-docs: build-typedoc
poetry run python -m mkdocs serve -f mkdocs.yml -w ../libs/langgraph -w ../libs/checkpoint -w ../libs/sdk-py --dirty
clean-docs:
find ./docs -name "*.ipynb" -type f -delete
rm -rf site
## Run format against the project documentation.
format-docs:
poetry run ruff format docs
poetry run ruff check --fix docs
# Check the docs for linting violations
lint-docs:
poetry run ruff format --check docs
poetry run ruff check docs
codespell:
./codespell_notebooks.sh .
start-services:
docker compose -f test-compose.yml up -V --force-recreate --wait --remove-orphans
stop-services:
docker compose -f test-compose.yml down
+7 -9
View File
@@ -19,23 +19,21 @@ make serve-docs
If you would like to automatically execute all of the notebooks, to mimic the "Run notebooks" GHA, you can run:
```bash
python docs/_scripts/prepare_notebooks_for_ci.py
./docs/_scripts/execute_notebooks.sh
python _scripts/prepare_notebooks_for_ci.py
./_scripts/execute_notebooks.sh
```
**Note**: if you want to run the notebooks without `%pip install` cells, you can run:
```bash
python docs/_scripts/prepare_notebooks_for_ci.py --comment-install-cells
./docs/_scripts/execute_notebooks.sh
python _scripts/prepare_notebooks_for_ci.py --comment-install-cells
./_scripts/execute_notebooks.sh
```
`prepare_notebooks_for_ci.py` script will add VCR cassette context manager for each cell in the notebook, so that:
* when the notebook is run for the first time, cells with network requests will be recorded to a VCR cassette file
* when the notebook is run subsequently, the cells with network requests will be replayed from the cassettes
**Note**: this is currently limited only to the notebooks in `docs/docs/how-tos`
## Adding new notebooks
If you are adding a notebook with API requests, it's **recommended** to record network requests so that they can be subsequently replayed. If this is not done, the notebook runner will make API requests every time the notebook is run, which can be costly and slow.
@@ -48,14 +46,14 @@ Then, run
jupyter execute <path_to_notebook>
```
Once the notebook is executed, you should see the new VCR cassettes recorded in `docs/cassettes` directory and discard the updated notebook.
Once the notebook is executed, you should see the new VCR cassettes recorded in `cassettes` directory and discard the updated notebook.
## Updating existing notebooks
If you are updating an existing notebook, please make sure to remove any existing cassettes for the notebook in `docs/cassettes` directory (each cassette is prefixed with the notebook name), and then run the steps from the "Adding new notebooks" section above.
If you are updating an existing notebook, please make sure to remove any existing cassettes for the notebook in `cassettes` directory (each cassette is prefixed with the notebook name), and then run the steps from the "Adding new notebooks" section above.
To delete cassettes for a notebook, you can run:
```bash
rm docs/cassettes/<notebook_name>*
rm cassettes/<notebook_name>*
```
+2 -2
View File
@@ -31,7 +31,7 @@ def request(self, method, url, body=None, headers=None):
The result of calling the parent request method.
"""
# Update the inner socket's timeout value to send the request.
# This only triggers if the connection is re-used.
# This only triggers if the connection is reused.
if getattr(self, "sock", None) is not None:
self.sock.settimeout(self.timeout)
@@ -90,4 +90,4 @@ def patch_urllib3():
return request(self, *args, **kwargs)
connection.HTTPConnection.request = new_request
_PATCHED = True
_PATCHED = True
+157
View File
@@ -0,0 +1,157 @@
"""Add typescript translation to a given markdown file."""
import argparse
import re
import requests
from langchain_anthropic import ChatAnthropic
URL = "https://gist.githubusercontent.com/eyurtsev/e7486731415463a9bc5b4682358859c8/raw/b5a5fda9c7e3387cfcb781f25082814d43675d50/gistfile1.txt"
response = requests.get(URL)
response.raise_for_status()
reference_snippets = response.text
model = ChatAnthropic(model="claude-3-5-sonnet-latest")
def _get_tqdm():
try:
from tqdm import tqdm
except ImportError:
# If not available return a simple identity function
def tqdm(iterable, *args, **kwargs):
return iterable
return tqdm
_tqdm = _get_tqdm()
opening_pattern = re.compile(r"^\s*```python(?:\s+.*)?\s*$")
closing_pattern = re.compile(r"^\s*```\s*$")
def extract_python_snippets(markdown: str) -> list[str]:
"""
Extract all python code blocks (including their fence lines) from the markdown content.
A python block is defined as any block that starts with a line containing an opening fence
with '```python' (optionally with extra parameters) and ends with a closing fence '```'.
"""
snippets = []
inside_block = False
current_snippet = []
for line in markdown.splitlines(keepends=True):
if not inside_block:
if opening_pattern.match(line):
inside_block = True
current_snippet = [line]
else:
current_snippet.append(line)
if closing_pattern.match(line):
inside_block = False
snippets.append("".join(current_snippet))
current_snippet = []
return snippets
def translate_snippet(python_snippet: str) -> str:
"""Translate a python code block into a TypeScript code block using Langchain.
The response is expected to be a properly fenced TypeScript code block (i.e.
starting with ```typescript and ending with ```).
"""
ai_message = model.invoke(
[
{
"role": "system",
"content": (
f"You have access to the following up-to-date example TypeScript code "
f"snippets that show examples of building with langgraph "
f"and langchain:\n\n{reference_snippets}\n\n"
"Use this context to translate the following Python code to equivalent "
"TypeScript. Ensure that your output is a valid fenced TypeScript "
"code block (i.e. starts with ```typescript and ends with ```)."
),
},
{
"role": "user",
"content": f"Translate this Python snippet to TypeScript:\n\n{python_snippet}",
},
]
)
# Use a regular expression to search for a TypeScript code block in the response.
pattern = r"```typescript\s*(.*?)\s*```"
match = re.search(pattern, ai_message.content, re.DOTALL)
if match:
# Reconstruct the code block with proper fences.
typescript_code = match.group(1).strip()
return f"```typescript\n{typescript_code}\n```"
else:
raise ValueError("No TypeScript code block found in the model's response.")
def insert_translations_into_markdown(
markdown: str, typescript_snippets: list[str]
) -> str:
"""Walks through the original markdown content and, after each
Python snippet block, inserts the corresponding translated TypeScript snippet.
It assumes that the ordering of the Python snippets
(from extract_python_snippets) matches the order they appear in the markdown.
"""
output_lines = []
lines = markdown.splitlines(keepends=True)
inside_block = False
snippet_index = 0
for line in lines:
output_lines.append(line)
if not inside_block and opening_pattern.match(line):
# We've encountered the start of a python code block.
inside_block = True
elif inside_block:
if closing_pattern.match(line):
# End of a python snippet block.
inside_block = False
if snippet_index < len(typescript_snippets):
# Insert an extra newline for clarity, then the translated TypeScript snippet.
output_lines.append("\n")
output_lines.append(typescript_snippets[snippet_index])
output_lines.append("\n")
snippet_index += 1
return "".join(output_lines)
def main(file_path: str) -> None:
# Read the markdown file.
with open(file_path, "r") as f:
markdown_content = f.read()
# 1. Extract all Python snippets.
python_snippets = extract_python_snippets(markdown_content)[:1]
# 2. Translate each Python snippet to TypeScript.
typescript_snippets = []
# Replace with .batch() for faster translation
for python_snippet in _tqdm(python_snippets):
ts_snippet = translate_snippet(python_snippet)
typescript_snippets.append(ts_snippet)
# 3. Insert the TypeScript translations after their respective Python snippets.
updated_markdown = insert_translations_into_markdown(
markdown_content, typescript_snippets
)
# Overwrite the original markdown file with the updated content.
with open(file_path, "w") as f:
f.write(updated_markdown)
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Translate Python snippets in a markdown file to TypeScript and insert them after each Python snippet."
)
parser.add_argument("file_path", type=str, help="Path to the markdown file.")
args = parser.parse_args()
main(args.file_path)
@@ -0,0 +1,75 @@
import nock, { Definition } from "nock";
import msgpack from "msgpack-lite";
import zlib from "node:zlib";
import fs from "node:fs/promises";
import { Buffer } from "node:buffer";
// deno style imports here because we're running this in the deno jupyter kernel
interface NockCassetteData {
hash: string;
entries: Definition[];
}
// Utility functions for compression & serialization
function compressData(data: NockCassetteData, compressionLevel = 9): string {
const packed = msgpack.encode(data);
const compressed = zlib.deflateSync(packed, { level: compressionLevel });
return compressed.toString("base64");
}
function decompressData(compressedString: string): NockCassetteData {
const decoded = Buffer.from(compressedString, "base64");
const decompressed = zlib.inflateSync(decoded);
return msgpack.decode(decompressed) as NockCassetteData;
}
// deno-lint-ignore no-unused-vars
class HashedCassette {
private recording = true;
constructor(
private readonly cassettePath: string,
private readonly hash: string
) {}
async enter() {
try {
const rawCassette = await fs.readFile(this.cassettePath, "utf-8");
const data = decompressData(rawCassette);
if (data.hash === this.hash) {
this.recording = false;
nock.disableNetConnect();
nock.define(data.entries);
return;
}
} catch (error) {
if (error instanceof Error && error.message.includes("ENOENT")) {
this.recording = true;
} else {
throw error;
}
}
nock.recorder.rec({
dont_print: true,
output_objects: true,
});
}
async exit() {
if (this.recording) {
const entries = nock.recorder.play() as Definition[];
const data = {
hash: this.hash,
entries,
};
const compressed = compressData(data);
await fs.writeFile(this.cassettePath, compressed);
} else {
nock.enableNetConnect();
nock.restore();
nock.cleanAll();
}
}
}
+107
View File
@@ -0,0 +1,107 @@
import base64
import os
import zlib
from types import TracebackType
from typing import Optional, Any, Type
import msgpack
import vcr
os.environ.pop("LANGCHAIN_TRACING_V2", None)
custom_vcr = vcr.VCR()
def compress_data(data: Any, compression_level: int = 9) -> str:
packed = msgpack.packb(data, use_bin_type=True)
compressed = zlib.compress(packed, level=compression_level)
return base64.b64encode(compressed).decode("utf-8")
def decompress_data(compressed_string: str) -> Any:
decoded = base64.b64decode(compressed_string)
decompressed = zlib.decompress(decoded)
return msgpack.unpackb(decompressed, raw=False)
class AdvancedCompressedSerializer:
def serialize(self, cassette_dict: Any) -> str:
return compress_data(cassette_dict)
def deserialize(self, cassette_string: str) -> Any:
return decompress_data(cassette_string)
custom_vcr.register_serializer("advanced_compressed", AdvancedCompressedSerializer())
custom_vcr.serializer = "advanced_compressed"
class HashedCassette:
def __init__(self, cassette_path: str, hash_value: str) -> None:
"""A context manager for using VCR cassettes with an embedded hash value.
Args:
cassette_path (str): The file path of the cassette (independent of hash).
hash_value (str): The expected hash value (e.g. a uuid string).
This class provides a context manager for using VCR cassettes with an embedded hash value.
The hash value is used to ensure that the cassette matches the expected state, and if not,
the cassette is removed or updated with the new hash value.
"""
self.cassette_path: str = cassette_path
self.hash_value: str = hash_value
self.vcr: vcr.VCR = custom_vcr
self.cassette_context: Optional[Any] = None
self.exited: bool = False
def __enter__(self) -> Any:
self.exited: bool = False
# Get the serializer instance from the VCR instance.
serializer = self.vcr.serializers[self.vcr.serializer]
# If the cassette file exists, check its embedded hash.
if os.path.exists(self.cassette_path):
with open(self.cassette_path, "r") as f:
content = f.read()
try:
cassette_data = serializer.deserialize(content)
except Exception as e:
os.remove(self.cassette_path)
else:
existing_hash = cassette_data.get("cassette_hash")
if existing_hash != self.hash_value:
os.remove(self.cassette_path)
# Now enter the VCR cassette context.
self.cassette_context = custom_vcr.use_cassette(
self.cassette_path,
filter_headers=["x-api-key", "authorization"],
record_mode="once",
serializer="advanced_compressed",
)
return self.cassette_context.__enter__()
def __exit__(
self,
exc_type: Optional[Type[BaseException]] = None,
exc_val: Optional[BaseException] = None,
exc_tb: Optional[TracebackType] = None,
) -> Optional[bool]:
if self.exited:
return
self.exited = True
# Exit the VCR cassette context.
result = self.cassette_context.__exit__(exc_type, exc_val, exc_tb)
serializer = self.vcr.serializers[self.vcr.serializer]
# If a cassette was recorded (or updated), open and update its hash.
if os.path.exists(self.cassette_path):
with open(self.cassette_path, "r") as f:
content = f.read()
try:
cassette_data = serializer.deserialize(content)
except Exception as e:
return result
# Update the cassette data with the expected hash.
if cassette_data.get("cassette_hash") != self.hash_value:
cassette_data["cassette_hash"] = self.hash_value
serialized_data = serializer.serialize(cassette_data)
with open(self.cassette_path, "w") as f:
f.write(serialized_data)
return result
+2 -2
View File
@@ -1,7 +1,7 @@
#!/bin/bash
# Read the list of notebooks to skip from the JSON file
SKIP_NOTEBOOKS=$(python -c "import json; print('\n'.join(json.load(open('docs/notebooks_no_execution.json'))))")
SKIP_NOTEBOOKS=$(python -c "import json; print('\n'.join(json.load(open('notebooks_no_execution.json'))))")
# Function to execute a single notebook
execute_notebook() {
@@ -27,7 +27,7 @@ if [ $# -gt 0 ]; then
notebooks=$(echo "$@" | tr ' ' '\n' | grep -vFf <(echo "$SKIP_NOTEBOOKS"))
else
# Find all notebooks and filter out those in the skip list
notebooks=$(find docs/docs/tutorials docs/docs/how-tos -name "*.ipynb" | grep -v ".ipynb_checkpoints" | grep -vFf <(echo "$SKIP_NOTEBOOKS"))
notebooks=$(find docs/tutorials docs/how-tos -name "*.ipynb" | grep -v ".ipynb_checkpoints" | grep -vFf <(echo "$SKIP_NOTEBOOKS"))
fi
# Execute notebooks sequentially
+6 -11
View File
@@ -1,17 +1,11 @@
import importlib
import inspect
import logging
import os
import re
from typing import List, Literal, Optional
from typing_extensions import TypedDict
from functools import lru_cache
from typing import List, Literal, Optional
import nbformat
from nbconvert.preprocessors import Preprocessor
from typing_extensions import TypedDict
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@@ -52,6 +46,8 @@ MANUAL_API_REFERENCES_LANGGRAPH = [
(["langgraph.constants"], "langgraph.types", "Interrupt", "types"),
(["langgraph.constants"], "langgraph.types", "interrupt", "types"),
(["langgraph.constants"], "langgraph.types", "Command", "types"),
(["langgraph.func"], "langgraph.func", "entrypoint", "func"),
(["langgraph.func"], "langgraph.func", "task", "func"),
([], "langgraph.types", "RetryPolicy", "types"),
([], "langgraph.checkpoint.base", "Checkpoint", "checkpoints"),
([], "langgraph.checkpoint.base", "CheckpointMetadata", "checkpoints"),
@@ -88,8 +84,6 @@ _IMPORT_LANGCHAIN_RE = _make_regular_expression("langchain")
_IMPORT_LANGGRAPH_RE = _make_regular_expression("langgraph")
@lru_cache(maxsize=10_000)
def _get_full_module_name(module_path: str, class_name: str) -> Optional[str]:
"""Get full module name using inspect, with LRU cache to memoize results."""
@@ -109,6 +103,7 @@ def _get_full_module_name(module_path: str, class_name: str) -> Optional[str]:
logger.warning(f"API Reference: Failed to load for class {class_name}, {e}")
return None
def _get_doc_title(data: str, file_name: str) -> str:
try:
return re.findall(r"^#\s*(.*)", data, re.MULTILINE)[0]
@@ -287,4 +282,4 @@ def update_markdown_with_imports(markdown: str) -> str:
# Apply the replace_code_block function to all matches in the markdown
updated_markdown = code_block_pattern.sub(replace_code_block, markdown)
return updated_markdown
return updated_markdown
+91
View File
@@ -0,0 +1,91 @@
"""Experimental script to generate consolidated llms text from the docs."""
import glob
import os
import pathlib
from mkdocs.structure.files import File
from mkdocs.structure.pages import Page
from notebook_hooks import _on_page_markdown_with_config
HERE = os.path.dirname(os.path.abspath(__file__))
# Get source directory (parent of HERE / docs)
SOURCE_DIR = os.path.abspath(os.path.join(os.path.dirname(HERE), "docs"))
def _make_llms_text(output_file: str) -> str:
"""Generate a consolidated text file from markdown/notebook files for LLM training.
Args:
output_file: Path to output the consolidated text file
"""
# Collect all markdown and notebook files
relative_paths = [
# Files relative to docs/docs/
"tutorials/introduction.ipynb",
]
all_files = [os.path.join(SOURCE_DIR, path) for path in relative_paths]
all_files.extend(
glob.glob(os.path.join(SOURCE_DIR, "how-tos/*.md"), recursive=True)
)
all_files.extend(
glob.glob(os.path.join(SOURCE_DIR, "how-tos/*.ipynb"), recursive=True)
)
# Add all concepts
all_files.extend(
glob.glob(os.path.join(SOURCE_DIR, "concepts/*.md"), recursive=True)
)
all_files.extend(
glob.glob(os.path.join(SOURCE_DIR, "concepts/*.ipynb"), recursive=True)
)
all_content = []
# Process each file
for file_path in all_files:
print(f"Processing {file_path}")
rel_path = os.path.relpath(file_path, SOURCE_DIR)
# Create File and Page objects to match mkdocs structure
file_obj = File(
path=rel_path, src_dir=SOURCE_DIR, dest_dir="", use_directory_urls=True
)
page = Page(
title="",
file=file_obj,
config={},
)
# Read raw content
with open(file_path, "r", encoding="utf-8") as f:
content = f.read()
# Convert to markdown without logic to resolve API references
processed_content = _on_page_markdown_with_config(
content, page, add_api_references=False, remove_base64_images=True
)
if processed_content:
# Add file name
all_content.append(f"---\n{rel_path}\n---")
# Add content
all_content.append(processed_content)
# Write consolidated output
with open(output_file, "w", encoding="utf-8") as f:
f.write("\n\n".join(all_content))
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(
description=(
"Generate consolidated text file from markdown/notebook files for LLMs."
)
)
parser.add_argument("output_file", help="Path to output the consolidated text file")
args = parser.parse_args()
_make_llms_text(args.output_file)
+133 -8
View File
@@ -1,6 +1,8 @@
import argparse
import os
import re
from pathlib import Path
from typing import Literal, Optional
import nbformat
from nbconvert.exporters import MarkdownExporter
@@ -8,21 +10,45 @@ from nbconvert.preprocessors import Preprocessor
class EscapePreprocessor(Preprocessor):
def __init__(self, rewrite_links: bool = True, **kwargs) -> None:
super().__init__(**kwargs)
self.rewrite_links = rewrite_links
def preprocess_cell(self, cell, resources, cell_index):
if cell.cell_type == "markdown":
# rewrite markdown links to html links (excluding image links)
cell.source = re.sub(
r"(?<!!)\[([^\]]*)\]\((?![^\)]*//)([^)]*)(?:\.ipynb)?\)",
r'<a href="\2">\1</a>',
cell.source,
)
if self.rewrite_links:
# We'll need to adjust the logic for this to keep markdown format
# but link to markdown files rather than ipynb files.
cell.source = re.sub(
r"(?<!!)\[([^\]]*)\]\((?![^\)]*//)([^)]*)(?:\.ipynb)?\)",
r'<a href="\2">\1</a>',
cell.source,
)
else:
# Keep format but replace the .ipynb extension with .md
cell.source = re.sub(
r"(?<!!)\[([^\]]*)\]\((?![^\)]*//)([^)]*)(?:\.ipynb)?\)",
r"[\1](\2.md)",
cell.source,
)
# Fix image paths in <img> tags
cell.source = re.sub(
r'<img\s+src="\.?/img/([^"]+)"', r'<img src="../img/\1"', cell.source
)
elif cell.cell_type == "code":
# Determine if the cell has bash or cell magic
if cell.source.startswith("%") or cell.source.startswith("!"):
# update metadata to denote that it's not a python cell
cell.metadata["language_info"] = {"name": "unknown"}
# Remove noqa comments
cell.source = re.sub(r"#\s*noqa.*$", "", cell.source, flags=re.MULTILINE)
# escape ``` in code
# This is needed because the markdown exporter will wrap code blocks in
# triple backticks, which will break the markdown output if the code block
# contains triple backticks.
cell.source = cell.source.replace("```", r"\`\`\`")
# escape ``` in output
if "outputs" in cell:
@@ -112,12 +138,111 @@ exporter = MarkdownExporter(
],
)
md_executable = MarkdownExporter(
preprocessors=[
ExtractAttachmentsPreprocessor,
EscapePreprocessor(rewrite_links=False),
],
template_name="md_executable",
extra_template_basedirs=[
os.path.join(os.path.dirname(__file__), "notebook_convert_templates")
],
)
def convert_notebook(
notebook_path: Path,
) -> Path:
mode: Literal["markdown", "exec"] = "markdown",
) -> str:
with open(notebook_path) as f:
nb = nbformat.read(f, as_version=4)
body, _ = exporter.from_notebook_node(nb)
nb.metadata.mode = mode
if mode == "markdown":
body, _ = exporter.from_notebook_node(nb)
else:
body, _ = md_executable.from_notebook_node(nb)
return body
HERE = Path(__file__).parent
DOCS = HERE.parent / "docs"
# Convert notebooks to markdown
def _convert_notebooks(
*,
output_dir: Optional[Path] = None,
replace: bool = False,
pattern: str = "*.ipynb",
) -> None:
"""Converting notebooks."""
if not output_dir and not replace:
raise ValueError("Either --output_dir or --replace must be specified")
output_dir_path = DOCS if replace else Path(output_dir)
notebooks = list(DOCS.rglob(pattern))
file_names = [notebook.name for notebook in notebooks]
for notebook in notebooks:
markdown = convert_notebook(notebook, mode="exec")
markdown_path = output_dir_path / notebook.relative_to(DOCS).with_suffix(".md")
markdown_path.parent.mkdir(parents=True, exist_ok=True)
with open(markdown_path, "w") as f:
f.write(markdown)
if replace:
notebook.unlink(missing_ok=False)
if replace:
# The regex will match markdown links that point to *.ipynb files.
# It captures:
# group(1): the link text (inside the square brackets)
# group(2): the file path (without the trailing .ipynb)
link_pattern = r"(?<!!)\[([^\]]+)\]\((?![^)]*//)([^)]+)\.ipynb\)"
def replace_link(match: re.Match) -> str:
link_text = match.group(1)
link_target = match.group(2)
# Reconstruct the file name with the .ipynb extension.
# For example, if link_target is "foo/bar", then linked_file becomes "bar.ipynb".
linked_file = Path(link_target).name + ".ipynb"
# Only update if the notebook was among those converted.
if linked_file in file_names:
# Change the extension from .ipynb to .md
return f"[{link_text}]({link_target}.md)"
# Otherwise, leave the original link intact.
return match.group(0)
# Process all markdown files in the output directory.
for path in output_dir_path.rglob("*.md"):
with open(path, "r", encoding="utf-8") as f:
content = f.read()
new_content = re.sub(link_pattern, replace_link, content)
with open(path, "w", encoding="utf-8") as f:
f.write(new_content)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Convert notebooks to markdown")
parser.add_argument(
"--output_dir",
default=None,
help="Directory to output markdown files",
)
parser.add_argument(
"--replace",
action="store_true",
help="Replace original notebooks with markdown files",
)
parser.add_argument(
"--pattern",
default="*.ipynb",
help="Glob pattern to match notebooks to convert",
)
args = parser.parse_args()
_convert_notebooks(
replace=args.replace,
output_dir=args.output_dir,
pattern=args.pattern,
)
@@ -0,0 +1,5 @@
{
"mimetypes": {
"text/markdown": true
}
}
@@ -0,0 +1,36 @@
{#https://github.com/rdbisme/nbconvert/blob/master/share/jupyter/nbconvert/templates/markdown/index.md.j2#}
{% extends 'markdown/index.md.j2' %}
{% block input %}
```
{%- if 'magics_language' in cell.metadata -%}
{{ cell.metadata.magics_language}}
{%- elif 'name' in nb.metadata.get('language_info', {}) -%}
{{ nb.metadata.language_info.name }} exec="on" source="above" session="1"
{%- endif %}
{{ cell.source}}
```
{% endblock input %}
{%- block traceback_line -%}
{%- endblock traceback_line -%}
{%- block stream -%}
{%- endblock stream -%}
{%- block data_text scoped -%}
{%- endblock data_text -%}
{%- block data_html scoped -%}
```html
{{ output.data['text/html'] | safe }}
```
{%- endblock data_html -%}
{%- block data_jpg scoped -%}
![](data:image/jpg;base64,{{ output.data['image/jpeg'] }})
{%- endblock data_jpg -%}
{%- block data_png scoped -%}
![](data:image/png;base64,{{ output.data['image/png'] }})
{%- endblock data_png -%}
+241 -7
View File
@@ -1,13 +1,21 @@
import logging
import os
import re
from typing import Any, Dict
import traceback
from typing import Any, Callable, Dict
from markdown import Markdown
from pymdownx.superfences import SuperFencesException
from mkdocs.structure.files import Files, File
from mkdocs.structure.pages import Page
import posixpath
from markdown_exec.hooks import SessionHistoryEntry
from notebook_convert import convert_notebook
from generate_api_reference_links import update_markdown_with_imports
from notebook_convert import convert_notebook
from setup_vcr import load_postamble, load_preamble, _hash_string
logger = logging.getLogger(__name__)
logging.basicConfig()
@@ -15,6 +23,24 @@ logger.setLevel(logging.INFO)
DISABLED = os.getenv("DISABLE_NOTEBOOK_CONVERT") in ("1", "true", "True")
REDIRECT_MAP = {
# lib redirects
"how-tos/stream-values.ipynb": "how-tos/streaming.ipynb#values",
"how-tos/stream-updates.ipynb": "how-tos/streaming.ipynb#updates",
"how-tos/streaming-content.ipynb": "how-tos/streaming.ipynb#custom",
"how-tos/stream-multiple.ipynb": "how-tos/streaming.ipynb#multiple",
"how-tos/streaming-tokens-without-langchain.ipynb": "how-tos/streaming-tokens.ipynb#example-without-langchain",
"how-tos/streaming-from-final-node.ipynb": "how-tos/streaming-specific-nodes.ipynb",
"how-tos/streaming-events-from-within-tools-without-langchain.ipynb": "how-tos/streaming-events-from-within-tools.ipynb#example-without-langchain",
# cloud redirects
"cloud/index.md": "concepts/index.md#langgraph-platform",
"cloud/how-tos/index.md": "how-tos/index.md#langgraph-platform",
"cloud/concepts/api.md": "concepts/langgraph_server.md",
"cloud/concepts/cloud.md": "concepts/langgraph_cloud.md",
"cloud/faq/studio.md": "concepts/langgraph_studio.md#studio-faqs",
}
class NotebookFile(File):
def is_documentation_page(self):
return True
@@ -38,6 +64,29 @@ def on_files(files: Files, **kwargs: Dict[str, Any]):
return new_files
def _add_path_to_code_blocks(markdown: str, page: Page) -> str:
"""Add the path to the code blocks."""
code_block_pattern = re.compile(
r"(?P<indent>[ \t]*)```(?P<language>\w+)[ ]*(?P<attributes>[^\n]*)\n"
r"(?P<code>((?:.*\n)*?))" # Capture the code inside the block using named group
r"(?P=indent)```" # Match closing backticks with the same indentation
)
def replace_code_block_header(match: re.Match) -> str:
indent = match.group("indent")
language = match.group("language")
attributes = match.group("attributes").rstrip()
if 'exec="on"' not in attributes:
# Return original code block
return match.group(0)
code = match.group("code")
return f'{indent}```{language} {attributes} path="{page.file.src_path}"\n{code}{indent}```'
return code_block_pattern.sub(replace_code_block_header, markdown)
def _highlight_code_blocks(markdown: str) -> str:
"""Find code blocks with highlight comments and add hl_lines attribute.
@@ -90,8 +139,8 @@ def _highlight_code_blocks(markdown: str) -> str:
return (
f'{indent}```{language} hl_lines="{" ".join(highlighted_lines)}"\n'
# The indent and terminating \n is already included in the code block
f'{new_code_block}'
f'{indent}```'
f"{new_code_block}"
f"{indent}```"
)
else:
return (
@@ -106,15 +155,200 @@ def _highlight_code_blocks(markdown: str) -> str:
return markdown
def on_page_markdown(markdown: str, page: Page, **kwargs: Dict[str, Any]):
def handle_vcr_setup(
*,
formatter: Callable,
language: str,
code: str,
session: str,
id: str,
md: Markdown,
**kwargs: Dict[str, Any],
) -> Dict[str, Any]:
"""Handle VCR setup in markdown content if necessary."""
try:
if kwargs.get("extra", None) is None:
raise SuperFencesException(
f"error while processing {language} block: extra dict is required"
)
if kwargs["extra"].get("path", None) is None:
raise SuperFencesException(
f"error while processing {language} block: path is required"
)
document_filename = kwargs["extra"]["path"]
if session is None or session == "" and id is None or id == "":
id = _hash_string(code)
if session is not None and session != "":
logger.info(f"new session {session} on page {document_filename}")
cassette_prefix = document_filename.replace(".md", "").replace(os.path.sep, "_")
cassette_dir = os.path.abspath(
os.path.join(os.path.dirname(os.path.dirname(__file__)), "cassettes")
)
os.makedirs(cassette_dir, exist_ok=True)
# Build a unique cassette name.
cassette_name = os.path.join(
cassette_dir,
f"{cassette_prefix}_{session if session else id}_{language}.msgpack.zlib",
)
# Add context manager at start with explicit __enter__ and __exit__ calls
wrapped_lines = [
load_preamble(language, code, cassette_name),
code,
]
if session is None or session == "":
logger.info(
f"no session, adding postamble for {language} in {document_filename}"
)
wrapped_lines.append(load_postamble(language))
transformed_source = "\n".join(wrapped_lines)
return dict(
transform_source=lambda code: (transformed_source, code),
id=id,
extra={},
)
except Exception as e:
raise SuperFencesException(traceback.format_exc()) from e
def handle_vcr_teardown(
*,
formatter: Callable,
language: str,
session: str,
history: list[SessionHistoryEntry],
):
last_inputs = dict(history[-1].inputs)
code = load_postamble(language)
md = last_inputs["md"]
html = False
update_toc = False
document_filename = last_inputs.get("extra", {}).get("path", None)
if document_filename is None:
logger.warning(f"no document filename found while tearing down {session}!")
else:
logger.info(f"tearing down {session} on {document_filename}")
logger.info(traceback.format_stack())
kwargs = dict(
code=code,
session=session,
id=f"{id}_vcr_end",
md=md,
html=html,
update_toc=update_toc,
extra={},
)
# This doesn't actually render anything, we just call the formatter so it
# executes in the same context as the session of which we're disposing.
formatter(**kwargs)
def _on_page_markdown_with_config(
markdown: str,
page: Page,
*,
add_api_references: bool = True,
remove_base64_images: bool = False,
**kwargs: Any,
) -> str:
if DISABLED:
return markdown
if page.file.src_path.endswith(".ipynb"):
logger.info("Processing Jupyter notebook: %s", page.file.src_path)
# logger.info("Processing Jupyter notebook: %s", page.file.src_path)
markdown = convert_notebook(page.file.abs_src_path)
# Append API reference links to code blocks
markdown = update_markdown_with_imports(markdown)
if add_api_references:
markdown = update_markdown_with_imports(markdown)
# Apply highlight comments to code blocks
markdown = _highlight_code_blocks(markdown)
# Add file path as an attribute to code blocks that are executable.
# This file path is used to associate fixtures with the executable code
# which can be used in CI to test the docs without making network requests.
markdown = _add_path_to_code_blocks(markdown, page)
if remove_base64_images:
# Remove base64 encoded images from markdown
markdown = re.sub(r"!\[.*?\]\(data:image/+;base64,[^\)]+\)", "", markdown)
return markdown
def on_page_markdown(markdown: str, page: Page, **kwargs: Dict[str, Any]):
return _on_page_markdown_with_config(
markdown,
page,
add_api_references=True,
**kwargs,
)
# redirects
HTML_TEMPLATE = """
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Redirecting...</title>
<link rel="canonical" href="{url}">
<meta name="robots" content="noindex">
<script>var anchor=window.location.hash.substr(1);location.href="{url}"+(anchor?"#"+anchor:"")</script>
<meta http-equiv="refresh" content="0; url={url}">
</head>
<body>
Redirecting...
</body>
</html>
"""
def write_html(site_dir, old_path, new_path):
"""Write an HTML file in the site_dir with a meta redirect to the new page"""
# Determine all relevant paths
old_path_abs = os.path.join(site_dir, old_path)
old_dir_abs = os.path.dirname(old_path_abs)
# Create parent directories if they don't exist
if not os.path.exists(old_dir_abs):
os.makedirs(old_dir_abs)
# Write the HTML redirect file in place of the old file
content = HTML_TEMPLATE.format(url=new_path)
with open(old_path_abs, "w", encoding="utf-8") as f:
f.write(content)
# Create HTML files for redirects after site dir has been built
def on_post_build(config):
use_directory_urls = config.get("use_directory_urls")
for page_old, page_new in REDIRECT_MAP.items():
page_old = page_old.replace(".ipynb", ".md")
page_new = page_new.replace(".ipynb", ".md")
page_new_before_hash, hash, suffix = page_new.partition("#")
old_html_path = File(page_old, "", "", use_directory_urls).dest_path.replace(
os.sep, "/"
)
new_html_path = File(page_new_before_hash, "", "", True).url
new_html_path = (
posixpath.relpath(new_html_path, start=posixpath.dirname(old_html_path))
+ hash
+ suffix
)
write_html(config["site_dir"], old_html_path, new_html_path)
+37 -30
View File
@@ -7,7 +7,7 @@ import click
import nbformat
logger = logging.getLogger(__name__)
NOTEBOOK_DIRS = ("docs/docs/how-tos","docs/docs/tutorials")
NOTEBOOK_DIRS = ("docs/how-tos","docs/tutorials")
DOCS_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
CASSETTES_PATH = os.path.join(DOCS_PATH, "cassettes")
@@ -19,33 +19,37 @@ BLOCKLIST_COMMANDS = (
)
NOTEBOOKS_NO_CASSETTES = (
"docs/docs/how-tos/visualization.ipynb",
"docs/docs/how-tos/many-tools.ipynb"
"docs/how-tos/visualization.ipynb",
"docs/how-tos/many-tools.ipynb"
)
NOTEBOOKS_NO_EXECUTION = [
# this uses a user provided project name for langsmith
"docs/docs/tutorials/tnt-llm/tnt-llm.ipynb",
"docs/tutorials/tnt-llm/tnt-llm.ipynb",
# this uses langsmith datasets
"docs/docs/tutorials/chatbot-simulation-evaluation/langsmith-agent-simulation-evaluation.ipynb",
"docs/tutorials/chatbot-simulation-evaluation/langsmith-agent-simulation-evaluation.ipynb",
# this uses browser APIs
"docs/docs/tutorials/web-navigation/web_voyager.ipynb",
"docs/tutorials/web-navigation/web_voyager.ipynb",
# these RAG guides use an ollama model
"docs/docs/tutorials/rag/langgraph_adaptive_rag_local.ipynb",
"docs/docs/tutorials/rag/langgraph_crag_local.ipynb",
"docs/docs/tutorials/rag/langgraph_self_rag_local.ipynb",
"docs/tutorials/rag/langgraph_adaptive_rag_local.ipynb",
"docs/tutorials/rag/langgraph_crag_local.ipynb",
"docs/tutorials/rag/langgraph_self_rag_local.ipynb",
# this loads a massive dataset from gcp
"docs/docs/tutorials/usaco/usaco.ipynb",
"docs/tutorials/usaco/usaco.ipynb",
# TODO: figure out why autogen notebook is not runnable (they are just hanging. possible due to code execution?)
"docs/docs/how-tos/autogen-integration.ipynb",
"docs/how-tos/autogen-integration.ipynb",
"docs/how-tos/autogen-integration-functional.ipynb",
# TODO: need to update these notebooks to make sure they are runnable in CI
"docs/docs/tutorials/storm/storm.ipynb", # issues only when running with VCR
"docs/docs/tutorials/lats/lats.ipynb", # issues only when running with VCR
"docs/docs/tutorials/rag/langgraph_crag.ipynb", # flakiness from tavily
"docs/docs/tutorials/rag/langgraph_adaptive_rag.ipynb", # Cannot create a consistent method resolution error from VCR
"docs/docs/how-tos/map-reduce.ipynb", # flakiness from structured output, only when running with VCR
"docs/docs/tutorials/tot/tot.ipynb",
"docs/docs/how-tos/visualization.ipynb"
"docs/tutorials/storm/storm.ipynb", # issues only when running with VCR
"docs/tutorials/lats/lats.ipynb", # issues only when running with VCR
"docs/tutorials/rag/langgraph_crag.ipynb", # flakiness from tavily
"docs/tutorials/rag/langgraph_adaptive_rag.ipynb", # flakiness only when running in GHA
"docs/tutorials/rag/langgraph_self_rag.ipynb", # flakiness only when running in GHA
"docs/tutorials/rag/langgraph_agentic_rag.ipynb", # flakiness only when running in GHA
"docs/how-tos/map-reduce.ipynb", # flakiness from structured output, only when running with VCR
"docs/tutorials/tot/tot.ipynb",
"docs/how-tos/visualization.ipynb",
"docs/tutorials/llm-compiler/LLMCompiler.ipynb"
]
@@ -127,7 +131,18 @@ def add_vcr_to_notebook(
uses_langsmith = True
# Add import statement
vcr_import_lines = [
vcr_import_lines = []
if uses_langsmith:
vcr_import_lines.extend([
# patch urllib3 to handle vcr errors, see more here:
# https://github.com/langchain-ai/langsmith-sdk/blob/main/python/langsmith/_internal/_patch.py
"import sys",
f"sys.path.insert(0, '{os.path.join(DOCS_PATH, '_scripts')}')",
"import _patch as patch_urllib3",
"patch_urllib3.patch_urllib3()",
])
vcr_import_lines.extend([
"import nest_asyncio",
"nest_asyncio.apply()",
"import vcr",
@@ -157,16 +172,8 @@ def add_vcr_to_notebook(
"",
"custom_vcr.register_serializer('advanced_compressed', AdvancedCompressedSerializer())",
"custom_vcr.serializer = 'advanced_compressed'",
]
if uses_langsmith:
vcr_import_lines.extend(
# patch urllib3 to handle vcr errors, see more here:
# https://github.com/langchain-ai/langsmith-sdk/blob/main/python/langsmith/_internal/_patch.py
"import sys",
f"sys.path.insert(0, '{os.path.join(DOCS_PATH, '_scripts')}')",
"import _patch as patch_urllib3",
"patch_urllib3.patch_urllib3()",
)
])
import_cell = nbformat.v4.new_code_cell(source="\n".join(vcr_import_lines))
import_cell.pop("id", None)
notebook.cells.insert(0, import_cell)
@@ -210,7 +217,7 @@ def process_notebooks(should_comment_install_cells: bool) -> None:
except Exception as e:
logger.error(f"Error processing {notebook_path}: {e}")
with open(os.path.join(DOCS_PATH, "notebooks_no_execution.json"), "w") as f:
with open("notebooks_no_execution.json", "w") as f:
json.dump(NOTEBOOKS_NO_EXECUTION, f)
+77
View File
@@ -0,0 +1,77 @@
# A list of patterns that, if found in a code block, will cause us to leave that block unchanged.
import hashlib
import os
from textwrap import dedent
preambles = {
"python": "vcr_setup_preamble.py",
"typescript": "nock_setup_preamble.ts",
}
def _get_python_cassette_init(cassette_name: str, hash_: str) -> str:
return dedent(
f"""
_cassette = HashedCassette('{cassette_name}', '{hash_}')
_cassette.__enter__()
"""
)
def _get_typescript_cassette_init(cassette_name: str, hash_: str) -> str:
return dedent(
f"""
const _cassette = new HashedCassette("{cassette_name}", "{hash_}");
await _cassette.enter();
"""
)
def _get_python_cassette_cleanup() -> str:
return "_cassette.__exit__()"
def _get_typescript_cassette_cleanup() -> str:
return "await _cassette.exit();"
preamble_inits = {
"python": _get_python_cassette_init,
"py": _get_python_cassette_init,
"typescript": _get_typescript_cassette_init,
"ts": _get_typescript_cassette_init,
}
preamble_cleanups = {
"python": _get_python_cassette_cleanup,
"py": _get_python_cassette_cleanup,
"typescript": _get_typescript_cassette_cleanup,
"ts": _get_typescript_cassette_cleanup,
}
def load_preamble(language: str, code: str, cassette_name: str) -> str:
"""Load the source code for the preamble for a given language."""
_assets_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "assets")
preamble_path = os.path.join(_assets_dir, preambles[language])
with open(preamble_path, "r") as f:
lines = f.readlines()
hash_ = _hash_string(code)
lines.append(preamble_inits[language](cassette_name, hash_))
return "\n".join(lines).strip()
def load_postamble(language: str) -> str:
"""Load the source code for the postamble for a given language."""
return preamble_cleanups[language]()
def _hash_string(input_string: str) -> str:
# Encode the input string to bytes
encoded_string = input_string.encode("utf-8")
# Create a SHA-256 hash object
sha256_hash = hashlib.sha256(encoded_string)
# Get the hexadecimal digest of the hash
return sha256_hash.hexdigest()
+137
View File
@@ -0,0 +1,137 @@
#!/usr/bin/env python
"""Create the third party page for the documentation."""
import argparse
from typing import List
from typing import TypedDict
import yaml
MARKDOWN = """\
[//]: # (This file is automatically generated using a script in docs/_scripts. Do not edit this file directly!)
# 🚀 Prebuilt Agents
LangGraph includes a prebuilt React agent. For more information on how to use it,
check out our [how-to guides](https://langchain-ai.github.io/langgraph/how-tos/#prebuilt-react-agent).
If youre looking for other prebuilt libraries, explore the community-built options
below. These libraries can extend LangGraph's functionality in various ways.
## 📚 Available Libraries
[//]: # (This file is automatically generated using a script in docs/_scripts. Do not edit this file directly!)
{library_list}
## ✨ Contributing Your Library
Have you built an awesome open-source library using LangGraph? We'd love to feature
your project on the official LangGraph documentation pages! 🏆
To share your project, simply open a Pull Request adding an entry for your package in our [packages.yml]({langgraph_url}) file.
**Guidelines**
- Your repo must be distributed as an installable package (e.g., PyPI for Python, npm
for JavaScript/TypeScript, etc.) 📦
- The repo should either use the Graph API (exposing a `StateGraph` instance) or
the Functional API (exposing an `entrypoint`).
- The package must include documentation (e.g., a `README.md` or docs site)
explaining how to use it.
We'll review your contribution and merge it in!
Thanks for contributing! 🚀
"""
class ResolvedPackage(TypedDict):
name: str
"""The name of the package."""
repo: str
"""Repository ID within github. Format is: [orgname]/[repo_name]."""
weekly_downloads: int | None
"""The weekly download count of the package."""
description: str
"""A brief description of what the package does."""
def generate_markdown(resolved_packages: List[ResolvedPackage], language: str) -> str:
"""Generate the markdown content for the third party page.
Args:
resolved_packages: A list of resolved package information.
language: str
Returns:
The markdown content as a string.
"""
# Update the URL to the actual file once the initial version is merged
if language == "python":
langgraph_url = (
"https://github.com/langchain-ai/langgraph/blob/main/docs"
"/_scripts/third_party_page/packages.yml"
)
elif language == "js":
langgraph_url = (
"https://github.com/langchain-ai/langgraphjs/blob/main/docs"
"/_scripts/third_party/packages.yml"
)
else:
raise ValueError(f"Invalid language '{language}'. Expected 'python' or 'js'.")
sorted_packages = sorted(
resolved_packages, key=lambda p: p["weekly_downloads"] or 0, reverse=True
)
rows = [
"| Name | GitHub URL | Description | Weekly Downloads |",
"| --- | --- | --- | --- |",
]
for package in sorted_packages:
name = f"**{package['name']}**"
repo_url = f"[{package['repo']}](https://github.com/{package['repo']})"
downloads = package["weekly_downloads"] or 0
row = f"| {name} | {repo_url} | {package['description']} | {downloads} |"
rows.append(row)
markdown_content = MARKDOWN.format(
library_list="\n".join(rows), langgraph_url=langgraph_url
)
return markdown_content
def main(input_file: str, output_file: str, language: str) -> None:
"""Main function to create the third party page.
Args:
input_file: Path to the input YAML file containing resolved package information.
output_file: Path to the output file for the third party page.
language: The language for which to generate the third party page.
"""
# Parse the input YAML file
with open(input_file, "r") as f:
resolved_packages: List[ResolvedPackage] = yaml.safe_load(f)
markdown_content = generate_markdown(resolved_packages, language)
# Write the markdown content to the output file
with open(output_file, "w", encoding="utf-8") as f:
f.write(markdown_content)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Create the third party page.")
parser.add_argument(
"input_file",
help="Path to the input YAML file containing resolved package information.",
)
parser.add_argument(
"output_file", help="Path to the output file for the third party page."
)
parser.add_argument(
"--language",
choices=["python", "js"],
default="python",
help="The language for which to generate the third party page. Defaults to 'python'.",
)
args = parser.parse_args()
main(args.input_file, args.output_file, args.language)
+95
View File
@@ -0,0 +1,95 @@
#!/usr/bin/env python
"""Retrieve download count for a list of Python packages from PyPI."""
import argparse
from datetime import datetime
from typing import TypedDict
import pathlib
import requests
import yaml
class Package(TypedDict):
"""A TypedDict representing a package"""
name: str
"""The name of the package."""
repo: str
"""Repository ID within github. Format is: [orgname]/[repo_name]."""
description: str
"""A brief description of what the package does."""
class ResolvedPackage(Package):
weekly_downloads: int | None
HERE = pathlib.Path(__file__).parent
PACKAGES_FILE = HERE / "packages.yml"
PACKAGES = yaml.safe_load(PACKAGES_FILE.read_text())['packages']
def _get_weekly_downloads(packages: list[Package]) -> list[ResolvedPackage]:
"""Retrieve the monthly download count for a list of packages from PyPIStats."""
resolved_packages: list[ResolvedPackage] = []
for package in packages:
url = f"https://pypistats.org/api/packages/{package['name']}/overall"
response = requests.get(url)
response.raise_for_status()
data = response.json()
sorted_data = sorted(
data["data"],
key=lambda x: datetime.strptime(x["date"], "%Y-%m-%d"),
reverse=True,
)
# Sum the last 7 days of downloads
num_downloads = sum(entry["downloads"] for entry in sorted_data[:7])
resolved_packages.append(
{
"name": package["name"],
"repo": package["repo"],
"weekly_downloads": num_downloads,
"description": package["description"],
}
)
return resolved_packages
def main(output_file: str) -> None:
"""Main function to generate package download information.
Args:
output_file: Path to the output YAML file.
"""
resolved_packages: list[ResolvedPackage] = _get_weekly_downloads(PACKAGES)
if not output_file.endswith(".yml"):
raise ValueError("Output file must have a .yml extension")
with open(output_file, "w") as f:
f.write("# This file is auto-generated. Do not edit.\n")
yaml.dump(resolved_packages, f)
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Generate package download information."
)
parser.add_argument(
"output_file",
help=(
"Path to the output YAML file. Example: python generate_downloads.py "
"downloads.yml"
),
)
args = parser.parse_args()
main(args.output_file)
@@ -0,0 +1,11 @@
#A list of third-party packages to surface on the third-party page.
packages:
- name: "trustcall"
repo: "hinthornw/trustcall"
description: "Tenacious tool calling built on LangGraph"
- name: "breeze-agent"
repo: "andrestorres123/breeze-agent"
description: "A streamlined research system built inspired on STORM and built on LangGraph"
- name: "langgraph-supervisor"
repo: "langchain-ai/langgraph-supervisor"
description: "Build supervisor multi-agent systems with LangGraph"
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
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
@@ -1 +0,0 @@
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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
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
@@ -1 +0,0 @@
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
@@ -0,0 +1 @@
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
@@ -0,0 +1 @@
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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
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
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
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
@@ -1 +1 @@
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
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
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
@@ -1 +0,0 @@
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
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
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
@@ -1 +0,0 @@
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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
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
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
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
@@ -0,0 +1 @@
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
@@ -1 +0,0 @@
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dN972SP/KqafcCW9MfE3e7f+y8b/agwuPrf37JoXputubDp2cXRnK78szlot3gsJKa54ecmXn2yfW71julATevviRzk749etHtlSU3/0tHpS3ow/hWdTq+NTnh58ZfvBBUcvV2Scb7XMulGouim9cm/JjtemDS4Lm9dOhpUFqy/QE4aWHth8+IXNrLrlyvHpSftGzW3JuRr6vPrukszN9W3n7780L7Jl3vKJu5fJdZP2mcP36ytafzV/6TNTr7boznakvFl3P+FmVd6FKHTixOmnli2aGLvwcfG1r5/6+pru1/unsi0t8SPOCM8NWjG7zDMpJl/IO1UlPl13y6ie88m1X/zuXJr3sPd6rnfWv7LPTDk5NNET/Oz6vPhR3pikTW1LY1fn15wbFrucP/jGrmOJFaPj7rau2t+2Lazzb0uIKxltBktO+5o11lGbcuhPf9MUwlfeqfQ0ndplLLAkhjVrynYV76yakHs9c4Jq43n0JpeZ39naOu7K4Huc0FV2b7KFzTftfL3xyFTh28qy48NHVH34BZU0Zu/18ubItcIo/esj04QLKbrfLvuo7kTVZeFgRNWuNUz3rZduP/PhgVub9r186bhj6QJNZ1n6YbrEYjBHR68YrU84f+jFjleiN5fbn18+fRp/1jV+2JBEj22C6XJqyHMxacKgqe6Smux3pSrZ9sX0vJSMisrThk+m1rV3XHr30j9s3zbtOfPayqB/50fMiQu/9qv9Xo1pQor65pg5jqYRHwimr70zPv7Fhajww8MmhBo7m56d0uXYPbJpzKmPT78VnHF9wbX6/dnZ+z7fM8s4/oNV42+v2RD+t5kbgqdHRL46TD3qzu0zmYd2hsXMP998PPWnUds+GXuySWe8eeRA16Vka35428kdf9gxpFqK3WkbN7/b+nrNOxvHxlJznz+5YHJR67MH15lD54dGu375VE7o+49v23px57X2nMoZk/Z+dOeEaU77lht7NkXPGlRTfvq/g8gtd8k5fxLWuis+Gzvee2jrwb9n5r4dX/DmXmP2qpXVh9p19cW3E7+4ZV+Xs8rwx3m59J3Clm/ylhtWl27/pSml4UjE8WNdK2e9UN59496f+c//MSQo6P79IUHd11LTXSOCgv4XTCOo8w==
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
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
@@ -1 +0,0 @@
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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
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
@@ -0,0 +1 @@
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
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
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
@@ -1 +1 @@
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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long

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