diff --git a/langgraph/graph/graph.py b/langgraph/graph/graph.py index 22e075575..16e3551f5 100644 --- a/langgraph/graph/graph.py +++ b/langgraph/graph/graph.py @@ -9,6 +9,8 @@ from langchain_core.runnables.base import ( RunnableLike, coerce_to_runnable, ) +from langchain_core.runnables.config import RunnableConfig +from langchain_core.runnables.graph import Graph as RunnableGraph from langgraph.checkpoint import BaseCheckpointSaver from langgraph.pregel import Channel, Pregel @@ -150,7 +152,7 @@ class Graph: checkpointer: Optional[BaseCheckpointSaver] = None, interrupt_before: Optional[Sequence[str]] = None, interrupt_after: Optional[Sequence[str]] = None, - ) -> Pregel: + ) -> "CompiledGraph": interrupt_before = interrupt_before or [] interrupt_after = interrupt_after or [] self.validate(interrupt=interrupt_before + interrupt_after) @@ -177,7 +179,8 @@ class Graph: branch.runnable, name=f"{key}_condition" ) - return Pregel( + return CompiledGraph( + graph=self, nodes=nodes, input=f"{self.entry_point}:inbox", output=END, @@ -188,3 +191,32 @@ class Graph: + [node for node in interrupt_after] ), ) + + +class CompiledGraph(Pregel): + graph: Graph + + def get_graph(self, config: Optional[RunnableConfig] = None) -> RunnableGraph: + graph = RunnableGraph() + graph.add_node(self.get_input_schema(config), "__start__") + graph.add_node(self.get_output_schema(config), END) + + for key, node in self.graph.nodes.items(): + graph.add_node(node, key) + for start, end in self.graph.edges: + graph.add_edge(graph.nodes[start], graph.nodes[end]) + for start, branches in self.graph.branches.items(): + for i, branch in enumerate(branches): + name = f"{start}_{branch.condition.__name__}" + if i > 0: + name += f"_{i}" + graph.add_node( + RunnableLambda(branch.runnable, name=branch.condition.__name__), + name, + ) + graph.add_edge(graph.nodes[start], graph.nodes[name]) + for label, end in branch.ends.items(): + graph.add_edge(graph.nodes[name], graph.nodes[end], label) + graph.add_edge(graph.nodes["__start__"], graph.nodes[self.graph.entry_point]) + + return graph diff --git a/langgraph/graph/state.py b/langgraph/graph/state.py index 552b954a1..2804990b6 100644 --- a/langgraph/graph/state.py +++ b/langgraph/graph/state.py @@ -12,8 +12,8 @@ from langgraph.channels.binop import BinaryOperatorAggregate from langgraph.channels.ephemeral_value import EphemeralValue from langgraph.channels.last_value import LastValue from langgraph.checkpoint import BaseCheckpointSaver -from langgraph.graph.graph import END, Graph -from langgraph.pregel import Channel, Pregel +from langgraph.graph.graph import END, CompiledGraph, Graph +from langgraph.pregel import Channel from langgraph.pregel.read import ChannelRead from langgraph.pregel.write import SKIP_WRITE, ChannelWrite @@ -41,7 +41,7 @@ class StateGraph(Graph): checkpointer: Optional[BaseCheckpointSaver] = None, interrupt_before: Optional[Sequence[str]] = None, interrupt_after: Optional[Sequence[str]] = None, - ) -> Pregel: + ) -> CompiledGraph: interrupt_before = interrupt_before or [] interrupt_after = interrupt_after or [] self.validate(interrupt=interrupt_before + interrupt_after) @@ -78,13 +78,13 @@ class StateGraph(Graph): node_inboxes = { # we take any value written to channel because all writers # write the entire state as of that step, which is equal for all - f"{key}:inbox": AnyValue(Any) - for key in self.nodes + f"{key}:inbox": AnyValue(self.schema) + for key in list(self.nodes) + [START] } node_outboxes = { # we clear outbox channels after each step key: EphemeralValue(Any) - for key in self.nodes + for key in list(self.nodes) + [START] } for key in self.nodes: @@ -111,9 +111,15 @@ class StateGraph(Graph): | Channel.write_to(f"{self.entry_point}:inbox") ) - return Pregel( + return CompiledGraph( + graph=self, nodes=nodes, - channels={**self.channels, **node_inboxes, **node_outboxes}, + channels={ + **self.channels, + **node_inboxes, + **node_outboxes, + END: LastValue(self.schema), + }, input=f"{START}:inbox", output=END, hidden=[f"{node}:inbox" for node in self.nodes] + [START] + state_keys, diff --git a/poetry.lock b/poetry.lock index 462f4e42e..0c5dc41ef 100644 --- a/poetry.lock +++ b/poetry.lock @@ -843,6 +843,23 @@ files = [ {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, ] +[[package]] +name = "grandalf" +version = "0.8" +description = "Graph and drawing algorithms framework" +optional = false +python-versions = "*" +files = [ + {file = "grandalf-0.8-py3-none-any.whl", hash = "sha256:793ca254442f4a79252ea9ff1ab998e852c1e071b863593e5383afee906b4185"}, + {file = "grandalf-0.8.tar.gz", hash = "sha256:2813f7aab87f0d20f334a3162ccfbcbf085977134a17a5b516940a93a77ea974"}, +] + +[package.dependencies] +pyparsing = "*" + +[package.extras] +full = ["numpy", "ply"] + [[package]] name = "greenlet" version = "3.0.3" @@ -1498,13 +1515,13 @@ files = [ [[package]] name = "langchain" -version = "0.1.4" +version = "0.1.8" description = "Building applications with LLMs through composability" optional = false python-versions = ">=3.8.1,<4.0" files = [ - {file = "langchain-0.1.4-py3-none-any.whl", hash = "sha256:6befdd6221f5f326092e31a3c19efdc7ce3d7d1f2e2cab065141071451730ed7"}, - {file = "langchain-0.1.4.tar.gz", hash = "sha256:8767a9461e2b717ce9a35b1fa20659de89ea86ba9c2a4ff516e05d47ab2d195d"}, + {file = "langchain-0.1.8-py3-none-any.whl", hash = "sha256:19e951b0e2be099ff048ee483acecb47e1a39c33a47dadfee70fcfa20f45cc19"}, + {file = "langchain-0.1.8.tar.gz", hash = "sha256:c8b1c2954a07cd6422c9027459473bafae90c78f07015bf2fc6262fadf97ea44"}, ] [package.dependencies] @@ -1512,9 +1529,9 @@ aiohttp = ">=3.8.3,<4.0.0" async-timeout = {version = ">=4.0.0,<5.0.0", markers = "python_version < \"3.11\""} dataclasses-json = ">=0.5.7,<0.7" jsonpatch = ">=1.33,<2.0" -langchain-community = ">=0.0.14,<0.1" -langchain-core = ">=0.1.16,<0.2" -langsmith = ">=0.0.83,<0.1" +langchain-community = ">=0.0.21,<0.1" +langchain-core = ">=0.1.24,<0.2" +langsmith = ">=0.1.0,<0.2.0" numpy = ">=1,<2" pydantic = ">=1,<3" PyYAML = ">=5.3" @@ -1529,7 +1546,7 @@ cli = ["typer (>=0.9.0,<0.10.0)"] cohere = ["cohere (>=4,<5)"] docarray = ["docarray[hnswlib] (>=0.32.0,<0.33.0)"] embeddings = ["sentence-transformers (>=2,<3)"] -extended-testing = ["aiosqlite (>=0.19.0,<0.20.0)", "aleph-alpha-client (>=2.15.0,<3.0.0)", "anthropic (>=0.3.11,<0.4.0)", "arxiv (>=1.4,<2.0)", "assemblyai (>=0.17.0,<0.18.0)", "atlassian-python-api (>=3.36.0,<4.0.0)", "beautifulsoup4 (>=4,<5)", "bibtexparser (>=1.4.0,<2.0.0)", "cassio (>=0.1.0,<0.2.0)", "chardet (>=5.1.0,<6.0.0)", "cohere (>=4,<5)", "couchbase (>=4.1.9,<5.0.0)", "dashvector (>=1.0.1,<2.0.0)", "databricks-vectorsearch (>=0.21,<0.22)", "datasets (>=2.15.0,<3.0.0)", "dgml-utils (>=0.3.0,<0.4.0)", "esprima (>=4.0.1,<5.0.0)", "faiss-cpu (>=1,<2)", "feedparser (>=6.0.10,<7.0.0)", "fireworks-ai (>=0.9.0,<0.10.0)", "geopandas (>=0.13.1,<0.14.0)", "gitpython (>=3.1.32,<4.0.0)", "google-cloud-documentai (>=2.20.1,<3.0.0)", "gql (>=3.4.1,<4.0.0)", "hologres-vector (>=0.0.6,<0.0.7)", "html2text (>=2020.1.16,<2021.0.0)", "javelin-sdk (>=0.1.8,<0.2.0)", "jinja2 (>=3,<4)", "jq (>=1.4.1,<2.0.0)", "jsonschema (>1)", "langchain-openai (>=0.0.2,<0.1)", "lxml (>=4.9.2,<5.0.0)", "markdownify (>=0.11.6,<0.12.0)", "motor (>=3.3.1,<4.0.0)", "msal (>=1.25.0,<2.0.0)", "mwparserfromhell (>=0.6.4,<0.7.0)", "mwxml (>=0.3.3,<0.4.0)", "newspaper3k (>=0.2.8,<0.3.0)", "numexpr (>=2.8.6,<3.0.0)", "openai (<2)", "openapi-pydantic (>=0.3.2,<0.4.0)", "pandas (>=2.0.1,<3.0.0)", "pdfminer-six (>=20221105,<20221106)", "pgvector (>=0.1.6,<0.2.0)", "praw (>=7.7.1,<8.0.0)", "psychicapi (>=0.8.0,<0.9.0)", "py-trello (>=0.19.0,<0.20.0)", "pymupdf (>=1.22.3,<2.0.0)", "pypdf (>=3.4.0,<4.0.0)", "pypdfium2 (>=4.10.0,<5.0.0)", "pyspark (>=3.4.0,<4.0.0)", "rank-bm25 (>=0.2.2,<0.3.0)", "rapidfuzz (>=3.1.1,<4.0.0)", "rapidocr-onnxruntime (>=1.3.2,<2.0.0)", "requests-toolbelt (>=1.0.0,<2.0.0)", "rspace_client (>=2.5.0,<3.0.0)", "scikit-learn (>=1.2.2,<2.0.0)", "sqlite-vss (>=0.1.2,<0.2.0)", "streamlit (>=1.18.0,<2.0.0)", "sympy (>=1.12,<2.0)", "telethon (>=1.28.5,<2.0.0)", "timescale-vector (>=0.0.1,<0.0.2)", "tqdm (>=4.48.0)", "upstash-redis (>=0.15.0,<0.16.0)", "xata (>=1.0.0a7,<2.0.0)", "xmltodict (>=0.13.0,<0.14.0)"] +extended-testing = ["aiosqlite (>=0.19.0,<0.20.0)", "aleph-alpha-client (>=2.15.0,<3.0.0)", "anthropic (>=0.3.11,<0.4.0)", "arxiv (>=1.4,<2.0)", "assemblyai (>=0.17.0,<0.18.0)", "atlassian-python-api (>=3.36.0,<4.0.0)", "beautifulsoup4 (>=4,<5)", "bibtexparser (>=1.4.0,<2.0.0)", "cassio (>=0.1.0,<0.2.0)", "chardet (>=5.1.0,<6.0.0)", "cohere (>=4,<5)", "couchbase (>=4.1.9,<5.0.0)", "dashvector (>=1.0.1,<2.0.0)", "databricks-vectorsearch (>=0.21,<0.22)", "datasets (>=2.15.0,<3.0.0)", "dgml-utils (>=0.3.0,<0.4.0)", "esprima (>=4.0.1,<5.0.0)", "faiss-cpu (>=1,<2)", "feedparser (>=6.0.10,<7.0.0)", "fireworks-ai (>=0.9.0,<0.10.0)", "geopandas (>=0.13.1,<0.14.0)", "gitpython (>=3.1.32,<4.0.0)", "google-cloud-documentai (>=2.20.1,<3.0.0)", "gql (>=3.4.1,<4.0.0)", "hologres-vector (>=0.0.6,<0.0.7)", "html2text (>=2020.1.16,<2021.0.0)", "javelin-sdk (>=0.1.8,<0.2.0)", "jinja2 (>=3,<4)", "jq (>=1.4.1,<2.0.0)", "jsonschema (>1)", "langchain-openai (>=0.0.2,<0.1)", "lxml (>=4.9.2,<5.0.0)", "markdownify (>=0.11.6,<0.12.0)", "motor (>=3.3.1,<4.0.0)", "msal (>=1.25.0,<2.0.0)", "mwparserfromhell (>=0.6.4,<0.7.0)", "mwxml (>=0.3.3,<0.4.0)", "newspaper3k (>=0.2.8,<0.3.0)", "numexpr (>=2.8.6,<3.0.0)", "openai (<2)", "openapi-pydantic (>=0.3.2,<0.4.0)", "pandas (>=2.0.1,<3.0.0)", "pdfminer-six (>=20221105,<20221106)", "pgvector (>=0.1.6,<0.2.0)", "praw (>=7.7.1,<8.0.0)", "psychicapi (>=0.8.0,<0.9.0)", "py-trello (>=0.19.0,<0.20.0)", "pymupdf (>=1.22.3,<2.0.0)", "pypdf (>=3.4.0,<4.0.0)", "pypdfium2 (>=4.10.0,<5.0.0)", "pyspark (>=3.4.0,<4.0.0)", "rank-bm25 (>=0.2.2,<0.3.0)", "rapidfuzz (>=3.1.1,<4.0.0)", "rapidocr-onnxruntime (>=1.3.2,<2.0.0)", "rdflib (==7.0.0)", "requests-toolbelt (>=1.0.0,<2.0.0)", "rspace_client (>=2.5.0,<3.0.0)", "scikit-learn (>=1.2.2,<2.0.0)", "sqlite-vss (>=0.1.2,<0.2.0)", "streamlit (>=1.18.0,<2.0.0)", "sympy (>=1.12,<2.0)", "telethon (>=1.28.5,<2.0.0)", "timescale-vector (>=0.0.1,<0.0.2)", "tqdm (>=4.48.0)", "upstash-redis (>=0.15.0,<0.16.0)", "xata (>=1.0.0a7,<2.0.0)", "xmltodict (>=0.13.0,<0.14.0)"] javascript = ["esprima (>=4.0.1,<5.0.0)"] llms = ["clarifai (>=9.1.0)", "cohere (>=4,<5)", "huggingface_hub (>=0,<1)", "manifest-ml (>=0.0.1,<0.0.2)", "nlpcloud (>=1,<2)", "openai (<2)", "openlm (>=0.0.5,<0.0.6)", "torch (>=1,<3)", "transformers (>=4,<5)"] openai = ["openai (<2)", "tiktoken (>=0.3.2,<0.6.0)"] @@ -1538,20 +1555,20 @@ text-helpers = ["chardet (>=5.1.0,<6.0.0)"] [[package]] name = "langchain-community" -version = "0.0.16" +version = "0.0.21" description = "Community contributed LangChain integrations." optional = false python-versions = ">=3.8.1,<4.0" files = [ - {file = "langchain_community-0.0.16-py3-none-any.whl", hash = "sha256:0f1dfc1a6205ce8d39931d3515974a208a9f69c16157c649f83490a7cc830b73"}, - {file = "langchain_community-0.0.16.tar.gz", hash = "sha256:c06512a93013a06fba7679cd5a1254ff8b927cddd2d1fbe0cc444bf7bbdf0b8c"}, + {file = "langchain_community-0.0.21-py3-none-any.whl", hash = "sha256:120977485d244eb472ad3618a31222fe6c2bce08026f4caa96bd6dae2e316ac0"}, + {file = "langchain_community-0.0.21.tar.gz", hash = "sha256:1c310a7e2663d5f6464a433981504894f97c12783cbeb8bdf4159a574f88c18d"}, ] [package.dependencies] aiohttp = ">=3.8.3,<4.0.0" dataclasses-json = ">=0.5.7,<0.7" -langchain-core = ">=0.1.16,<0.2" -langsmith = ">=0.0.83,<0.1" +langchain-core = ">=0.1.24,<0.2" +langsmith = ">=0.1.0,<0.2.0" numpy = ">=1,<2" PyYAML = ">=5.3" requests = ">=2,<3" @@ -1560,23 +1577,23 @@ tenacity = ">=8.1.0,<9.0.0" [package.extras] cli = ["typer (>=0.9.0,<0.10.0)"] -extended-testing = ["aiosqlite (>=0.19.0,<0.20.0)", "aleph-alpha-client (>=2.15.0,<3.0.0)", "anthropic (>=0.3.11,<0.4.0)", "arxiv (>=1.4,<2.0)", "assemblyai (>=0.17.0,<0.18.0)", "atlassian-python-api (>=3.36.0,<4.0.0)", "azure-ai-documentintelligence (>=1.0.0b1,<2.0.0)", "beautifulsoup4 (>=4,<5)", "bibtexparser (>=1.4.0,<2.0.0)", "cassio (>=0.1.0,<0.2.0)", "chardet (>=5.1.0,<6.0.0)", "cohere (>=4,<5)", "dashvector (>=1.0.1,<2.0.0)", "databricks-vectorsearch (>=0.21,<0.22)", "datasets (>=2.15.0,<3.0.0)", "dgml-utils (>=0.3.0,<0.4.0)", "elasticsearch (>=8.12.0,<9.0.0)", "esprima (>=4.0.1,<5.0.0)", "faiss-cpu (>=1,<2)", "feedparser (>=6.0.10,<7.0.0)", "fireworks-ai (>=0.9.0,<0.10.0)", "geopandas (>=0.13.1,<0.14.0)", "gitpython (>=3.1.32,<4.0.0)", "google-cloud-documentai (>=2.20.1,<3.0.0)", "gql (>=3.4.1,<4.0.0)", "gradientai (>=1.4.0,<2.0.0)", "hdbcli (>=2.19.21,<3.0.0)", "hologres-vector (>=0.0.6,<0.0.7)", "html2text (>=2020.1.16,<2021.0.0)", "javelin-sdk (>=0.1.8,<0.2.0)", "jinja2 (>=3,<4)", "jq (>=1.4.1,<2.0.0)", "jsonschema (>1)", "lxml (>=4.9.2,<5.0.0)", "markdownify (>=0.11.6,<0.12.0)", "motor (>=3.3.1,<4.0.0)", "msal (>=1.25.0,<2.0.0)", "mwparserfromhell (>=0.6.4,<0.7.0)", "mwxml (>=0.3.3,<0.4.0)", "newspaper3k (>=0.2.8,<0.3.0)", "numexpr (>=2.8.6,<3.0.0)", "oci (>=2.119.1,<3.0.0)", "openai (<2)", "openapi-pydantic (>=0.3.2,<0.4.0)", "oracle-ads (>=2.9.1,<3.0.0)", "pandas (>=2.0.1,<3.0.0)", "pdfminer-six (>=20221105,<20221106)", "pgvector (>=0.1.6,<0.2.0)", "praw (>=7.7.1,<8.0.0)", "psychicapi (>=0.8.0,<0.9.0)", "py-trello (>=0.19.0,<0.20.0)", "pymupdf (>=1.22.3,<2.0.0)", "pypdf (>=3.4.0,<4.0.0)", "pypdfium2 (>=4.10.0,<5.0.0)", "pyspark (>=3.4.0,<4.0.0)", "rank-bm25 (>=0.2.2,<0.3.0)", "rapidfuzz (>=3.1.1,<4.0.0)", "rapidocr-onnxruntime (>=1.3.2,<2.0.0)", "requests-toolbelt (>=1.0.0,<2.0.0)", "rspace_client (>=2.5.0,<3.0.0)", "scikit-learn (>=1.2.2,<2.0.0)", "sqlite-vss (>=0.1.2,<0.2.0)", "streamlit (>=1.18.0,<2.0.0)", "sympy (>=1.12,<2.0)", "telethon (>=1.28.5,<2.0.0)", "timescale-vector (>=0.0.1,<0.0.2)", "tqdm (>=4.48.0)", "upstash-redis (>=0.15.0,<0.16.0)", "xata (>=1.0.0a7,<2.0.0)", "xmltodict (>=0.13.0,<0.14.0)", "zhipuai (>=1.0.7,<2.0.0)"] +extended-testing = ["aiosqlite (>=0.19.0,<0.20.0)", "aleph-alpha-client (>=2.15.0,<3.0.0)", "anthropic (>=0.3.11,<0.4.0)", "arxiv (>=1.4,<2.0)", "assemblyai (>=0.17.0,<0.18.0)", "atlassian-python-api (>=3.36.0,<4.0.0)", "azure-ai-documentintelligence (>=1.0.0b1,<2.0.0)", "beautifulsoup4 (>=4,<5)", "bibtexparser (>=1.4.0,<2.0.0)", "cassio (>=0.1.0,<0.2.0)", "chardet (>=5.1.0,<6.0.0)", "cohere (>=4,<5)", "databricks-vectorsearch (>=0.21,<0.22)", "datasets (>=2.15.0,<3.0.0)", "dgml-utils (>=0.3.0,<0.4.0)", "elasticsearch (>=8.12.0,<9.0.0)", "esprima (>=4.0.1,<5.0.0)", "faiss-cpu (>=1,<2)", "feedparser (>=6.0.10,<7.0.0)", "fireworks-ai (>=0.9.0,<0.10.0)", "geopandas (>=0.13.1,<0.14.0)", "gitpython (>=3.1.32,<4.0.0)", "google-cloud-documentai (>=2.20.1,<3.0.0)", "gql (>=3.4.1,<4.0.0)", "gradientai (>=1.4.0,<2.0.0)", "hdbcli (>=2.19.21,<3.0.0)", "hologres-vector (>=0.0.6,<0.0.7)", "html2text (>=2020.1.16,<2021.0.0)", "httpx (>=0.24.1,<0.25.0)", "javelin-sdk (>=0.1.8,<0.2.0)", "jinja2 (>=3,<4)", "jq (>=1.4.1,<2.0.0)", "jsonschema (>1)", "lxml (>=4.9.2,<5.0.0)", "markdownify (>=0.11.6,<0.12.0)", "motor (>=3.3.1,<4.0.0)", "msal (>=1.25.0,<2.0.0)", "mwparserfromhell (>=0.6.4,<0.7.0)", "mwxml (>=0.3.3,<0.4.0)", "newspaper3k (>=0.2.8,<0.3.0)", "numexpr (>=2.8.6,<3.0.0)", "nvidia-riva-client (>=2.14.0,<3.0.0)", "oci (>=2.119.1,<3.0.0)", "openai (<2)", "openapi-pydantic (>=0.3.2,<0.4.0)", "oracle-ads (>=2.9.1,<3.0.0)", "pandas (>=2.0.1,<3.0.0)", "pdfminer-six (>=20221105,<20221106)", "pgvector (>=0.1.6,<0.2.0)", "praw (>=7.7.1,<8.0.0)", "psychicapi (>=0.8.0,<0.9.0)", "py-trello (>=0.19.0,<0.20.0)", "pymupdf (>=1.22.3,<2.0.0)", "pypdf (>=3.4.0,<4.0.0)", "pypdfium2 (>=4.10.0,<5.0.0)", "pyspark (>=3.4.0,<4.0.0)", "rank-bm25 (>=0.2.2,<0.3.0)", "rapidfuzz (>=3.1.1,<4.0.0)", "rapidocr-onnxruntime (>=1.3.2,<2.0.0)", "rdflib (==7.0.0)", "requests-toolbelt (>=1.0.0,<2.0.0)", "rspace_client (>=2.5.0,<3.0.0)", "scikit-learn (>=1.2.2,<2.0.0)", "sqlite-vss (>=0.1.2,<0.2.0)", "streamlit (>=1.18.0,<2.0.0)", "sympy (>=1.12,<2.0)", "telethon (>=1.28.5,<2.0.0)", "timescale-vector (>=0.0.1,<0.0.2)", "tqdm (>=4.48.0)", "tree-sitter (>=0.20.2,<0.21.0)", "tree-sitter-languages (>=1.8.0,<2.0.0)", "upstash-redis (>=0.15.0,<0.16.0)", "xata (>=1.0.0a7,<2.0.0)", "xmltodict (>=0.13.0,<0.14.0)", "zhipuai (>=1.0.7,<2.0.0)"] [[package]] name = "langchain-core" -version = "0.1.17" +version = "0.1.25" description = "Building applications with LLMs through composability" optional = false python-versions = ">=3.8.1,<4.0" files = [ - {file = "langchain_core-0.1.17-py3-none-any.whl", hash = "sha256:026155cf97867bde410ab1834799ab4c5ba64c39380f2a4328bcf9c78623ca64"}, - {file = "langchain_core-0.1.17.tar.gz", hash = "sha256:59016e457cd6a1708d83a3a454acc97cf02c2a2c3af95626d13f83894fd4e777"}, + {file = "langchain_core-0.1.25-py3-none-any.whl", hash = "sha256:ff0a0ad1ed877878e7b9c7601870cd12145abf3c814aae41995968d05ea6c09d"}, + {file = "langchain_core-0.1.25.tar.gz", hash = "sha256:065ff8b4e383c5645d175b20ae44b258330ed06457b0fc0179efee310b6f2af6"}, ] [package.dependencies] anyio = ">=3,<5" jsonpatch = ">=1.33,<2.0" -langsmith = ">=0.0.83,<0.1" +langsmith = ">=0.1.0,<0.2.0" packaging = ">=23.2,<24.0" pydantic = ">=1,<3" PyYAML = ">=5.3" @@ -1620,13 +1637,13 @@ types-requests = ">=2.31.0.2,<3.0.0.0" [[package]] name = "langsmith" -version = "0.0.85" +version = "0.1.4" description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." optional = false python-versions = ">=3.8.1,<4.0" files = [ - {file = "langsmith-0.0.85-py3-none-any.whl", hash = "sha256:9d0ccbcda7b69c83828060603a51bb4319e43b8dc807fbd90b6355f8ec709500"}, - {file = "langsmith-0.0.85.tar.gz", hash = "sha256:fefc631fc30d836b54d4e3f99961c41aea497633898b8f09e305b6c7216c2c54"}, + {file = "langsmith-0.1.4-py3-none-any.whl", hash = "sha256:13ea90c030a3ef472e00f4dd31b9c89f165f98f0c870309eca3366c93fcaa29f"}, + {file = "langsmith-0.1.4.tar.gz", hash = "sha256:b45ea1001f67c4c233b3521eb578326863e32e0eb738e52900c035261deec368"}, ] [package.dependencies] @@ -2397,6 +2414,20 @@ files = [ plugins = ["importlib-metadata"] windows-terminal = ["colorama (>=0.4.6)"] +[[package]] +name = "pyparsing" +version = "3.1.1" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +optional = false +python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.1.1-py3-none-any.whl", hash = "sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb"}, + {file = "pyparsing-3.1.1.tar.gz", hash = "sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + [[package]] name = "pytest" version = "7.4.4" @@ -3729,4 +3760,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9.0,<4.0" -content-hash = "f489f2e9159e8db255a43027617c41a389afb5fe84ff94bf8521c0f98de8cd38" +content-hash = "0e7777d77d3b34acbfdead224a2b5c5e65ecbf890c57c29bf43f5ebff09d4c0d" diff --git a/pyproject.toml b/pyproject.toml index 8df024914..e2c419264 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ repository = "https://www.github.com/langchain-ai/langgraph" [tool.poetry.dependencies] python = ">=3.9.0,<4.0" -langchain-core = "^0.1.16" +langchain-core = "^0.1.25" [tool.poetry.group.test.dependencies] @@ -26,6 +26,7 @@ httpx = "^0.26.0" pytest-watcher = "^0.3.4" langchain = "^0.1.0" aiosqlite = "^0.19.0" +grandalf = "^0.8" [tool.poetry.group.lint.dependencies] ruff = "^0.1.4" diff --git a/tests/__snapshots__/test_pregel.ambr b/tests/__snapshots__/test_pregel.ambr new file mode 100644 index 000000000..330b87bd0 --- /dev/null +++ b/tests/__snapshots__/test_pregel.ambr @@ -0,0 +1,1695 @@ +# serializer version: 1 +# name: test_conditional_graph + ''' + { + "nodes": [ + { + "id": "__start__", + "type": "schema", + "data": { + "title": "LangGraphInput" + } + }, + { + "id": "__end__", + "type": "schema", + "data": { + "title": "LangGraphOutput" + } + }, + { + "id": "agent", + "type": "runnable", + "data": { + "id": [ + "langchain", + "schema", + "runnable", + "RunnableAssign" + ], + "name": "RunnableAssign" + } + }, + { + "id": "tools", + "type": "runnable", + "data": { + "id": [ + "langchain_core", + "runnables", + "base", + "RunnableLambda" + ], + "name": "execute_tools" + } + }, + { + "id": "agent_should_continue", + "type": "runnable", + "data": { + "id": [ + "langchain_core", + "runnables", + "base", + "RunnableLambda" + ], + "name": "should_continue" + } + } + ], + "edges": [ + { + "source": "tools", + "target": "agent" + }, + { + "source": "agent", + "target": "agent_should_continue" + }, + { + "source": "agent_should_continue", + "target": "tools", + "data": "continue" + }, + { + "source": "agent_should_continue", + "target": "__end__", + "data": "exit" + }, + { + "source": "__start__", + "target": "agent" + } + ] + } + ''' +# --- +# name: test_conditional_graph.1 + ''' + +-----------+ + | __start__ | + +-----------+ + * + * + * + +-------+ + | agent | + *+-------+* + ** *** + ** ** + ** ** + +-----------------------+ ** + | agent_should_continue | * + +-----------------------+ * + * **** * + * ***** * + * *** * + +---------+ +-------+ + | __end__ | | tools | + +---------+ +-------+ + ''' +# --- +# name: test_conditional_graph_state + '{"title": "LangGraphInput", "$ref": "#/definitions/AgentState", "definitions": {"AgentAction": {"title": "AgentAction", "description": "A full description of an action for an ActionAgent to execute.", "type": "object", "properties": {"tool": {"title": "Tool", "type": "string"}, "tool_input": {"title": "Tool Input", "anyOf": [{"type": "string"}, {"type": "object"}]}, "log": {"title": "Log", "type": "string"}, "type": {"title": "Type", "default": "AgentAction", "enum": ["AgentAction"], "type": "string"}}, "required": ["tool", "tool_input", "log"]}, "AgentFinish": {"title": "AgentFinish", "description": "The final return value of an ActionAgent.", "type": "object", "properties": {"return_values": {"title": "Return Values", "type": "object"}, "log": {"title": "Log", "type": "string"}, "type": {"title": "Type", "default": "AgentFinish", "enum": ["AgentFinish"], "type": "string"}}, "required": ["return_values", "log"]}, "AgentState": {"title": "AgentState", "type": "object", "properties": {"input": {"title": "Input", "type": "string"}, "agent_outcome": {"title": "Agent Outcome", "anyOf": [{"$ref": "#/definitions/AgentAction"}, {"$ref": "#/definitions/AgentFinish"}]}, "intermediate_steps": {"title": "Intermediate Steps", "type": "array", "items": {"type": "array", "minItems": 2, "maxItems": 2, "items": [{"$ref": "#/definitions/AgentAction"}, {"type": "string"}]}}}}}}' +# --- +# name: test_conditional_graph_state.1 + '{"title": "LangGraphOutput", "$ref": "#/definitions/AgentState", "definitions": {"AgentAction": {"title": "AgentAction", "description": "A full description of an action for an ActionAgent to execute.", "type": "object", "properties": {"tool": {"title": "Tool", "type": "string"}, "tool_input": {"title": "Tool Input", "anyOf": [{"type": "string"}, {"type": "object"}]}, "log": {"title": "Log", "type": "string"}, "type": {"title": "Type", "default": "AgentAction", "enum": ["AgentAction"], "type": "string"}}, "required": ["tool", "tool_input", "log"]}, "AgentFinish": {"title": "AgentFinish", "description": "The final return value of an ActionAgent.", "type": "object", "properties": {"return_values": {"title": "Return Values", "type": "object"}, "log": {"title": "Log", "type": "string"}, "type": {"title": "Type", "default": "AgentFinish", "enum": ["AgentFinish"], "type": "string"}}, "required": ["return_values", "log"]}, "AgentState": {"title": "AgentState", "type": "object", "properties": {"input": {"title": "Input", "type": "string"}, "agent_outcome": {"title": "Agent Outcome", "anyOf": [{"$ref": "#/definitions/AgentAction"}, {"$ref": "#/definitions/AgentFinish"}]}, "intermediate_steps": {"title": "Intermediate Steps", "type": "array", "items": {"type": "array", "minItems": 2, "maxItems": 2, "items": [{"$ref": "#/definitions/AgentAction"}, {"type": "string"}]}}}}}}' +# --- +# name: test_conditional_graph_state.2 + ''' + { + "nodes": [ + { + "id": "__start__", + "type": "schema", + "data": { + "title": "LangGraphInput", + "$ref": "#/definitions/AgentState", + "definitions": { + "AgentAction": { + "title": "AgentAction", + "description": "A full description of an action for an ActionAgent to execute.", + "type": "object", + "properties": { + "tool": { + "title": "Tool", + "type": "string" + }, + "tool_input": { + "title": "Tool Input", + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + }, + "log": { + "title": "Log", + "type": "string" + }, + "type": { + "title": "Type", + "default": "AgentAction", + "enum": [ + "AgentAction" + ], + "type": "string" + } + }, + "required": [ + "tool", + "tool_input", + "log" + ] + }, + "AgentFinish": { + "title": "AgentFinish", + "description": "The final return value of an ActionAgent.", + "type": "object", + "properties": { + "return_values": { + "title": "Return Values", + "type": "object" + }, + "log": { + "title": "Log", + "type": "string" + }, + "type": { + "title": "Type", + "default": "AgentFinish", + "enum": [ + "AgentFinish" + ], + "type": "string" + } + }, + "required": [ + "return_values", + "log" + ] + }, + "AgentState": { + "title": "AgentState", + "type": "object", + "properties": { + "input": { + "title": "Input", + "type": "string" + }, + "agent_outcome": { + "title": "Agent Outcome", + "anyOf": [ + { + "$ref": "#/definitions/AgentAction" + }, + { + "$ref": "#/definitions/AgentFinish" + } + ] + }, + "intermediate_steps": { + "title": "Intermediate Steps", + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": [ + { + "$ref": "#/definitions/AgentAction" + }, + { + "type": "string" + } + ] + } + } + } + } + } + } + }, + { + "id": "__end__", + "type": "schema", + "data": { + "title": "LangGraphOutput", + "$ref": "#/definitions/AgentState", + "definitions": { + "AgentAction": { + "title": "AgentAction", + "description": "A full description of an action for an ActionAgent to execute.", + "type": "object", + "properties": { + "tool": { + "title": "Tool", + "type": "string" + }, + "tool_input": { + "title": "Tool Input", + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + }, + "log": { + "title": "Log", + "type": "string" + }, + "type": { + "title": "Type", + "default": "AgentAction", + "enum": [ + "AgentAction" + ], + "type": "string" + } + }, + "required": [ + "tool", + "tool_input", + "log" + ] + }, + "AgentFinish": { + "title": "AgentFinish", + "description": "The final return value of an ActionAgent.", + "type": "object", + "properties": { + "return_values": { + "title": "Return Values", + "type": "object" + }, + "log": { + "title": "Log", + "type": "string" + }, + "type": { + "title": "Type", + "default": "AgentFinish", + "enum": [ + "AgentFinish" + ], + "type": "string" + } + }, + "required": [ + "return_values", + "log" + ] + }, + "AgentState": { + "title": "AgentState", + "type": "object", + "properties": { + "input": { + "title": "Input", + "type": "string" + }, + "agent_outcome": { + "title": "Agent Outcome", + "anyOf": [ + { + "$ref": "#/definitions/AgentAction" + }, + { + "$ref": "#/definitions/AgentFinish" + } + ] + }, + "intermediate_steps": { + "title": "Intermediate Steps", + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": [ + { + "$ref": "#/definitions/AgentAction" + }, + { + "type": "string" + } + ] + } + } + } + } + } + } + }, + { + "id": "agent", + "type": "runnable", + "data": { + "id": [ + "langchain", + "schema", + "runnable", + "RunnableSequence" + ], + "name": "RunnableSequence" + } + }, + { + "id": "tools", + "type": "runnable", + "data": { + "id": [ + "langchain_core", + "runnables", + "base", + "RunnableLambda" + ], + "name": "execute_tools" + } + }, + { + "id": "agent_should_continue", + "type": "runnable", + "data": { + "id": [ + "langchain_core", + "runnables", + "base", + "RunnableLambda" + ], + "name": "should_continue" + } + } + ], + "edges": [ + { + "source": "tools", + "target": "agent" + }, + { + "source": "agent", + "target": "agent_should_continue" + }, + { + "source": "agent_should_continue", + "target": "tools", + "data": "continue" + }, + { + "source": "agent_should_continue", + "target": "__end__", + "data": "exit" + }, + { + "source": "__start__", + "target": "agent" + } + ] + } + ''' +# --- +# name: test_conditional_graph_state.3 + ''' + +-----------+ + | __start__ | + +-----------+ + * + * + * + +-------+ + | agent | + *+-------+* + ** *** + ** ** + ** ** + +-----------------------+ ** + | agent_should_continue | * + +-----------------------+ * + * **** * + * ***** * + * *** * + +---------+ +-------+ + | __end__ | | tools | + +---------+ +-------+ + ''' +# --- +# name: test_message_graph + '{"title": "LangGraphInput", "type": "array", "items": {"anyOf": [{"$ref": "#/definitions/AIMessage"}, {"$ref": "#/definitions/HumanMessage"}, {"$ref": "#/definitions/ChatMessage"}, {"$ref": "#/definitions/SystemMessage"}, {"$ref": "#/definitions/FunctionMessage"}, {"$ref": "#/definitions/ToolMessage"}]}, "definitions": {"AIMessage": {"title": "AIMessage", "description": "Message from an AI.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "type": {"title": "Type", "default": "ai", "enum": ["ai"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "example": {"title": "Example", "default": false, "type": "boolean"}}, "required": ["content"]}, "HumanMessage": {"title": "HumanMessage", "description": "Message from a human.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "type": {"title": "Type", "default": "human", "enum": ["human"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "example": {"title": "Example", "default": false, "type": "boolean"}}, "required": ["content"]}, "ChatMessage": {"title": "ChatMessage", "description": "Message that can be assigned an arbitrary speaker (i.e. role).", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "type": {"title": "Type", "default": "chat", "enum": ["chat"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "role": {"title": "Role", "type": "string"}}, "required": ["content", "role"]}, "SystemMessage": {"title": "SystemMessage", "description": "Message for priming AI behavior, usually passed in as the first of a sequence\\nof input messages.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "type": {"title": "Type", "default": "system", "enum": ["system"], "type": "string"}, "name": {"title": "Name", "type": "string"}}, "required": ["content"]}, "FunctionMessage": {"title": "FunctionMessage", "description": "Message for passing the result of executing a function back to a model.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "type": {"title": "Type", "default": "function", "enum": ["function"], "type": "string"}, "name": {"title": "Name", "type": "string"}}, "required": ["content", "name"]}, "ToolMessage": {"title": "ToolMessage", "description": "Message for passing the result of executing a tool back to a model.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "type": {"title": "Type", "default": "tool", "enum": ["tool"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "tool_call_id": {"title": "Tool Call Id", "type": "string"}}, "required": ["content", "tool_call_id"]}}}' +# --- +# name: test_message_graph.1 + '{"title": "LangGraphOutput", "type": "array", "items": {"anyOf": [{"$ref": "#/definitions/AIMessage"}, {"$ref": "#/definitions/HumanMessage"}, {"$ref": "#/definitions/ChatMessage"}, {"$ref": "#/definitions/SystemMessage"}, {"$ref": "#/definitions/FunctionMessage"}, {"$ref": "#/definitions/ToolMessage"}]}, "definitions": {"AIMessage": {"title": "AIMessage", "description": "Message from an AI.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "type": {"title": "Type", "default": "ai", "enum": ["ai"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "example": {"title": "Example", "default": false, "type": "boolean"}}, "required": ["content"]}, "HumanMessage": {"title": "HumanMessage", "description": "Message from a human.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "type": {"title": "Type", "default": "human", "enum": ["human"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "example": {"title": "Example", "default": false, "type": "boolean"}}, "required": ["content"]}, "ChatMessage": {"title": "ChatMessage", "description": "Message that can be assigned an arbitrary speaker (i.e. role).", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "type": {"title": "Type", "default": "chat", "enum": ["chat"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "role": {"title": "Role", "type": "string"}}, "required": ["content", "role"]}, "SystemMessage": {"title": "SystemMessage", "description": "Message for priming AI behavior, usually passed in as the first of a sequence\\nof input messages.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "type": {"title": "Type", "default": "system", "enum": ["system"], "type": "string"}, "name": {"title": "Name", "type": "string"}}, "required": ["content"]}, "FunctionMessage": {"title": "FunctionMessage", "description": "Message for passing the result of executing a function back to a model.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "type": {"title": "Type", "default": "function", "enum": ["function"], "type": "string"}, "name": {"title": "Name", "type": "string"}}, "required": ["content", "name"]}, "ToolMessage": {"title": "ToolMessage", "description": "Message for passing the result of executing a tool back to a model.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "type": {"title": "Type", "default": "tool", "enum": ["tool"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "tool_call_id": {"title": "Tool Call Id", "type": "string"}}, "required": ["content", "tool_call_id"]}}}' +# --- +# name: test_message_graph.2 + ''' + { + "nodes": [ + { + "id": "__start__", + "type": "schema", + "data": { + "title": "LangGraphInput", + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/AIMessage" + }, + { + "$ref": "#/definitions/HumanMessage" + }, + { + "$ref": "#/definitions/ChatMessage" + }, + { + "$ref": "#/definitions/SystemMessage" + }, + { + "$ref": "#/definitions/FunctionMessage" + }, + { + "$ref": "#/definitions/ToolMessage" + } + ] + }, + "definitions": { + "AIMessage": { + "title": "AIMessage", + "description": "Message from an AI.", + "type": "object", + "properties": { + "content": { + "title": "Content", + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + } + } + ] + }, + "additional_kwargs": { + "title": "Additional Kwargs", + "type": "object" + }, + "type": { + "title": "Type", + "default": "ai", + "enum": [ + "ai" + ], + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "example": { + "title": "Example", + "default": false, + "type": "boolean" + } + }, + "required": [ + "content" + ] + }, + "HumanMessage": { + "title": "HumanMessage", + "description": "Message from a human.", + "type": "object", + "properties": { + "content": { + "title": "Content", + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + } + } + ] + }, + "additional_kwargs": { + "title": "Additional Kwargs", + "type": "object" + }, + "type": { + "title": "Type", + "default": "human", + "enum": [ + "human" + ], + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "example": { + "title": "Example", + "default": false, + "type": "boolean" + } + }, + "required": [ + "content" + ] + }, + "ChatMessage": { + "title": "ChatMessage", + "description": "Message that can be assigned an arbitrary speaker (i.e. role).", + "type": "object", + "properties": { + "content": { + "title": "Content", + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + } + } + ] + }, + "additional_kwargs": { + "title": "Additional Kwargs", + "type": "object" + }, + "type": { + "title": "Type", + "default": "chat", + "enum": [ + "chat" + ], + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "role": { + "title": "Role", + "type": "string" + } + }, + "required": [ + "content", + "role" + ] + }, + "SystemMessage": { + "title": "SystemMessage", + "description": "Message for priming AI behavior, usually passed in as the first of a sequence\nof input messages.", + "type": "object", + "properties": { + "content": { + "title": "Content", + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + } + } + ] + }, + "additional_kwargs": { + "title": "Additional Kwargs", + "type": "object" + }, + "type": { + "title": "Type", + "default": "system", + "enum": [ + "system" + ], + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + } + }, + "required": [ + "content" + ] + }, + "FunctionMessage": { + "title": "FunctionMessage", + "description": "Message for passing the result of executing a function back to a model.", + "type": "object", + "properties": { + "content": { + "title": "Content", + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + } + } + ] + }, + "additional_kwargs": { + "title": "Additional Kwargs", + "type": "object" + }, + "type": { + "title": "Type", + "default": "function", + "enum": [ + "function" + ], + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + } + }, + "required": [ + "content", + "name" + ] + }, + "ToolMessage": { + "title": "ToolMessage", + "description": "Message for passing the result of executing a tool back to a model.", + "type": "object", + "properties": { + "content": { + "title": "Content", + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + } + } + ] + }, + "additional_kwargs": { + "title": "Additional Kwargs", + "type": "object" + }, + "type": { + "title": "Type", + "default": "tool", + "enum": [ + "tool" + ], + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "tool_call_id": { + "title": "Tool Call Id", + "type": "string" + } + }, + "required": [ + "content", + "tool_call_id" + ] + } + } + } + }, + { + "id": "__end__", + "type": "schema", + "data": { + "title": "LangGraphOutput", + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/AIMessage" + }, + { + "$ref": "#/definitions/HumanMessage" + }, + { + "$ref": "#/definitions/ChatMessage" + }, + { + "$ref": "#/definitions/SystemMessage" + }, + { + "$ref": "#/definitions/FunctionMessage" + }, + { + "$ref": "#/definitions/ToolMessage" + } + ] + }, + "definitions": { + "AIMessage": { + "title": "AIMessage", + "description": "Message from an AI.", + "type": "object", + "properties": { + "content": { + "title": "Content", + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + } + } + ] + }, + "additional_kwargs": { + "title": "Additional Kwargs", + "type": "object" + }, + "type": { + "title": "Type", + "default": "ai", + "enum": [ + "ai" + ], + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "example": { + "title": "Example", + "default": false, + "type": "boolean" + } + }, + "required": [ + "content" + ] + }, + "HumanMessage": { + "title": "HumanMessage", + "description": "Message from a human.", + "type": "object", + "properties": { + "content": { + "title": "Content", + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + } + } + ] + }, + "additional_kwargs": { + "title": "Additional Kwargs", + "type": "object" + }, + "type": { + "title": "Type", + "default": "human", + "enum": [ + "human" + ], + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "example": { + "title": "Example", + "default": false, + "type": "boolean" + } + }, + "required": [ + "content" + ] + }, + "ChatMessage": { + "title": "ChatMessage", + "description": "Message that can be assigned an arbitrary speaker (i.e. role).", + "type": "object", + "properties": { + "content": { + "title": "Content", + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + } + } + ] + }, + "additional_kwargs": { + "title": "Additional Kwargs", + "type": "object" + }, + "type": { + "title": "Type", + "default": "chat", + "enum": [ + "chat" + ], + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "role": { + "title": "Role", + "type": "string" + } + }, + "required": [ + "content", + "role" + ] + }, + "SystemMessage": { + "title": "SystemMessage", + "description": "Message for priming AI behavior, usually passed in as the first of a sequence\nof input messages.", + "type": "object", + "properties": { + "content": { + "title": "Content", + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + } + } + ] + }, + "additional_kwargs": { + "title": "Additional Kwargs", + "type": "object" + }, + "type": { + "title": "Type", + "default": "system", + "enum": [ + "system" + ], + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + } + }, + "required": [ + "content" + ] + }, + "FunctionMessage": { + "title": "FunctionMessage", + "description": "Message for passing the result of executing a function back to a model.", + "type": "object", + "properties": { + "content": { + "title": "Content", + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + } + } + ] + }, + "additional_kwargs": { + "title": "Additional Kwargs", + "type": "object" + }, + "type": { + "title": "Type", + "default": "function", + "enum": [ + "function" + ], + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + } + }, + "required": [ + "content", + "name" + ] + }, + "ToolMessage": { + "title": "ToolMessage", + "description": "Message for passing the result of executing a tool back to a model.", + "type": "object", + "properties": { + "content": { + "title": "Content", + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + } + } + ] + }, + "additional_kwargs": { + "title": "Additional Kwargs", + "type": "object" + }, + "type": { + "title": "Type", + "default": "tool", + "enum": [ + "tool" + ], + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "tool_call_id": { + "title": "Tool Call Id", + "type": "string" + } + }, + "required": [ + "content", + "tool_call_id" + ] + } + } + } + }, + { + "id": "agent", + "type": "runnable", + "data": { + "id": [ + "test_pregel", + "FakeFuntionChatModel" + ], + "name": "FakeFuntionChatModel" + } + }, + { + "id": "action", + "type": "runnable", + "data": { + "id": [ + "langchain_core", + "runnables", + "base", + "RunnableLambda" + ], + "name": "call_tool" + } + }, + { + "id": "agent_should_continue", + "type": "runnable", + "data": { + "id": [ + "langchain_core", + "runnables", + "base", + "RunnableLambda" + ], + "name": "should_continue" + } + } + ], + "edges": [ + { + "source": "action", + "target": "agent" + }, + { + "source": "agent", + "target": "agent_should_continue" + }, + { + "source": "agent_should_continue", + "target": "action", + "data": "continue" + }, + { + "source": "agent_should_continue", + "target": "__end__", + "data": "end" + }, + { + "source": "__start__", + "target": "agent" + } + ] + } + ''' +# --- +# name: test_message_graph.3 + ''' + +-----------+ + | __start__ | + +-----------+ + * + * + * + +-------+ + | agent | + *+-------+* + ** *** + ** ** + ** ** + +-----------------------+ ** + | agent_should_continue | * + +-----------------------+ * + * ***** * + * **** * + * *** * + +---------+ +--------+ + | __end__ | | action | + +---------+ +--------+ + ''' +# --- +# name: test_prebuilt_chat + '{"title": "LangGraphInput", "$ref": "#/definitions/AgentState", "definitions": {"BaseMessage": {"title": "BaseMessage", "description": "Base abstract Message class.\\n\\nMessages are the inputs and outputs of ChatModels.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "type": {"title": "Type", "type": "string"}, "name": {"title": "Name", "type": "string"}}, "required": ["content", "type"]}, "AgentState": {"title": "AgentState", "type": "object", "properties": {"messages": {"title": "Messages", "type": "array", "items": {"$ref": "#/definitions/BaseMessage"}}}, "required": ["messages"]}}}' +# --- +# name: test_prebuilt_chat.1 + '{"title": "LangGraphOutput", "$ref": "#/definitions/AgentState", "definitions": {"BaseMessage": {"title": "BaseMessage", "description": "Base abstract Message class.\\n\\nMessages are the inputs and outputs of ChatModels.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "type": {"title": "Type", "type": "string"}, "name": {"title": "Name", "type": "string"}}, "required": ["content", "type"]}, "AgentState": {"title": "AgentState", "type": "object", "properties": {"messages": {"title": "Messages", "type": "array", "items": {"$ref": "#/definitions/BaseMessage"}}}, "required": ["messages"]}}}' +# --- +# name: test_prebuilt_chat.2 + ''' + { + "nodes": [ + { + "id": "__start__", + "type": "schema", + "data": { + "title": "LangGraphInput", + "$ref": "#/definitions/AgentState", + "definitions": { + "BaseMessage": { + "title": "BaseMessage", + "description": "Base abstract Message class.\n\nMessages are the inputs and outputs of ChatModels.", + "type": "object", + "properties": { + "content": { + "title": "Content", + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + } + } + ] + }, + "additional_kwargs": { + "title": "Additional Kwargs", + "type": "object" + }, + "type": { + "title": "Type", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + } + }, + "required": [ + "content", + "type" + ] + }, + "AgentState": { + "title": "AgentState", + "type": "object", + "properties": { + "messages": { + "title": "Messages", + "type": "array", + "items": { + "$ref": "#/definitions/BaseMessage" + } + } + }, + "required": [ + "messages" + ] + } + } + } + }, + { + "id": "__end__", + "type": "schema", + "data": { + "title": "LangGraphOutput", + "$ref": "#/definitions/AgentState", + "definitions": { + "BaseMessage": { + "title": "BaseMessage", + "description": "Base abstract Message class.\n\nMessages are the inputs and outputs of ChatModels.", + "type": "object", + "properties": { + "content": { + "title": "Content", + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + } + } + ] + }, + "additional_kwargs": { + "title": "Additional Kwargs", + "type": "object" + }, + "type": { + "title": "Type", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + } + }, + "required": [ + "content", + "type" + ] + }, + "AgentState": { + "title": "AgentState", + "type": "object", + "properties": { + "messages": { + "title": "Messages", + "type": "array", + "items": { + "$ref": "#/definitions/BaseMessage" + } + } + }, + "required": [ + "messages" + ] + } + } + } + }, + { + "id": "agent", + "type": "runnable", + "data": { + "id": [ + "langchain_core", + "runnables", + "base", + "RunnableLambda" + ], + "name": "call_model" + } + }, + { + "id": "action", + "type": "runnable", + "data": { + "id": [ + "langchain_core", + "runnables", + "base", + "RunnableLambda" + ], + "name": "call_tool" + } + }, + { + "id": "agent_should_continue", + "type": "runnable", + "data": { + "id": [ + "langchain_core", + "runnables", + "base", + "RunnableLambda" + ], + "name": "should_continue" + } + } + ], + "edges": [ + { + "source": "action", + "target": "agent" + }, + { + "source": "agent", + "target": "agent_should_continue" + }, + { + "source": "agent_should_continue", + "target": "action", + "data": "continue" + }, + { + "source": "agent_should_continue", + "target": "__end__", + "data": "end" + }, + { + "source": "__start__", + "target": "agent" + } + ] + } + ''' +# --- +# name: test_prebuilt_chat.3 + ''' + +-----------+ + | __start__ | + +-----------+ + * + * + * + +-------+ + | agent | + *+-------+* + ** *** + ** ** + ** ** + +-----------------------+ ** + | agent_should_continue | * + +-----------------------+ * + * ***** * + * **** * + * *** * + +---------+ +--------+ + | __end__ | | action | + +---------+ +--------+ + ''' +# --- +# name: test_prebuilt_tool_chat + '{"title": "LangGraphInput", "$ref": "#/definitions/AgentState", "definitions": {"BaseMessage": {"title": "BaseMessage", "description": "Base abstract Message class.\\n\\nMessages are the inputs and outputs of ChatModels.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "type": {"title": "Type", "type": "string"}, "name": {"title": "Name", "type": "string"}}, "required": ["content", "type"]}, "AgentState": {"title": "AgentState", "type": "object", "properties": {"messages": {"title": "Messages", "type": "array", "items": {"$ref": "#/definitions/BaseMessage"}}}, "required": ["messages"]}}}' +# --- +# name: test_prebuilt_tool_chat.1 + '{"title": "LangGraphOutput", "$ref": "#/definitions/AgentState", "definitions": {"BaseMessage": {"title": "BaseMessage", "description": "Base abstract Message class.\\n\\nMessages are the inputs and outputs of ChatModels.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "type": {"title": "Type", "type": "string"}, "name": {"title": "Name", "type": "string"}}, "required": ["content", "type"]}, "AgentState": {"title": "AgentState", "type": "object", "properties": {"messages": {"title": "Messages", "type": "array", "items": {"$ref": "#/definitions/BaseMessage"}}}, "required": ["messages"]}}}' +# --- +# name: test_prebuilt_tool_chat.2 + ''' + { + "nodes": [ + { + "id": "__start__", + "type": "schema", + "data": { + "title": "LangGraphInput", + "$ref": "#/definitions/AgentState", + "definitions": { + "BaseMessage": { + "title": "BaseMessage", + "description": "Base abstract Message class.\n\nMessages are the inputs and outputs of ChatModels.", + "type": "object", + "properties": { + "content": { + "title": "Content", + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + } + } + ] + }, + "additional_kwargs": { + "title": "Additional Kwargs", + "type": "object" + }, + "type": { + "title": "Type", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + } + }, + "required": [ + "content", + "type" + ] + }, + "AgentState": { + "title": "AgentState", + "type": "object", + "properties": { + "messages": { + "title": "Messages", + "type": "array", + "items": { + "$ref": "#/definitions/BaseMessage" + } + } + }, + "required": [ + "messages" + ] + } + } + } + }, + { + "id": "__end__", + "type": "schema", + "data": { + "title": "LangGraphOutput", + "$ref": "#/definitions/AgentState", + "definitions": { + "BaseMessage": { + "title": "BaseMessage", + "description": "Base abstract Message class.\n\nMessages are the inputs and outputs of ChatModels.", + "type": "object", + "properties": { + "content": { + "title": "Content", + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + } + } + ] + }, + "additional_kwargs": { + "title": "Additional Kwargs", + "type": "object" + }, + "type": { + "title": "Type", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + } + }, + "required": [ + "content", + "type" + ] + }, + "AgentState": { + "title": "AgentState", + "type": "object", + "properties": { + "messages": { + "title": "Messages", + "type": "array", + "items": { + "$ref": "#/definitions/BaseMessage" + } + } + }, + "required": [ + "messages" + ] + } + } + } + }, + { + "id": "agent", + "type": "runnable", + "data": { + "id": [ + "langchain_core", + "runnables", + "base", + "RunnableLambda" + ], + "name": "call_model" + } + }, + { + "id": "action", + "type": "runnable", + "data": { + "id": [ + "langchain_core", + "runnables", + "base", + "RunnableLambda" + ], + "name": "call_tool" + } + }, + { + "id": "agent_should_continue", + "type": "runnable", + "data": { + "id": [ + "langchain_core", + "runnables", + "base", + "RunnableLambda" + ], + "name": "should_continue" + } + } + ], + "edges": [ + { + "source": "action", + "target": "agent" + }, + { + "source": "agent", + "target": "agent_should_continue" + }, + { + "source": "agent_should_continue", + "target": "action", + "data": "continue" + }, + { + "source": "agent_should_continue", + "target": "__end__", + "data": "end" + }, + { + "source": "__start__", + "target": "agent" + } + ] + } + ''' +# --- +# name: test_prebuilt_tool_chat.3 + ''' + +-----------+ + | __start__ | + +-----------+ + * + * + * + +-------+ + | agent | + *+-------+* + ** *** + ** ** + ** ** + +-----------------------+ ** + | agent_should_continue | * + +-----------------------+ * + * ***** * + * **** * + * *** * + +---------+ +--------+ + | __end__ | | action | + +---------+ +--------+ + ''' +# --- diff --git a/tests/test_pregel.py b/tests/test_pregel.py index cce76cf85..33788d9d5 100644 --- a/tests/test_pregel.py +++ b/tests/test_pregel.py @@ -9,6 +9,7 @@ from typing import Annotated, Generator, Optional, TypedDict, Union import pytest from langchain_core.runnables import RunnablePassthrough from pytest_mock import MockerFixture +from syrupy import SnapshotAssertion from langgraph.channels.base import InvalidUpdateError from langgraph.channels.binop import BinaryOperatorAggregate @@ -659,7 +660,7 @@ def test_channel_enter_exit_timing(mocker: MockerFixture) -> None: assert cleanup.call_count == 1, "Expected cleanup to be called once" -def test_conditional_graph() -> None: +def test_conditional_graph(snapshot: SnapshotAssertion) -> None: from copy import deepcopy from langchain.llms.fake import FakeStreamingListLLM @@ -732,6 +733,9 @@ def test_conditional_graph() -> None: app = workflow.compile() + assert json.dumps(app.get_graph().to_json(), indent=2) == snapshot + assert app.get_graph().draw_ascii() == snapshot + assert app.invoke({"input": "what is weather in sf"}) == { "input": "what is weather in sf", "intermediate_steps": [ @@ -879,13 +883,13 @@ def test_conditional_graph() -> None: ] -def test_conditional_graph_state() -> None: +def test_conditional_graph_state(snapshot: SnapshotAssertion) -> None: from langchain.llms.fake import FakeStreamingListLLM from langchain_community.tools import tool from langchain_core.agents import AgentAction, AgentFinish from langchain_core.prompts import PromptTemplate - class AgentState(TypedDict): + class AgentState(TypedDict, total=False): input: str agent_outcome: Optional[Union[AgentAction, AgentFinish]] intermediate_steps: Annotated[list[tuple[AgentAction, str]], operator.add] @@ -959,6 +963,11 @@ def test_conditional_graph_state() -> None: app = workflow.compile() + assert app.get_input_schema().schema_json() == snapshot + assert app.get_output_schema().schema_json() == snapshot + assert json.dumps(app.get_graph().to_json(), indent=2) == snapshot + assert app.get_graph().draw_ascii() == snapshot + assert app.invoke({"input": "what is weather in sf"}) == { "input": "what is weather in sf", "intermediate_steps": [ @@ -1065,7 +1074,7 @@ def test_conditional_graph_state() -> None: ] -def test_prebuilt_tool_chat() -> None: +def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None: from langchain.chat_models.fake import FakeMessagesListChatModel from langchain_community.tools import tool from langchain_core.messages import AIMessage, HumanMessage, ToolMessage @@ -1128,6 +1137,11 @@ def test_prebuilt_tool_chat() -> None: tools, ) + assert app.get_input_schema().schema_json() == snapshot + assert app.get_output_schema().schema_json() == snapshot + assert json.dumps(app.get_graph().to_json(), indent=2) == snapshot + assert app.get_graph().draw_ascii() == snapshot + assert app.invoke( {"messages": [HumanMessage(content="what is weather in sf")]} ) == { @@ -1309,7 +1323,7 @@ def test_prebuilt_tool_chat() -> None: ] -def test_prebuilt_chat() -> None: +def test_prebuilt_chat(snapshot: SnapshotAssertion) -> None: from langchain.chat_models.fake import FakeMessagesListChatModel from langchain_community.tools import tool from langchain_core.messages import AIMessage, FunctionMessage, HumanMessage @@ -1352,6 +1366,11 @@ def test_prebuilt_chat() -> None: tools, ) + assert app.get_input_schema().schema_json() == snapshot + assert app.get_output_schema().schema_json() == snapshot + assert json.dumps(app.get_graph().to_json(), indent=2) == snapshot + assert app.get_graph().draw_ascii() == snapshot + assert app.invoke( {"messages": [HumanMessage(content="what is weather in sf")]} ) == { @@ -1454,7 +1473,7 @@ def test_prebuilt_chat() -> None: ] -def test_message_graph() -> None: +def test_message_graph(snapshot: SnapshotAssertion) -> None: from langchain.chat_models.fake import FakeMessagesListChatModel from langchain_community.tools import tool from langchain_core.agents import AgentAction @@ -1565,6 +1584,11 @@ def test_message_graph() -> None: # meaning you can use it as you would any other runnable app = workflow.compile() + assert app.get_input_schema().schema_json() == snapshot + assert app.get_output_schema().schema_json() == snapshot + assert json.dumps(app.get_graph().to_json(), indent=2) == snapshot + assert app.get_graph().draw_ascii() == snapshot + assert app.invoke(HumanMessage(content="what is weather in sf")) == [ HumanMessage(content="what is weather in sf"), AIMessage(