From a83fd19b293e0bbdd4be5b51284328d6c89ebcd9 Mon Sep 17 00:00:00 2001 From: Nuno Campos Date: Mon, 29 Apr 2024 10:38:06 -0700 Subject: [PATCH 1/7] For cond edges support specifying list of possible destinations as a list or typing annotation --- langgraph/graph/graph.py | 14 ++- tests/__snapshots__/test_pregel.ambr | 132 +++++++++++++-------------- tests/test_pregel.py | 10 +- 3 files changed, 85 insertions(+), 71 deletions(-) diff --git a/langgraph/graph/graph.py b/langgraph/graph/graph.py index 7291b4e15..7580eb797 100644 --- a/langgraph/graph/graph.py +++ b/langgraph/graph/graph.py @@ -5,11 +5,15 @@ from typing import ( Awaitable, Callable, Dict, + Literal, NamedTuple, Optional, Sequence, Union, cast, + get_args, + get_origin, + get_type_hints, ) from langchain_core.runnables import Runnable @@ -142,7 +146,7 @@ class Graph: Callable[..., Awaitable[Union[str, list[str]]]], Runnable[Any, Union[str, list[str]]], ], - path_map: Optional[dict[str, str]] = None, + path_map: Optional[Union[dict[str, str], list[str]]] = None, then: Optional[str] = None, ) -> None: """Add a conditional edge from the starting node to any number of destination nodes. @@ -166,6 +170,14 @@ class Graph: "Adding an edge to a graph that has already been compiled. This will " "not be reflected in the compiled graph." ) + # coerce path_map to a dictionary + if isinstance(path_map, dict): + pass + elif isinstance(path_map, list): + path_map = {name: name for name in path_map} + elif rtn_type := get_type_hints(path).get("return"): + if get_origin(rtn_type) is Literal: + path_map = {name: name for name in get_args(rtn_type)} # find a name for the condition path = coerce_to_runnable(path, name=None, trace=True) name = path.name or "condition" diff --git a/tests/__snapshots__/test_pregel.ambr b/tests/__snapshots__/test_pregel.ambr index baef3d081..18ded1bc0 100644 --- a/tests/__snapshots__/test_pregel.ambr +++ b/tests/__snapshots__/test_pregel.ambr @@ -1541,76 +1541,76 @@ # --- # name: test_in_one_fan_out_state_graph_waiting_edge_via_branch[end_of_run] ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +---------------+ - | rewrite_query | - +---------------+ - *** *** - * * - ** ** - +--------------+ +-----------+ - | analyzer_one | | condition | - +--------------+ +-----------+ - * . - * . - * . - +---------------+ +---------------+ - | retriever_one | | retriever_two | - +---------------+ +---------------+ - *** *** - * * - ** ** - +----+ - | qa | - +----+ - * - * - * - +---------+ - | __end__ | - +---------+ + +-----------+ + | __start__ | + +-----------+ + * + * + * + +---------------+ + | rewrite_query | + +---------------+ + *** *** + * * + ** ** + +--------------+ +--------------------+ + | analyzer_one | | rewrite_query_then | + +--------------+ +--------------------+ + * . + * . + * . + +---------------+ +---------------+ + | retriever_one | | retriever_two | + +---------------+ +---------------+ + *** *** + * * + ** ** + +----+ + | qa | + +----+ + * + * + * + +---------+ + | __end__ | + +---------+ ''' # --- # name: test_in_one_fan_out_state_graph_waiting_edge_via_branch[end_of_step] ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +---------------+ - | rewrite_query | - +---------------+ - *** *** - * * - ** ** - +--------------+ +-----------+ - | analyzer_one | | condition | - +--------------+ +-----------+ - * . - * . - * . - +---------------+ +---------------+ - | retriever_one | | retriever_two | - +---------------+ +---------------+ - *** *** - * * - ** ** - +----+ - | qa | - +----+ - * - * - * - +---------+ - | __end__ | - +---------+ + +-----------+ + | __start__ | + +-----------+ + * + * + * + +---------------+ + | rewrite_query | + +---------------+ + *** *** + * * + ** ** + +--------------+ +--------------------+ + | analyzer_one | | rewrite_query_then | + +--------------+ +--------------------+ + * . + * . + * . + +---------------+ +---------------+ + | retriever_one | | retriever_two | + +---------------+ +---------------+ + *** *** + * * + ** ** + +----+ + | qa | + +----+ + * + * + * + +---------+ + | __end__ | + +---------+ ''' # --- # name: test_message_graph[end_of_run] diff --git a/tests/test_pregel.py b/tests/test_pregel.py index 631f666b6..550dcea64 100644 --- a/tests/test_pregel.py +++ b/tests/test_pregel.py @@ -4,7 +4,7 @@ import time import warnings from concurrent.futures import ThreadPoolExecutor from contextlib import contextmanager -from typing import Annotated, Any, Generator, Optional, TypedDict, Union +from typing import Annotated, Any, Generator, Literal, Optional, TypedDict, Union import pytest from langchain_core.runnables import RunnableLambda, RunnablePassthrough @@ -3115,6 +3115,7 @@ def test_branch_then(snapshot: SnapshotAssertion, checkpoint_at: CheckpointAt) - invalid_graph.add_conditional_edges( source="prepare", path=lambda s: "tool_two_slow" if s["market"] == "DE" else "tool_two_fast", + path_map=["tool_two_slow", "tool_two_fast"], ) invalid_graph.add_node("prepare", lambda s: {"my_key": " prepared"}) invalid_graph.add_node("tool_two_slow", lambda s: {"my_key": " slow"}) @@ -3387,6 +3388,9 @@ def test_in_one_fan_out_state_graph_waiting_edge_via_branch( def qa(data: State) -> State: return {"answer": ",".join(data["docs"])} + def rewrite_query_then(data: State) -> Literal["retriever_two"]: + return "retriever_two" + workflow = StateGraph(State) workflow.add_node("rewrite_query", rewrite_query) @@ -3398,9 +3402,7 @@ def test_in_one_fan_out_state_graph_waiting_edge_via_branch( workflow.set_entry_point("rewrite_query") workflow.add_edge("rewrite_query", "analyzer_one") workflow.add_edge("analyzer_one", "retriever_one") - workflow.add_conditional_edges( - "rewrite_query", lambda _: "retriever_two", {"retriever_two": "retriever_two"} - ) + workflow.add_conditional_edges("rewrite_query", rewrite_query_then) workflow.add_edge(["retriever_one", "retriever_two"], "qa") workflow.set_finish_point("qa") From 4a28f199221cee6aa8300a0c1fc8a05b4cea988a Mon Sep 17 00:00:00 2001 From: Nuno Campos Date: Mon, 29 Apr 2024 10:44:45 -0700 Subject: [PATCH 2/7] Remove labels if redundant --- langgraph/graph/graph.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/langgraph/graph/graph.py b/langgraph/graph/graph.py index 7580eb797..df8c59639 100644 --- a/langgraph/graph/graph.py +++ b/langgraph/graph/graph.py @@ -450,13 +450,21 @@ class CompiledGraph(Pregel): ) graph.add_edge(start_nodes[start], cond) for label, end in ends.items(): - graph.add_edge(cond, end_nodes[end], label, conditional=True) + graph.add_edge( + cond, + end_nodes[end], + label if label != end else None, + conditional=True, + ) if branch.then is not None: graph.add_edge(start_nodes[end], end_nodes[branch.then]) else: for label, end in ends.items(): graph.add_edge( - start_nodes[start], end_nodes[end], label, conditional=True + start_nodes[start], + end_nodes[end], + label if label != end else None, + conditional=True, ) if branch.then is not None: graph.add_edge(start_nodes[end], end_nodes[branch.then]) From 3a59fb22475003cfb9494341a534bd7ffdab41db Mon Sep 17 00:00:00 2001 From: Nuno Campos Date: Mon, 29 Apr 2024 11:04:15 -0700 Subject: [PATCH 3/7] Pass add_condition_node --- langgraph/graph/graph.py | 1 + tests/__snapshots__/test_pregel.ambr | 83 ++++++++++++++++++---------- tests/test_pregel.py | 3 + 3 files changed, 57 insertions(+), 30 deletions(-) diff --git a/langgraph/graph/graph.py b/langgraph/graph/graph.py index df8c59639..2e6042bbc 100644 --- a/langgraph/graph/graph.py +++ b/langgraph/graph/graph.py @@ -406,6 +406,7 @@ class CompiledGraph(Pregel): subgraph = ( node.get_graph( config=config, + add_condition_nodes=add_condition_nodes, xray=xray - 1 if isinstance(xray, int) and xray > 0 else xray, ) if isinstance(node, CompiledGraph) diff --git a/tests/__snapshots__/test_pregel.ambr b/tests/__snapshots__/test_pregel.ambr index 18ded1bc0..adc8c6368 100644 --- a/tests/__snapshots__/test_pregel.ambr +++ b/tests/__snapshots__/test_pregel.ambr @@ -81,9 +81,9 @@ __start__ --> prepare; finish --> __end__; prepare --> condition; - condition -. tool_two_slow .-> tool_two_slow; + condition -.-> tool_two_slow; tool_two_slow --> finish; - condition -. tool_two_fast .-> tool_two_fast; + condition -.-> tool_two_fast; tool_two_fast --> finish; classDef startclass fill:#ffdfba; classDef endclass fill:#baffc9; @@ -103,9 +103,9 @@ finish([finish]):::otherclass; __start__ --> prepare; finish --> __end__; - prepare -. tool_two_slow .-> tool_two_slow; + prepare -.-> tool_two_slow; tool_two_slow --> finish; - prepare -. tool_two_fast .-> tool_two_fast; + prepare -.-> tool_two_fast; tool_two_fast --> finish; classDef startclass fill:#ffdfba; classDef endclass fill:#baffc9; @@ -195,9 +195,9 @@ __start__ --> prepare; finish --> __end__; prepare --> condition; - condition -. tool_two_slow .-> tool_two_slow; + condition -.-> tool_two_slow; tool_two_slow --> finish; - condition -. tool_two_fast .-> tool_two_fast; + condition -.-> tool_two_fast; tool_two_fast --> finish; classDef startclass fill:#ffdfba; classDef endclass fill:#baffc9; @@ -217,9 +217,9 @@ finish([finish]):::otherclass; __start__ --> prepare; finish --> __end__; - prepare -. tool_two_slow .-> tool_two_slow; + prepare -.-> tool_two_slow; tool_two_slow --> finish; - prepare -. tool_two_fast .-> tool_two_fast; + prepare -.-> tool_two_fast; tool_two_fast --> finish; classDef startclass fill:#ffdfba; classDef endclass fill:#baffc9; @@ -324,7 +324,6 @@ { "source": "condition", "target": "__end__", - "data": "__end__", "conditional": true } ] @@ -459,7 +458,6 @@ { "source": "condition", "target": "__end__", - "data": "__end__", "conditional": true } ] @@ -1884,7 +1882,6 @@ 'edges': list([ dict({ 'conditional': True, - 'data': 'tool_two_slow', 'source': 'tool_two:condition', 'target': 'tool_two:tool_two_slow', }), @@ -1894,7 +1891,6 @@ }), dict({ 'conditional': True, - 'data': 'tool_two_fast', 'source': 'tool_two:condition', 'target': 'tool_two:tool_two_fast', }), @@ -1908,7 +1904,6 @@ }), dict({ 'conditional': True, - 'data': 'tool_one', 'source': 'condition', 'target': 'tool_one', }), @@ -1918,7 +1913,6 @@ }), dict({ 'conditional': True, - 'data': 'tool_two', 'source': 'condition', 'target': 'tool_two:condition', }), @@ -1928,7 +1922,6 @@ }), dict({ 'conditional': True, - 'data': 'tool_three', 'source': 'condition', 'target': 'tool_three', }), @@ -2067,17 +2060,47 @@ tool_three([tool_three]):::otherclass; condition([condition]):::otherclass; subgraph tool_two - tool_two_condition -. tool_two_slow .-> tool_two_tool_two_slow; + tool_two_condition -.-> tool_two_tool_two_slow; tool_two_tool_two_slow --> tool_two___end__; - tool_two_condition -. tool_two_fast .-> tool_two_tool_two_fast; + tool_two_condition -.-> tool_two_tool_two_fast; tool_two_tool_two_fast --> tool_two___end__; end __start__ --> condition; - condition -. tool_one .-> tool_one; + condition -.-> tool_one; tool_one --> __end__; - condition -. tool_two .-> tool_two_condition; + condition -.-> tool_two_condition; tool_two___end__ --> __end__; - condition -. tool_three .-> tool_three; + condition -.-> tool_three; + tool_three --> __end__; + classDef startclass fill:#ffdfba; + classDef endclass fill:#baffc9; + classDef otherclass fill:#fad7de; + + ''' +# --- +# name: test_nested_graph_xray.3 + ''' + %%{init: {'flowchart': {'curve': 'linear'}}}%% + graph TD; + __start__[__start__]:::startclass; + __end__[__end__]:::endclass; + tool_one([tool_one]):::otherclass; + tool_two___start__([__start__]):::otherclass; + tool_two___end__([__end__]):::otherclass; + tool_two_tool_two_slow([tool_two_slow]):::otherclass; + tool_two_tool_two_fast([tool_two_fast]):::otherclass; + tool_three([tool_three]):::otherclass; + subgraph tool_two + tool_two___start__ -.-> tool_two_tool_two_slow; + tool_two_tool_two_slow --> tool_two___end__; + tool_two___start__ -.-> tool_two_tool_two_fast; + tool_two_tool_two_fast --> tool_two___end__; + end + __start__ -.-> tool_one; + tool_one --> __end__; + __start__ -.-> tool_two___start__; + tool_two___end__ --> __end__; + __start__ -.-> tool_three; tool_three --> __end__; classDef startclass fill:#ffdfba; classDef endclass fill:#baffc9; @@ -2324,8 +2347,8 @@ Chart_Generator_router -. call_tool .-> Call_Tool; Chart_Generator_router -. end .-> __end__; Call_Tool --> condition; - condition -. Researcher .-> Researcher; - condition -. Chart Generator .-> Chart_Generator; + condition -.-> Researcher; + condition -.-> Chart_Generator; ''' # --- @@ -2414,9 +2437,9 @@ tool_two_fast([tool_two_fast]):::otherclass; condition([condition]):::otherclass; __start__ --> condition; - condition -. tool_two_slow .-> tool_two_slow; + condition -.-> tool_two_slow; tool_two_slow --> __end__; - condition -. tool_two_fast .-> tool_two_fast; + condition -.-> tool_two_fast; tool_two_fast --> __end__; classDef startclass fill:#ffdfba; classDef endclass fill:#baffc9; @@ -2432,9 +2455,9 @@ __end__[__end__]:::endclass; tool_two_slow([tool_two_slow]):::otherclass; tool_two_fast([tool_two_fast]):::otherclass; - __start__ -. tool_two_slow .-> tool_two_slow; + __start__ -.-> tool_two_slow; tool_two_slow --> __end__; - __start__ -. tool_two_fast .-> tool_two_fast; + __start__ -.-> tool_two_fast; tool_two_fast --> __end__; classDef startclass fill:#ffdfba; classDef endclass fill:#baffc9; @@ -2496,9 +2519,9 @@ tool_two_fast([tool_two_fast]):::otherclass; condition([condition]):::otherclass; __start__ --> condition; - condition -. tool_two_slow .-> tool_two_slow; + condition -.-> tool_two_slow; tool_two_slow --> __end__; - condition -. tool_two_fast .-> tool_two_fast; + condition -.-> tool_two_fast; tool_two_fast --> __end__; classDef startclass fill:#ffdfba; classDef endclass fill:#baffc9; @@ -2514,9 +2537,9 @@ __end__[__end__]:::endclass; tool_two_slow([tool_two_slow]):::otherclass; tool_two_fast([tool_two_fast]):::otherclass; - __start__ -. tool_two_slow .-> tool_two_slow; + __start__ -.-> tool_two_slow; tool_two_slow --> __end__; - __start__ -. tool_two_fast .-> tool_two_fast; + __start__ -.-> tool_two_fast; tool_two_fast --> __end__; classDef startclass fill:#ffdfba; classDef endclass fill:#baffc9; diff --git a/tests/test_pregel.py b/tests/test_pregel.py index 550dcea64..8e8222697 100644 --- a/tests/test_pregel.py +++ b/tests/test_pregel.py @@ -3877,6 +3877,9 @@ def test_nested_graph_xray(snapshot: SnapshotAssertion) -> None: assert app.get_graph(xray=True).to_json() == snapshot assert app.get_graph().draw_ascii() == snapshot assert app.get_graph(xray=True).draw_mermaid() == snapshot + assert ( + app.get_graph(xray=True, add_condition_nodes=False).draw_mermaid() == snapshot + ) def test_nested_graph(snapshot: SnapshotAssertion) -> None: From a3c867d68810caeeb726635b840a02c583d5c7a0 Mon Sep 17 00:00:00 2001 From: Nuno Campos Date: Mon, 29 Apr 2024 11:13:16 -0700 Subject: [PATCH 4/7] Remove add_condition_nodes --- langgraph/graph/graph.py | 40 +- tests/__snapshots__/test_pregel.ambr | 1262 ++++++-------------- tests/__snapshots__/test_pregel_async.ambr | 48 +- tests/test_pregel.py | 19 +- 4 files changed, 374 insertions(+), 995 deletions(-) diff --git a/langgraph/graph/graph.py b/langgraph/graph/graph.py index 2e6042bbc..23c86d83a 100644 --- a/langgraph/graph/graph.py +++ b/langgraph/graph/graph.py @@ -390,7 +390,6 @@ class CompiledGraph(Pregel): config: Optional[RunnableConfig] = None, *, xray: Union[int, bool] = False, - add_condition_nodes: bool = True, ) -> DrawableGraph: """Returns a drawable representation of the computation graph.""" graph = DrawableGraph() @@ -406,7 +405,6 @@ class CompiledGraph(Pregel): subgraph = ( node.get_graph( config=config, - add_condition_nodes=add_condition_nodes, xray=xray - 1 if isinstance(xray, int) and xray > 0 else xray, ) if isinstance(node, CompiledGraph) @@ -428,46 +426,26 @@ class CompiledGraph(Pregel): end_nodes[key] = n for start, end in sorted(self.graph._all_edges): graph.add_edge(start_nodes[start], end_nodes[end]) - branches_by_name = Counter( - name for _, branches in self.graph.branches.items() for name in branches - ) for start, branches in self.graph.branches.items(): default_ends = { **{k: k for k in self.graph.nodes if k != start}, END: END, } - for name, branch in branches.items(): + for _, branch in branches.items(): if branch.ends is not None: ends = branch.ends elif branch.then is not None: ends = {k: k for k in default_ends if k not in (END, branch.then)} else: ends = default_ends - - if add_condition_nodes is True: - cond = graph.add_node( - branch.path, - f"{start}_{name}" if branches_by_name[name] > 1 else name, + for label, end in ends.items(): + graph.add_edge( + start_nodes[start], + end_nodes[end], + label if label != end else None, + conditional=True, ) - graph.add_edge(start_nodes[start], cond) - for label, end in ends.items(): - graph.add_edge( - cond, - end_nodes[end], - label if label != end else None, - conditional=True, - ) - if branch.then is not None: - graph.add_edge(start_nodes[end], end_nodes[branch.then]) - else: - for label, end in ends.items(): - graph.add_edge( - start_nodes[start], - end_nodes[end], - label if label != end else None, - conditional=True, - ) - if branch.then is not None: - graph.add_edge(start_nodes[end], end_nodes[branch.then]) + if branch.then is not None: + graph.add_edge(start_nodes[end], end_nodes[branch.then]) return graph diff --git a/tests/__snapshots__/test_pregel.ambr b/tests/__snapshots__/test_pregel.ambr index adc8c6368..41542464d 100644 --- a/tests/__snapshots__/test_pregel.ambr +++ b/tests/__snapshots__/test_pregel.ambr @@ -1,42 +1,5 @@ # serializer version: 1 # name: test_branch_then[end_of_run] - ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +---------+ - | prepare | - +---------+ - * - * - * - +-----------+ - | condition | - +-----------+ - .. .. - .. .. - .. .. - +---------------+ +---------------+ - | tool_two_slow | | tool_two_fast | - +---------------+ +---------------+ - ** ** - ** ** - ** ** - +--------+ - | finish | - +--------+ - * - * - * - +---------+ - | __end__ | - +---------+ - ''' -# --- -# name: test_branch_then[end_of_run].1 ''' +-----------+ | __start__ | @@ -67,31 +30,7 @@ +---------+ ''' # --- -# name: test_branch_then[end_of_run].2 - ''' - %%{init: {'flowchart': {'curve': 'linear'}}}%% - graph TD; - __start__[__start__]:::startclass; - __end__[__end__]:::endclass; - prepare([prepare]):::otherclass; - tool_two_slow([tool_two_slow]):::otherclass; - tool_two_fast([tool_two_fast]):::otherclass; - finish([finish]):::otherclass; - condition([condition]):::otherclass; - __start__ --> prepare; - finish --> __end__; - prepare --> condition; - condition -.-> tool_two_slow; - tool_two_slow --> finish; - condition -.-> tool_two_fast; - tool_two_fast --> finish; - classDef startclass fill:#ffdfba; - classDef endclass fill:#baffc9; - classDef otherclass fill:#fad7de; - - ''' -# --- -# name: test_branch_then[end_of_run].3 +# name: test_branch_then[end_of_run].1 ''' %%{init: {'flowchart': {'curve': 'linear'}}}%% graph TD; @@ -114,43 +53,6 @@ ''' # --- # name: test_branch_then[end_of_step] - ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +---------+ - | prepare | - +---------+ - * - * - * - +-----------+ - | condition | - +-----------+ - .. .. - .. .. - .. .. - +---------------+ +---------------+ - | tool_two_slow | | tool_two_fast | - +---------------+ +---------------+ - ** ** - ** ** - ** ** - +--------+ - | finish | - +--------+ - * - * - * - +---------+ - | __end__ | - +---------+ - ''' -# --- -# name: test_branch_then[end_of_step].1 ''' +-----------+ | __start__ | @@ -181,31 +83,7 @@ +---------+ ''' # --- -# name: test_branch_then[end_of_step].2 - ''' - %%{init: {'flowchart': {'curve': 'linear'}}}%% - graph TD; - __start__[__start__]:::startclass; - __end__[__end__]:::endclass; - prepare([prepare]):::otherclass; - tool_two_slow([tool_two_slow]):::otherclass; - tool_two_fast([tool_two_fast]):::otherclass; - finish([finish]):::otherclass; - condition([condition]):::otherclass; - __start__ --> prepare; - finish --> __end__; - prepare --> condition; - condition -.-> tool_two_slow; - tool_two_slow --> finish; - condition -.-> tool_two_fast; - tool_two_fast --> finish; - classDef startclass fill:#ffdfba; - classDef endclass fill:#baffc9; - classDef otherclass fill:#fad7de; - - ''' -# --- -# name: test_branch_then[end_of_step].3 +# name: test_branch_then[end_of_step].1 ''' %%{init: {'flowchart': {'curve': 'linear'}}}%% graph TD; @@ -270,30 +148,6 @@ ], "name": "right" } - }, - { - "id": "should_start", - "type": "runnable", - "data": { - "id": [ - "langgraph", - "utils", - "RunnableCallable" - ], - "name": "should_start" - } - }, - { - "id": "condition", - "type": "runnable", - "data": { - "id": [ - "langgraph", - "utils", - "RunnableCallable" - ], - "name": "RunnableCallable" - } } ], "edges": [ @@ -303,26 +157,18 @@ }, { "source": "__start__", - "target": "should_start" - }, - { - "source": "should_start", "target": "left", "data": "go-left", "conditional": true }, { - "source": "should_start", + "source": "__start__", "target": "right", "data": "go-right", "conditional": true }, { "source": "left", - "target": "condition" - }, - { - "source": "condition", "target": "__end__", "conditional": true } @@ -332,33 +178,21 @@ # --- # name: test_conditional_entrypoint_graph.3 ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +--------------+ - | should_start | - +--------------+ - . .. - .. . - . .. - +------+ . - | left | . - +------+ . - * . - * . - * . - +-----------+ +-------+ - | condition | | right | - +-----------+ +-------+ - . * - .. ** - . * - +---------+ - | __end__ | - +---------+ + +-----------+ + | __start__ | + +-----------+ + .. . + . .. + . . + +-------+ +------+ + | right | | left | + +-------+ +------+ + ** . + * .. + * . + +---------+ + | __end__ | + +---------+ ''' # --- # name: test_conditional_entrypoint_graph_state @@ -404,30 +238,6 @@ ], "name": "right" } - }, - { - "id": "should_start", - "type": "runnable", - "data": { - "id": [ - "langgraph", - "utils", - "RunnableCallable" - ], - "name": "should_start" - } - }, - { - "id": "condition", - "type": "runnable", - "data": { - "id": [ - "langgraph", - "utils", - "RunnableCallable" - ], - "name": "RunnableCallable" - } } ], "edges": [ @@ -437,26 +247,18 @@ }, { "source": "__start__", - "target": "should_start" - }, - { - "source": "should_start", "target": "left", "data": "go-left", "conditional": true }, { - "source": "should_start", + "source": "__start__", "target": "right", "data": "go-right", "conditional": true }, { "source": "left", - "target": "condition" - }, - { - "source": "condition", "target": "__end__", "conditional": true } @@ -466,33 +268,21 @@ # --- # name: test_conditional_entrypoint_graph_state.3 ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +--------------+ - | should_start | - +--------------+ - . .. - .. . - . .. - +------+ . - | left | . - +------+ . - * . - * . - * . - +-----------+ +-------+ - | condition | | right | - +-----------+ +-------+ - . * - .. ** - . * - +---------+ - | __end__ | - +---------+ + +-----------+ + | __start__ | + +-----------+ + .. . + . .. + . . + +-------+ +------+ + | right | | left | + +-------+ +------+ + ** . + * .. + * . + +---------+ + | __end__ | + +---------+ ''' # --- # name: test_conditional_graph[end_of_run] @@ -533,18 +323,6 @@ ], "name": "tools" } - }, - { - "id": "should_continue", - "type": "runnable", - "data": { - "id": [ - "langgraph", - "utils", - "RunnableCallable" - ], - "name": "should_continue" - } } ], "edges": [ @@ -558,16 +336,12 @@ }, { "source": "agent", - "target": "should_continue" - }, - { - "source": "should_continue", "target": "tools", "data": "continue", "conditional": true }, { - "source": "should_continue", + "source": "agent", "target": "__end__", "data": "exit", "conditional": true @@ -578,27 +352,21 @@ # --- # name: test_conditional_graph[end_of_run].1 ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +-------+ - | agent | - +-------+* - *** *** - * * - ** *** - +-----------------+ * - | should_continue | * - +-----------------+. * - . ..... * - . ... * - . ... * - +---------+ +-------+ - | __end__ | | tools | - +---------+ +-------+ + +-----------+ + | __start__ | + +-----------+ + * + * + * + +-------+ + | agent | + +-------+ + * . + ** .. + * . + +-------+ +---------+ + | tools | | __end__ | + +-------+ +---------+ ''' # --- # name: test_conditional_graph[end_of_run].2 @@ -698,18 +466,6 @@ ], "name": "tools" } - }, - { - "id": "should_continue", - "type": "runnable", - "data": { - "id": [ - "langgraph", - "utils", - "RunnableCallable" - ], - "name": "should_continue" - } } ], "edges": [ @@ -747,16 +503,12 @@ }, { "source": 3, - "target": "should_continue" - }, - { - "source": "should_continue", "target": "tools", "data": "continue", "conditional": true }, { - "source": "should_continue", + "source": 3, "target": "__end__", "data": "exit", "conditional": true @@ -767,51 +519,45 @@ # --- # name: test_conditional_graph[end_of_run].4 ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +------------------------------+ - | ParallelInput | - +------------------------------+ - ***** * ***** - ****** * **** - *** * ***** - +----------------+ * *** - | PromptTemplate | * * - +----------------+ * * - * * * - * * * - * * * - +----------------------+ * * - | FakeStreamingListLLM | * * - +----------------------+ * * - * * * - * * * - * * * - +----------------------+ +-------------+ * - | Lambda(agent_parser) | | Passthrough | * - +----------------------+ +-------------+ * - *** *** * - ** ** * - ** ** * - +-------------------------------+ * - | ParallelOutput | * - +-------------------------------+ * - * * - * * - * * - +-----------------+ * - | should_continue |... * - +-----------------+ ......... * - . ........ * - . ......... * - . ..... * - +---------+ +-------+ - | __end__ | | tools | - +---------+ +-------+ + +-----------+ + | __start__ | + +-----------+ + * + * + * + +------------------------------+ + | ParallelInput | + +------------------------------+ + ***** * ***** + ****** * **** + *** * ***** + +----------------+ * *** + | PromptTemplate | * * + +----------------+ * * + * * * + * * * + * * * + +----------------------+ * * + | FakeStreamingListLLM | * * + +----------------------+ * * + * * * + * * * + * * * + +----------------------+ +-------------+ * + | Lambda(agent_parser) | | Passthrough | * + +----------------------+ +-------------+ * + *** *** * + ** ** * + ** ** * + +-------------------------------+ * + | ParallelOutput | * + +-------------------------------+..... * + . ......... * + . ........ * + . ..... * + +---------+ +-------+ + | __end__ | | tools | + +---------+ +-------+ ''' # --- # name: test_conditional_graph[end_of_run].5 @@ -868,18 +614,6 @@ ], "name": "tools" } - }, - { - "id": "should_continue", - "type": "runnable", - "data": { - "id": [ - "langgraph", - "utils", - "RunnableCallable" - ], - "name": "should_continue" - } } ], "edges": [ @@ -893,16 +627,12 @@ }, { "source": "agent", - "target": "should_continue" - }, - { - "source": "should_continue", "target": "tools", "data": "continue", "conditional": true }, { - "source": "should_continue", + "source": "agent", "target": "__end__", "data": "exit", "conditional": true @@ -913,27 +643,21 @@ # --- # name: test_conditional_graph[end_of_step].1 ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +-------+ - | agent | - +-------+* - *** *** - * * - ** *** - +-----------------+ * - | should_continue | * - +-----------------+. * - . ..... * - . ... * - . ... * - +---------+ +-------+ - | __end__ | | tools | - +---------+ +-------+ + +-----------+ + | __start__ | + +-----------+ + * + * + * + +-------+ + | agent | + +-------+ + * . + ** .. + * . + +-------+ +---------+ + | tools | | __end__ | + +-------+ +---------+ ''' # --- # name: test_conditional_graph[end_of_step].2 @@ -1033,18 +757,6 @@ ], "name": "tools" } - }, - { - "id": "should_continue", - "type": "runnable", - "data": { - "id": [ - "langgraph", - "utils", - "RunnableCallable" - ], - "name": "should_continue" - } } ], "edges": [ @@ -1082,16 +794,12 @@ }, { "source": 3, - "target": "should_continue" - }, - { - "source": "should_continue", "target": "tools", "data": "continue", "conditional": true }, { - "source": "should_continue", + "source": 3, "target": "__end__", "data": "exit", "conditional": true @@ -1102,51 +810,45 @@ # --- # name: test_conditional_graph[end_of_step].4 ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +------------------------------+ - | ParallelInput | - +------------------------------+ - ***** * ***** - ****** * **** - *** * ***** - +----------------+ * *** - | PromptTemplate | * * - +----------------+ * * - * * * - * * * - * * * - +----------------------+ * * - | FakeStreamingListLLM | * * - +----------------------+ * * - * * * - * * * - * * * - +----------------------+ +-------------+ * - | Lambda(agent_parser) | | Passthrough | * - +----------------------+ +-------------+ * - *** *** * - ** ** * - ** ** * - +-------------------------------+ * - | ParallelOutput | * - +-------------------------------+ * - * * - * * - * * - +-----------------+ * - | should_continue |... * - +-----------------+ ......... * - . ........ * - . ......... * - . ..... * - +---------+ +-------+ - | __end__ | | tools | - +---------+ +-------+ + +-----------+ + | __start__ | + +-----------+ + * + * + * + +------------------------------+ + | ParallelInput | + +------------------------------+ + ***** * ***** + ****** * **** + *** * ***** + +----------------+ * *** + | PromptTemplate | * * + +----------------+ * * + * * * + * * * + * * * + +----------------------+ * * + | FakeStreamingListLLM | * * + +----------------------+ * * + * * * + * * * + * * * + +----------------------+ +-------------+ * + | Lambda(agent_parser) | | Passthrough | * + +----------------------+ +-------------+ * + *** *** * + ** ** * + ** ** * + +-------------------------------+ * + | ParallelOutput | * + +-------------------------------+..... * + . ......... * + . ........ * + . ..... * + +---------+ +-------+ + | __end__ | | tools | + +---------+ +-------+ ''' # --- # name: test_conditional_graph[end_of_step].5 @@ -1209,18 +911,6 @@ ], "name": "tools" } - }, - { - "id": "should_continue", - "type": "runnable", - "data": { - "id": [ - "langgraph", - "utils", - "RunnableCallable" - ], - "name": "should_continue" - } } ], "edges": [ @@ -1234,16 +924,12 @@ }, { "source": "agent", - "target": "should_continue" - }, - { - "source": "should_continue", "target": "tools", "data": "continue", "conditional": true }, { - "source": "should_continue", + "source": "agent", "target": "__end__", "data": "exit", "conditional": true @@ -1254,27 +940,21 @@ # --- # name: test_conditional_graph_state[end_of_run].3 ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +-------+ - | agent | - +-------+* - *** *** - * * - ** *** - +-----------------+ * - | should_continue | * - +-----------------+. * - . ..... * - . ... * - . ... * - +---------+ +-------+ - | __end__ | | tools | - +---------+ +-------+ + +-----------+ + | __start__ | + +-----------+ + * + * + * + +-------+ + | agent | + +-------+ + . . + .. .. + . . + +-------+ +---------+ + | tools | | __end__ | + +-------+ +---------+ ''' # --- # name: test_conditional_graph_state[end_of_step] @@ -1321,18 +1001,6 @@ ], "name": "tools" } - }, - { - "id": "should_continue", - "type": "runnable", - "data": { - "id": [ - "langgraph", - "utils", - "RunnableCallable" - ], - "name": "should_continue" - } } ], "edges": [ @@ -1346,16 +1014,12 @@ }, { "source": "agent", - "target": "should_continue" - }, - { - "source": "should_continue", "target": "tools", "data": "continue", "conditional": true }, { - "source": "should_continue", + "source": "agent", "target": "__end__", "data": "exit", "conditional": true @@ -1366,27 +1030,21 @@ # --- # name: test_conditional_graph_state[end_of_step].3 ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +-------+ - | agent | - +-------+* - *** *** - * * - ** *** - +-----------------+ * - | should_continue | * - +-----------------+. * - . ..... * - . ... * - . ... * - +---------+ +-------+ - | __end__ | | tools | - +---------+ +-------+ + +-----------+ + | __start__ | + +-----------+ + * + * + * + +-------+ + | agent | + +-------+ + * . + ** .. + * . + +-------+ +---------+ + | tools | | __end__ | + +-------+ +---------+ ''' # --- # name: test_in_one_fan_out_state_graph_waiting_edge[end_of_run] @@ -1474,12 +1132,12 @@ +---------------+ | rewrite_query | +---------------+ - *** *** - * * - ** ** - +--------------+ +---------+ - | analyzer_one | | decider | - +--------------+ +---------+ + *** ... + * . + ** ... + +--------------+ . + | analyzer_one | . + +--------------+ . * . * . * . @@ -1511,12 +1169,12 @@ +---------------+ | rewrite_query | +---------------+ - *** *** - * * - ** ** - +--------------+ +---------+ - | analyzer_one | | decider | - +--------------+ +---------+ + *** ... + * . + ** ... + +--------------+ . + | analyzer_one | . + +--------------+ . * . * . * . @@ -1539,76 +1197,76 @@ # --- # name: test_in_one_fan_out_state_graph_waiting_edge_via_branch[end_of_run] ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +---------------+ - | rewrite_query | - +---------------+ - *** *** - * * - ** ** - +--------------+ +--------------------+ - | analyzer_one | | rewrite_query_then | - +--------------+ +--------------------+ - * . - * . - * . - +---------------+ +---------------+ - | retriever_one | | retriever_two | - +---------------+ +---------------+ - *** *** - * * - ** ** - +----+ - | qa | - +----+ - * - * - * - +---------+ - | __end__ | - +---------+ + +-----------+ + | __start__ | + +-----------+ + * + * + * + +---------------+ + | rewrite_query | + +---------------+ + *** ... + * . + ** ... + +--------------+ . + | analyzer_one | . + +--------------+ . + * . + * . + * . + +---------------+ +---------------+ + | retriever_one | | retriever_two | + +---------------+ +---------------+ + *** *** + * * + ** ** + +----+ + | qa | + +----+ + * + * + * + +---------+ + | __end__ | + +---------+ ''' # --- # name: test_in_one_fan_out_state_graph_waiting_edge_via_branch[end_of_step] ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +---------------+ - | rewrite_query | - +---------------+ - *** *** - * * - ** ** - +--------------+ +--------------------+ - | analyzer_one | | rewrite_query_then | - +--------------+ +--------------------+ - * . - * . - * . - +---------------+ +---------------+ - | retriever_one | | retriever_two | - +---------------+ +---------------+ - *** *** - * * - ** ** - +----+ - | qa | - +----+ - * - * - * - +---------+ - | __end__ | - +---------+ + +-----------+ + | __start__ | + +-----------+ + * + * + * + +---------------+ + | rewrite_query | + +---------------+ + *** ... + * . + ** ... + +--------------+ . + | analyzer_one | . + +--------------+ . + * . + * . + * . + +---------------+ +---------------+ + | retriever_one | | retriever_two | + +---------------+ +---------------+ + *** *** + * * + ** ** + +----+ + | qa | + +----+ + * + * + * + +---------+ + | __end__ | + +---------+ ''' # --- # name: test_message_graph[end_of_run] @@ -1655,18 +1313,6 @@ ], "name": "tools" } - }, - { - "id": "should_continue", - "type": "runnable", - "data": { - "id": [ - "langgraph", - "utils", - "RunnableCallable" - ], - "name": "should_continue" - } } ], "edges": [ @@ -1680,16 +1326,12 @@ }, { "source": "agent", - "target": "should_continue" - }, - { - "source": "should_continue", "target": "action", "data": "continue", "conditional": true }, { - "source": "should_continue", + "source": "agent", "target": "__end__", "data": "end", "conditional": true @@ -1700,27 +1342,21 @@ # --- # name: test_message_graph[end_of_run].3 ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +-------+ - | agent | - +-------+* - *** *** - * * - ** *** - +-----------------+ * - | should_continue | * - +-----------------+. * - . ..... * - . ... * - . ... * - +---------+ +--------+ - | __end__ | | action | - +---------+ +--------+ + +-----------+ + | __start__ | + +-----------+ + * + * + * + +-------+ + | agent | + +-------+ + * .. + ** .. + * . + +--------+ +---------+ + | action | | __end__ | + +--------+ +---------+ ''' # --- # name: test_message_graph[end_of_step] @@ -1767,18 +1403,6 @@ ], "name": "tools" } - }, - { - "id": "should_continue", - "type": "runnable", - "data": { - "id": [ - "langgraph", - "utils", - "RunnableCallable" - ], - "name": "should_continue" - } } ], "edges": [ @@ -1792,16 +1416,12 @@ }, { "source": "agent", - "target": "should_continue" - }, - { - "source": "should_continue", "target": "action", "data": "continue", "conditional": true }, { - "source": "should_continue", + "source": "agent", "target": "__end__", "data": "end", "conditional": true @@ -1812,27 +1432,21 @@ # --- # name: test_message_graph[end_of_step].3 ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +-------+ - | agent | - +-------+* - *** *** - * * - ** *** - +-----------------+ * - | should_continue | * - +-----------------+. * - . ..... * - . ... * - . ... * - +---------+ +--------+ - | __end__ | | action | - +---------+ +--------+ + +-----------+ + | __start__ | + +-----------+ + * + * + * + +-------+ + | agent | + +-------+ + * .. + ** .. + * . + +--------+ +---------+ + | action | | __end__ | + +--------+ +---------+ ''' # --- # name: test_nested_graph @@ -1882,7 +1496,7 @@ 'edges': list([ dict({ 'conditional': True, - 'source': 'tool_two:condition', + 'source': 'tool_two:__start__', 'target': 'tool_two:tool_two_slow', }), dict({ @@ -1891,20 +1505,16 @@ }), dict({ 'conditional': True, - 'source': 'tool_two:condition', + 'source': 'tool_two:__start__', 'target': 'tool_two:tool_two_fast', }), dict({ 'source': 'tool_two:tool_two_fast', 'target': 'tool_two:__end__', }), - dict({ - 'source': '__start__', - 'target': 'condition', - }), dict({ 'conditional': True, - 'source': 'condition', + 'source': '__start__', 'target': 'tool_one', }), dict({ @@ -1913,8 +1523,8 @@ }), dict({ 'conditional': True, - 'source': 'condition', - 'target': 'tool_two:condition', + 'source': '__start__', + 'target': 'tool_two:__start__', }), dict({ 'source': 'tool_two:__end__', @@ -1922,7 +1532,7 @@ }), dict({ 'conditional': True, - 'source': 'condition', + 'source': '__start__', 'target': 'tool_three', }), dict({ @@ -1953,6 +1563,11 @@ 'id': 'tool_one', 'type': 'runnable', }), + dict({ + 'data': 'tool_two:__start__', + 'id': 'tool_two:__start__', + 'type': 'schema', + }), dict({ 'data': 'tool_two:__end__', 'id': 'tool_two:__end__', @@ -1982,18 +1597,6 @@ 'id': 'tool_two:tool_two_fast', 'type': 'runnable', }), - dict({ - 'data': dict({ - 'id': list([ - 'langgraph', - 'utils', - 'RunnableCallable', - ]), - 'name': 'RunnableCallable', - }), - 'id': 'tool_two:condition', - 'type': 'runnable', - }), dict({ 'data': dict({ 'id': list([ @@ -2006,18 +1609,6 @@ 'id': 'tool_three', 'type': 'runnable', }), - dict({ - 'data': dict({ - 'id': list([ - 'langgraph', - 'utils', - 'RunnableCallable', - ]), - 'name': 'RunnableCallable', - }), - 'id': 'condition', - 'type': 'runnable', - }), ]), }) # --- @@ -2025,12 +1616,6 @@ ''' +-----------+ | __start__ | - +-----------+ - * - * - * - +-----------+ - | condition | ..+-----------+... ..... . ..... ... . ... @@ -2047,38 +1632,6 @@ ''' # --- # name: test_nested_graph_xray.2 - ''' - %%{init: {'flowchart': {'curve': 'linear'}}}%% - graph TD; - __start__[__start__]:::startclass; - __end__[__end__]:::endclass; - tool_one([tool_one]):::otherclass; - tool_two___end__([__end__]):::otherclass; - tool_two_tool_two_slow([tool_two_slow]):::otherclass; - tool_two_tool_two_fast([tool_two_fast]):::otherclass; - tool_two_condition([condition]):::otherclass; - tool_three([tool_three]):::otherclass; - condition([condition]):::otherclass; - subgraph tool_two - tool_two_condition -.-> tool_two_tool_two_slow; - tool_two_tool_two_slow --> tool_two___end__; - tool_two_condition -.-> tool_two_tool_two_fast; - tool_two_tool_two_fast --> tool_two___end__; - end - __start__ --> condition; - condition -.-> tool_one; - tool_one --> __end__; - condition -.-> tool_two_condition; - tool_two___end__ --> __end__; - condition -.-> tool_three; - tool_three --> __end__; - classDef startclass fill:#ffdfba; - classDef endclass fill:#baffc9; - classDef otherclass fill:#fad7de; - - ''' -# --- -# name: test_nested_graph_xray.3 ''' %%{init: {'flowchart': {'curve': 'linear'}}}%% graph TD; @@ -2153,18 +1706,6 @@ ], "name": "call_tool" } - }, - { - "id": "should_continue", - "type": "runnable", - "data": { - "id": [ - "langgraph", - "utils", - "RunnableCallable" - ], - "name": "should_continue" - } } ], "edges": [ @@ -2178,16 +1719,12 @@ }, { "source": "agent", - "target": "should_continue" - }, - { - "source": "should_continue", "target": "action", "data": "continue", "conditional": true }, { - "source": "should_continue", + "source": "agent", "target": "__end__", "data": "end", "conditional": true @@ -2198,27 +1735,21 @@ # --- # name: test_prebuilt_chat.3 ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +-------+ - | agent | - +-------+* - *** *** - * * - ** *** - +-----------------+ * - | should_continue | * - +-----------------+. * - . ..... * - . ... * - . ... * - +---------+ +--------+ - | __end__ | | action | - +---------+ +--------+ + +-----------+ + | __start__ | + +-----------+ + * + * + * + +-------+ + | agent | + +-------+ + * .. + ** .. + * . + +--------+ +---------+ + | action | | __end__ | + +--------+ +---------+ ''' # --- # name: test_prebuilt_tool_chat @@ -2266,18 +1797,6 @@ ], "name": "tools" } - }, - { - "id": "should_continue", - "type": "runnable", - "data": { - "id": [ - "langgraph", - "utils", - "RunnableCallable" - ], - "name": "should_continue" - } } ], "edges": [ @@ -2291,16 +1810,12 @@ }, { "source": "agent", - "target": "should_continue" - }, - { - "source": "should_continue", "target": "action", "data": "continue", "conditional": true }, { - "source": "should_continue", + "source": "agent", "target": "__end__", "data": "end", "conditional": true @@ -2311,44 +1826,35 @@ # --- # name: test_prebuilt_tool_chat.3 ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +-------+ - | agent | - +-------+* - *** *** - * * - ** *** - +-----------------+ * - | should_continue | * - +-----------------+. * - . ..... * - . ... * - . ... * - +---------+ +--------+ - | __end__ | | action | - +---------+ +--------+ + +-----------+ + | __start__ | + +-----------+ + * + * + * + +-------+ + | agent | + +-------+ + . .. + .. .. + . . + +--------+ +---------+ + | action | | __end__ | + +--------+ +---------+ ''' # --- # name: test_repeat_condition ''' graph TD; __start__ --> Researcher; - Researcher --> Researcher_router; - Researcher_router -. continue .-> Chart_Generator; - Researcher_router -. call_tool .-> Call_Tool; - Researcher_router -. end .-> __end__; - Chart_Generator --> Chart_Generator_router; - Chart_Generator_router -. continue .-> Researcher; - Chart_Generator_router -. call_tool .-> Call_Tool; - Chart_Generator_router -. end .-> __end__; - Call_Tool --> condition; - condition -.-> Researcher; - condition -.-> Chart_Generator; + Researcher -. continue .-> Chart_Generator; + Researcher -. call_tool .-> Call_Tool; + Researcher -. end .-> __end__; + Chart_Generator -. continue .-> Researcher; + Chart_Generator -. call_tool .-> Call_Tool; + Chart_Generator -. end .-> __end__; + Call_Tool -.-> Researcher; + Call_Tool -.-> Chart_Generator; ''' # --- @@ -2387,12 +1893,6 @@ ''' +-----------+ | __start__ | - +-----------+ - * - * - * - +-----------+ - | condition | +-----------+ .. .. .. .. @@ -2409,45 +1909,6 @@ ''' # --- # name: test_start_branch_then[end_of_run].1 - ''' - +-----------+ - | __start__ | - +-----------+ - .. .. - .. .. - .. .. - +---------------+ +---------------+ - | tool_two_slow | | tool_two_fast | - +---------------+ +---------------+ - ** ** - ** ** - ** ** - +---------+ - | __end__ | - +---------+ - ''' -# --- -# name: test_start_branch_then[end_of_run].2 - ''' - %%{init: {'flowchart': {'curve': 'linear'}}}%% - graph TD; - __start__[__start__]:::startclass; - __end__[__end__]:::endclass; - tool_two_slow([tool_two_slow]):::otherclass; - tool_two_fast([tool_two_fast]):::otherclass; - condition([condition]):::otherclass; - __start__ --> condition; - condition -.-> tool_two_slow; - tool_two_slow --> __end__; - condition -.-> tool_two_fast; - tool_two_fast --> __end__; - classDef startclass fill:#ffdfba; - classDef endclass fill:#baffc9; - classDef otherclass fill:#fad7de; - - ''' -# --- -# name: test_start_branch_then[end_of_run].3 ''' %%{init: {'flowchart': {'curve': 'linear'}}}%% graph TD; @@ -2469,12 +1930,6 @@ ''' +-----------+ | __start__ | - +-----------+ - * - * - * - +-----------+ - | condition | +-----------+ .. .. .. .. @@ -2491,45 +1946,6 @@ ''' # --- # name: test_start_branch_then[end_of_step].1 - ''' - +-----------+ - | __start__ | - +-----------+ - .. .. - .. .. - .. .. - +---------------+ +---------------+ - | tool_two_slow | | tool_two_fast | - +---------------+ +---------------+ - ** ** - ** ** - ** ** - +---------+ - | __end__ | - +---------+ - ''' -# --- -# name: test_start_branch_then[end_of_step].2 - ''' - %%{init: {'flowchart': {'curve': 'linear'}}}%% - graph TD; - __start__[__start__]:::startclass; - __end__[__end__]:::endclass; - tool_two_slow([tool_two_slow]):::otherclass; - tool_two_fast([tool_two_fast]):::otherclass; - condition([condition]):::otherclass; - __start__ --> condition; - condition -.-> tool_two_slow; - tool_two_slow --> __end__; - condition -.-> tool_two_fast; - tool_two_fast --> __end__; - classDef startclass fill:#ffdfba; - classDef endclass fill:#baffc9; - classDef otherclass fill:#fad7de; - - ''' -# --- -# name: test_start_branch_then[end_of_step].3 ''' %%{init: {'flowchart': {'curve': 'linear'}}}%% graph TD; diff --git a/tests/__snapshots__/test_pregel_async.ambr b/tests/__snapshots__/test_pregel_async.ambr index 709726945..2d68a82a5 100644 --- a/tests/__snapshots__/test_pregel_async.ambr +++ b/tests/__snapshots__/test_pregel_async.ambr @@ -10,12 +10,12 @@ +---------------+ | rewrite_query | +---------------+ - *** *** - * * - ** ** - +--------------+ +---------+ - | analyzer_one | | decider | - +--------------+ +---------+ + *** ... + * . + ** ... + +--------------+ . + | analyzer_one | . + +--------------+ . * . * . * . @@ -47,12 +47,12 @@ +---------------+ | rewrite_query | +---------------+ - *** *** - * * - ** ** - +--------------+ +---------+ - | analyzer_one | | decider | - +--------------+ +---------+ + *** ... + * . + ** ... + +--------------+ . + | analyzer_one | . + +--------------+ . * . * . * . @@ -84,12 +84,12 @@ +---------------+ | rewrite_query | +---------------+ - *** *** - * * - ** ** - +--------------+ +-----------+ - | analyzer_one | | condition | - +--------------+ +-----------+ + *** ... + * . + ** ... + +--------------+ . + | analyzer_one | . + +--------------+ . * . * . * . @@ -121,12 +121,12 @@ +---------------+ | rewrite_query | +---------------+ - *** *** - * * - ** ** - +--------------+ +-----------+ - | analyzer_one | | condition | - +--------------+ +-----------+ + *** ... + * . + ** ... + +--------------+ . + | analyzer_one | . + +--------------+ . * . * . * . diff --git a/tests/test_pregel.py b/tests/test_pregel.py index 8e8222697..900683242 100644 --- a/tests/test_pregel.py +++ b/tests/test_pregel.py @@ -883,18 +883,10 @@ def test_conditional_graph( assert json.dumps(app.get_graph().to_json(), indent=2) == snapshot assert app.get_graph().draw_ascii() == snapshot - assert ( - app.get_graph(add_condition_nodes=False).draw_mermaid(with_styles=False) - == snapshot - ) + assert app.get_graph().draw_mermaid(with_styles=False) == snapshot assert json.dumps(app.get_graph(xray=True).to_json(), indent=2) == snapshot assert app.get_graph(xray=True).draw_ascii() == snapshot - assert ( - app.get_graph(xray=True, add_condition_nodes=False).draw_mermaid( - with_styles=False - ) - == snapshot - ) + assert app.get_graph(xray=True).draw_mermaid(with_styles=False) == snapshot assert app.invoke({"input": "what is weather in sf"}) == { "input": "what is weather in sf", @@ -3032,9 +3024,7 @@ def test_start_branch_then( ) tool_two = tool_two_graph.compile() assert tool_two.get_graph().draw_ascii() == snapshot - assert tool_two.get_graph(add_condition_nodes=False).draw_ascii() == snapshot assert tool_two.get_graph().draw_mermaid() == snapshot - assert tool_two.get_graph(add_condition_nodes=False).draw_mermaid() == snapshot assert tool_two.invoke({"my_key": "value", "market": "DE"}) == { "my_key": "value slow", @@ -3138,9 +3128,7 @@ def test_branch_then(snapshot: SnapshotAssertion, checkpoint_at: CheckpointAt) - tool_two_graph.add_node("finish", lambda s: {"my_key": " finished"}) tool_two = tool_two_graph.compile() assert tool_two.get_graph().draw_ascii() == snapshot - assert tool_two.get_graph(add_condition_nodes=False).draw_ascii() == snapshot assert tool_two.get_graph().draw_mermaid() == snapshot - assert tool_two.get_graph(add_condition_nodes=False).draw_mermaid() == snapshot assert tool_two.invoke({"my_key": "value", "market": "DE"}, debug=1) == { "my_key": "value prepared slow finished", @@ -3877,9 +3865,6 @@ def test_nested_graph_xray(snapshot: SnapshotAssertion) -> None: assert app.get_graph(xray=True).to_json() == snapshot assert app.get_graph().draw_ascii() == snapshot assert app.get_graph(xray=True).draw_mermaid() == snapshot - assert ( - app.get_graph(xray=True, add_condition_nodes=False).draw_mermaid() == snapshot - ) def test_nested_graph(snapshot: SnapshotAssertion) -> None: From 516a74351d5c34bbd210630733c3e69002fca131 Mon Sep 17 00:00:00 2001 From: Nuno Campos Date: Mon, 29 Apr 2024 17:41:03 -0700 Subject: [PATCH 5/7] Support non str ids in sqlite checkpointers --- langgraph/checkpoint/aiosqlite.py | 10 +++++----- langgraph/checkpoint/sqlite.py | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/langgraph/checkpoint/aiosqlite.py b/langgraph/checkpoint/aiosqlite.py index 8d9d3fbc0..7d3101bf2 100644 --- a/langgraph/checkpoint/aiosqlite.py +++ b/langgraph/checkpoint/aiosqlite.py @@ -75,8 +75,8 @@ class AsyncSqliteSaver(BaseCheckpointSaver, AbstractAsyncContextManager): async with self.conn.execute( "SELECT checkpoint, parent_ts FROM checkpoints WHERE thread_id = ? AND thread_ts = ?", ( - config["configurable"]["thread_id"], - config["configurable"]["thread_ts"], + str(config["configurable"]["thread_id"]), + str(config["configurable"]["thread_ts"]), ), ) as cursor: if value := await cursor.fetchone(): @@ -95,7 +95,7 @@ class AsyncSqliteSaver(BaseCheckpointSaver, AbstractAsyncContextManager): else: async with self.conn.execute( "SELECT thread_id, thread_ts, parent_ts, checkpoint FROM checkpoints WHERE thread_id = ? ORDER BY thread_ts DESC LIMIT 1", - (config["configurable"]["thread_id"],), + (str(config["configurable"]["thread_id"]),), ) as cursor: if value := await cursor.fetchone(): return CheckpointTuple( @@ -120,7 +120,7 @@ class AsyncSqliteSaver(BaseCheckpointSaver, AbstractAsyncContextManager): await self.setup() async with self.conn.execute( "SELECT thread_id, thread_ts, parent_ts, checkpoint FROM checkpoints WHERE thread_id = ? ORDER BY thread_ts DESC", - (config["configurable"]["thread_id"],), + (str(config["configurable"]["thread_id"]),), ) as cursor: async for thread_id, thread_ts, parent_ts, value in cursor: yield CheckpointTuple( @@ -138,7 +138,7 @@ class AsyncSqliteSaver(BaseCheckpointSaver, AbstractAsyncContextManager): async with self.conn.execute( "INSERT OR REPLACE INTO checkpoints (thread_id, thread_ts, parent_ts, checkpoint) VALUES (?, ?, ?, ?)", ( - config["configurable"]["thread_id"], + str(config["configurable"]["thread_id"]), checkpoint["ts"], config["configurable"].get("thread_ts"), self.serde.dumps(checkpoint), diff --git a/langgraph/checkpoint/sqlite.py b/langgraph/checkpoint/sqlite.py index 0378b80c6..578c42d8d 100644 --- a/langgraph/checkpoint/sqlite.py +++ b/langgraph/checkpoint/sqlite.py @@ -93,8 +93,8 @@ class SqliteSaver(BaseCheckpointSaver, AbstractContextManager): cur.execute( "SELECT checkpoint, parent_ts FROM checkpoints WHERE thread_id = ? AND thread_ts = ?", ( - config["configurable"]["thread_id"], - config["configurable"]["thread_ts"], + str(config["configurable"]["thread_id"]), + str(config["configurable"]["thread_ts"]), ), ) if value := cur.fetchone(): @@ -113,7 +113,7 @@ class SqliteSaver(BaseCheckpointSaver, AbstractContextManager): else: cur.execute( "SELECT thread_id, thread_ts, parent_ts, checkpoint FROM checkpoints WHERE thread_id = ? ORDER BY thread_ts DESC LIMIT 1", - (config["configurable"]["thread_id"],), + (str(config["configurable"]["thread_id"]),), ) if value := cur.fetchone(): return CheckpointTuple( @@ -138,7 +138,7 @@ class SqliteSaver(BaseCheckpointSaver, AbstractContextManager): with self.cursor(transaction=False) as cur: cur.execute( "SELECT thread_id, thread_ts, parent_ts, checkpoint FROM checkpoints WHERE thread_id = ? ORDER BY thread_ts DESC", - (config["configurable"]["thread_id"],), + (str(config["configurable"]["thread_id"]),), ) for thread_id, thread_ts, parent_ts, value in cur: yield CheckpointTuple( @@ -159,7 +159,7 @@ class SqliteSaver(BaseCheckpointSaver, AbstractContextManager): cur.execute( "INSERT OR REPLACE INTO checkpoints (thread_id, thread_ts, parent_ts, checkpoint) VALUES (?, ?, ?, ?)", ( - config["configurable"]["thread_id"], + str(config["configurable"]["thread_id"]), checkpoint["ts"], config["configurable"].get("thread_ts"), self.serde.dumps(checkpoint), From ca2c93e0a1845affb31b6101fdffe1988ad9c927 Mon Sep 17 00:00:00 2001 From: Nuno Campos Date: Mon, 29 Apr 2024 17:41:36 -0700 Subject: [PATCH 6/7] Lint --- langgraph/graph/graph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/langgraph/graph/graph.py b/langgraph/graph/graph.py index 23c86d83a..b6b6fc9e5 100644 --- a/langgraph/graph/graph.py +++ b/langgraph/graph/graph.py @@ -1,5 +1,5 @@ import logging -from collections import Counter, defaultdict +from collections import defaultdict from typing import ( Any, Awaitable, From 7554309abfd4577b82f79280c0c5bd405583ba90 Mon Sep 17 00:00:00 2001 From: Nuno Campos Date: Mon, 29 Apr 2024 17:46:42 -0700 Subject: [PATCH 7/7] Update tests --- examples/time-travel.ipynb | 6 +- tests/__snapshots__/test_pregel.ambr | 739 +++++---------------------- tests/test_pregel.py | 28 +- 3 files changed, 149 insertions(+), 624 deletions(-) diff --git a/examples/time-travel.ipynb b/examples/time-travel.ipynb index 337428bcb..fed24cd44 100644 --- a/examples/time-travel.ipynb +++ b/examples/time-travel.ipynb @@ -331,18 +331,18 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "id": "c9ab60eb-679b-4eef-9e64-5ffbf3dffc70", "metadata": {}, "outputs": [ { "data": { - "image/png": "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", + "image/png": "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", "text/plain": [ "" ] }, - "execution_count": 9, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } diff --git a/tests/__snapshots__/test_pregel.ambr b/tests/__snapshots__/test_pregel.ambr index 41542464d..19fd322df 100644 --- a/tests/__snapshots__/test_pregel.ambr +++ b/tests/__snapshots__/test_pregel.ambr @@ -1,33 +1,14 @@ # serializer version: 1 # name: test_branch_then[end_of_run] ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +---------+ - | prepare | - +---------+. - .. .. - .. .. - .. .. - +---------------+ +---------------+ - | tool_two_slow | | tool_two_fast | - +---------------+ +---------------+ - ** ** - ** ** - ** ** - +--------+ - | finish | - +--------+ - * - * - * - +---------+ - | __end__ | - +---------+ + graph TD; + __start__ --> prepare; + finish --> __end__; + prepare -.-> tool_two_slow; + tool_two_slow --> finish; + prepare -.-> tool_two_fast; + tool_two_fast --> finish; + ''' # --- # name: test_branch_then[end_of_run].1 @@ -54,33 +35,14 @@ # --- # name: test_branch_then[end_of_step] ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +---------+ - | prepare | - +---------+. - .. .. - .. .. - .. .. - +---------------+ +---------------+ - | tool_two_slow | | tool_two_fast | - +---------------+ +---------------+ - ** ** - ** ** - ** ** - +--------+ - | finish | - +--------+ - * - * - * - +---------+ - | __end__ | - +---------+ + graph TD; + __start__ --> prepare; + finish --> __end__; + prepare -.-> tool_two_slow; + tool_two_slow --> finish; + prepare -.-> tool_two_fast; + tool_two_fast --> finish; + ''' # --- # name: test_branch_then[end_of_step].1 @@ -178,21 +140,12 @@ # --- # name: test_conditional_entrypoint_graph.3 ''' - +-----------+ - | __start__ | - +-----------+ - .. . - . .. - . . - +-------+ +------+ - | right | | left | - +-------+ +------+ - ** . - * .. - * . - +---------+ - | __end__ | - +---------+ + graph TD; + right --> __end__; + __start__ -. go-left .-> left; + __start__ -. go-right .-> right; + left -.-> __end__; + ''' # --- # name: test_conditional_entrypoint_graph_state @@ -268,21 +221,12 @@ # --- # name: test_conditional_entrypoint_graph_state.3 ''' - +-----------+ - | __start__ | - +-----------+ - .. . - . .. - . . - +-------+ +------+ - | right | | left | - +-------+ +------+ - ** . - * .. - * . - +---------+ - | __end__ | - +---------+ + graph TD; + right --> __end__; + __start__ -. go-left .-> left; + __start__ -. go-right .-> right; + left -.-> __end__; + ''' # --- # name: test_conditional_graph[end_of_run] @@ -351,25 +295,6 @@ ''' # --- # name: test_conditional_graph[end_of_run].1 - ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +-------+ - | agent | - +-------+ - * . - ** .. - * . - +-------+ +---------+ - | tools | | __end__ | - +-------+ +---------+ - ''' -# --- -# name: test_conditional_graph[end_of_run].2 ''' graph TD; __start__ --> agent; @@ -379,7 +304,7 @@ ''' # --- -# name: test_conditional_graph[end_of_run].3 +# name: test_conditional_graph[end_of_run].2 ''' { "nodes": [ @@ -517,50 +442,7 @@ } ''' # --- -# name: test_conditional_graph[end_of_run].4 - ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +------------------------------+ - | ParallelInput | - +------------------------------+ - ***** * ***** - ****** * **** - *** * ***** - +----------------+ * *** - | PromptTemplate | * * - +----------------+ * * - * * * - * * * - * * * - +----------------------+ * * - | FakeStreamingListLLM | * * - +----------------------+ * * - * * * - * * * - * * * - +----------------------+ +-------------+ * - | Lambda(agent_parser) | | Passthrough | * - +----------------------+ +-------------+ * - *** *** * - ** ** * - ** ** * - +-------------------------------+ * - | ParallelOutput | * - +-------------------------------+..... * - . ......... * - . ........ * - . ..... * - +---------+ +-------+ - | __end__ | | tools | - +---------+ +-------+ - ''' -# --- -# name: test_conditional_graph[end_of_run].5 +# name: test_conditional_graph[end_of_run].3 ''' graph TD; PromptTemplate --> FakeStreamingListLLM; @@ -642,25 +524,6 @@ ''' # --- # name: test_conditional_graph[end_of_step].1 - ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +-------+ - | agent | - +-------+ - * . - ** .. - * . - +-------+ +---------+ - | tools | | __end__ | - +-------+ +---------+ - ''' -# --- -# name: test_conditional_graph[end_of_step].2 ''' graph TD; __start__ --> agent; @@ -670,7 +533,7 @@ ''' # --- -# name: test_conditional_graph[end_of_step].3 +# name: test_conditional_graph[end_of_step].2 ''' { "nodes": [ @@ -808,50 +671,7 @@ } ''' # --- -# name: test_conditional_graph[end_of_step].4 - ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +------------------------------+ - | ParallelInput | - +------------------------------+ - ***** * ***** - ****** * **** - *** * ***** - +----------------+ * *** - | PromptTemplate | * * - +----------------+ * * - * * * - * * * - * * * - +----------------------+ * * - | FakeStreamingListLLM | * * - +----------------------+ * * - * * * - * * * - * * * - +----------------------+ +-------------+ * - | Lambda(agent_parser) | | Passthrough | * - +----------------------+ +-------------+ * - *** *** * - ** ** * - ** ** * - +-------------------------------+ * - | ParallelOutput | * - +-------------------------------+..... * - . ......... * - . ........ * - . ..... * - +---------+ +-------+ - | __end__ | | tools | - +---------+ +-------+ - ''' -# --- -# name: test_conditional_graph[end_of_step].5 +# name: test_conditional_graph[end_of_step].3 ''' graph TD; PromptTemplate --> FakeStreamingListLLM; @@ -940,21 +760,12 @@ # --- # name: test_conditional_graph_state[end_of_run].3 ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +-------+ - | agent | - +-------+ - . . - .. .. - . . - +-------+ +---------+ - | tools | | __end__ | - +-------+ +---------+ + graph TD; + __start__ --> agent; + tools --> agent; + agent -. continue .-> tools; + agent -. exit .-> __end__; + ''' # --- # name: test_conditional_graph_state[end_of_step] @@ -1030,243 +841,90 @@ # --- # name: test_conditional_graph_state[end_of_step].3 ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +-------+ - | agent | - +-------+ - * . - ** .. - * . - +-------+ +---------+ - | tools | | __end__ | - +-------+ +---------+ + graph TD; + __start__ --> agent; + tools --> agent; + agent -. continue .-> tools; + agent -. exit .-> __end__; + ''' # --- # name: test_in_one_fan_out_state_graph_waiting_edge[end_of_run] ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +---------------+ - | rewrite_query | - +---------------+ - *** *** - * * - ** *** - +--------------+ * - | analyzer_one | * - +--------------+ * - * * - * * - * * - +---------------+ +---------------+ - | retriever_one | | retriever_two | - +---------------+ +---------------+ - *** *** - * * - ** ** - +----+ - | qa | - +----+ - * - * - * - +---------+ - | __end__ | - +---------+ + graph TD; + __start__ --> rewrite_query; + analyzer_one --> retriever_one; + qa --> __end__; + retriever_one --> qa; + retriever_two --> qa; + rewrite_query --> analyzer_one; + rewrite_query --> retriever_two; + ''' # --- # name: test_in_one_fan_out_state_graph_waiting_edge[end_of_step] ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +---------------+ - | rewrite_query | - +---------------+ - *** *** - * * - ** *** - +--------------+ * - | analyzer_one | * - +--------------+ * - * * - * * - * * - +---------------+ +---------------+ - | retriever_one | | retriever_two | - +---------------+ +---------------+ - *** *** - * * - ** ** - +----+ - | qa | - +----+ - * - * - * - +---------+ - | __end__ | - +---------+ + graph TD; + __start__ --> rewrite_query; + analyzer_one --> retriever_one; + qa --> __end__; + retriever_one --> qa; + retriever_two --> qa; + rewrite_query --> analyzer_one; + rewrite_query --> retriever_two; + ''' # --- # name: test_in_one_fan_out_state_graph_waiting_edge_custom_state_class[end_of_run] ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +---------------+ - | rewrite_query | - +---------------+ - *** ... - * . - ** ... - +--------------+ . - | analyzer_one | . - +--------------+ . - * . - * . - * . - +---------------+ +---------------+ - | retriever_one | | retriever_two | - +---------------+ +---------------+ - *** *** - * * - ** ** - +----+ - | qa | - +----+ - * - * - * - +---------+ - | __end__ | - +---------+ + graph TD; + __start__ --> rewrite_query; + analyzer_one --> retriever_one; + qa --> __end__; + retriever_one --> qa; + retriever_two --> qa; + rewrite_query --> analyzer_one; + rewrite_query -.-> retriever_two; + ''' # --- # name: test_in_one_fan_out_state_graph_waiting_edge_custom_state_class[end_of_step] ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +---------------+ - | rewrite_query | - +---------------+ - *** ... - * . - ** ... - +--------------+ . - | analyzer_one | . - +--------------+ . - * . - * . - * . - +---------------+ +---------------+ - | retriever_one | | retriever_two | - +---------------+ +---------------+ - *** *** - * * - ** ** - +----+ - | qa | - +----+ - * - * - * - +---------+ - | __end__ | - +---------+ + graph TD; + __start__ --> rewrite_query; + analyzer_one --> retriever_one; + qa --> __end__; + retriever_one --> qa; + retriever_two --> qa; + rewrite_query --> analyzer_one; + rewrite_query -.-> retriever_two; + ''' # --- # name: test_in_one_fan_out_state_graph_waiting_edge_via_branch[end_of_run] ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +---------------+ - | rewrite_query | - +---------------+ - *** ... - * . - ** ... - +--------------+ . - | analyzer_one | . - +--------------+ . - * . - * . - * . - +---------------+ +---------------+ - | retriever_one | | retriever_two | - +---------------+ +---------------+ - *** *** - * * - ** ** - +----+ - | qa | - +----+ - * - * - * - +---------+ - | __end__ | - +---------+ + graph TD; + __start__ --> rewrite_query; + analyzer_one --> retriever_one; + qa --> __end__; + retriever_one --> qa; + retriever_two --> qa; + rewrite_query --> analyzer_one; + rewrite_query -.-> retriever_two; + ''' # --- # name: test_in_one_fan_out_state_graph_waiting_edge_via_branch[end_of_step] ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +---------------+ - | rewrite_query | - +---------------+ - *** ... - * . - ** ... - +--------------+ . - | analyzer_one | . - +--------------+ . - * . - * . - * . - +---------------+ +---------------+ - | retriever_one | | retriever_two | - +---------------+ +---------------+ - *** *** - * * - ** ** - +----+ - | qa | - +----+ - * - * - * - +---------+ - | __end__ | - +---------+ + graph TD; + __start__ --> rewrite_query; + analyzer_one --> retriever_one; + qa --> __end__; + retriever_one --> qa; + retriever_two --> qa; + rewrite_query --> analyzer_one; + rewrite_query -.-> retriever_two; + ''' # --- # name: test_message_graph[end_of_run] @@ -1342,21 +1000,12 @@ # --- # name: test_message_graph[end_of_run].3 ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +-------+ - | agent | - +-------+ - * .. - ** .. - * . - +--------+ +---------+ - | action | | __end__ | - +--------+ +---------+ + graph TD; + __start__ --> agent; + action --> agent; + agent -. continue .-> action; + agent -. end .-> __end__; + ''' # --- # name: test_message_graph[end_of_step] @@ -1432,46 +1081,21 @@ # --- # name: test_message_graph[end_of_step].3 ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +-------+ - | agent | - +-------+ - * .. - ** .. - * . - +--------+ +---------+ - | action | | __end__ | - +--------+ +---------+ + graph TD; + __start__ --> agent; + action --> agent; + agent -. continue .-> action; + agent -. end .-> __end__; + ''' # --- # name: test_nested_graph ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +-------+ - | inner | - +-------+ - * - * - * - +------+ - | side | - +------+ - * - * - * - +---------+ - | __end__ | - +---------+ + graph TD; + __start__ --> inner; + inner --> side; + side --> __end__; + ''' # --- # name: test_nested_graph.1 @@ -1613,25 +1237,6 @@ }) # --- # name: test_nested_graph_xray.1 - ''' - +-----------+ - | __start__ | - ..+-----------+... - ..... . ..... - ... . ... - ... . ... - +----------+ +----------+ +------------+ - | tool_one |* | tool_two | | tool_three | - +----------+ *** +----------+ **+------------+ - ***** * ***** - *** * *** - *** * *** - +---------+ - | __end__ | - +---------+ - ''' -# --- -# name: test_nested_graph_xray.2 ''' %%{init: {'flowchart': {'curve': 'linear'}}}%% graph TD; @@ -1735,21 +1340,12 @@ # --- # name: test_prebuilt_chat.3 ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +-------+ - | agent | - +-------+ - * .. - ** .. - * . - +--------+ +---------+ - | action | | __end__ | - +--------+ +---------+ + graph TD; + __start__ --> agent; + action --> agent; + agent -. continue .-> action; + agent -. end .-> __end__; + ''' # --- # name: test_prebuilt_tool_chat @@ -1826,21 +1422,12 @@ # --- # name: test_prebuilt_tool_chat.3 ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +-------+ - | agent | - +-------+ - . .. - .. .. - . . - +--------+ +---------+ - | action | | __end__ | - +--------+ +---------+ + graph TD; + __start__ --> agent; + action --> agent; + agent -. continue .-> action; + agent -. end .-> __end__; + ''' # --- # name: test_repeat_condition @@ -1860,55 +1447,16 @@ # --- # name: test_simple_multi_edge ''' - +-----------+ - | __start__ | - +-----------+ - * - * - * - +----+ - | up | - +----+ - ** ** - * * - * * - +------+ * - | side | * - +------+ * - ** ** - * * - * * - +------+ - | down | - +------+ - * - * - * - +---------+ - | __end__ | - +---------+ + graph TD; + __start__ --> up; + down --> __end__; + side --> down; + up --> down; + up --> side; + ''' # --- # name: test_start_branch_then[end_of_run] - ''' - +-----------+ - | __start__ | - +-----------+ - .. .. - .. .. - .. .. - +---------------+ +---------------+ - | tool_two_slow | | tool_two_fast | - +---------------+ +---------------+ - ** ** - ** ** - ** ** - +---------+ - | __end__ | - +---------+ - ''' -# --- -# name: test_start_branch_then[end_of_run].1 ''' %%{init: {'flowchart': {'curve': 'linear'}}}%% graph TD; @@ -1927,25 +1475,6 @@ ''' # --- # name: test_start_branch_then[end_of_step] - ''' - +-----------+ - | __start__ | - +-----------+ - .. .. - .. .. - .. .. - +---------------+ +---------------+ - | tool_two_slow | | tool_two_fast | - +---------------+ +---------------+ - ** ** - ** ** - ** ** - +---------+ - | __end__ | - +---------+ - ''' -# --- -# name: test_start_branch_then[end_of_step].1 ''' %%{init: {'flowchart': {'curve': 'linear'}}}%% graph TD; diff --git a/tests/test_pregel.py b/tests/test_pregel.py index 900683242..909be8cce 100644 --- a/tests/test_pregel.py +++ b/tests/test_pregel.py @@ -882,10 +882,8 @@ def test_conditional_graph( app = workflow.compile() assert json.dumps(app.get_graph().to_json(), indent=2) == snapshot - assert app.get_graph().draw_ascii() == snapshot assert app.get_graph().draw_mermaid(with_styles=False) == snapshot assert json.dumps(app.get_graph(xray=True).to_json(), indent=2) == snapshot - assert app.get_graph(xray=True).draw_ascii() == snapshot assert app.get_graph(xray=True).draw_mermaid(with_styles=False) == snapshot assert app.invoke({"input": "what is weather in sf"}) == { @@ -1462,7 +1460,7 @@ def test_conditional_entrypoint_graph(snapshot: SnapshotAssertion) -> None: 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.get_graph().draw_mermaid(with_styles=False) == snapshot assert ( app.invoke("what is weather in sf", debug=True) @@ -1562,7 +1560,7 @@ def test_conditional_graph_state( 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.get_graph().draw_mermaid(with_styles=False) == snapshot assert app.invoke({"input": "what is weather in sf"}) == { "input": "what is weather in sf", @@ -1914,7 +1912,7 @@ def test_conditional_entrypoint_graph_state(snapshot: SnapshotAssertion) -> None 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.get_graph().draw_mermaid(with_styles=False) == snapshot assert app.invoke({"input": "what is weather in sf"}) == { "input": "what is weather in sf", @@ -1980,7 +1978,7 @@ def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None: 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.get_graph().draw_mermaid(with_styles=False) == snapshot assert app.invoke( {"messages": [HumanMessage(content="what is weather in sf")]} @@ -2243,7 +2241,7 @@ def test_prebuilt_chat(snapshot: SnapshotAssertion) -> None: 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.get_graph().draw_mermaid(with_styles=False) == snapshot assert app.invoke( {"messages": [HumanMessage(content="what is weather in sf")]} @@ -2458,7 +2456,7 @@ def test_message_graph( 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.get_graph().draw_mermaid(with_styles=False) == snapshot assert app.invoke(HumanMessage(content="what is weather in sf")) == [ HumanMessage( @@ -3023,7 +3021,6 @@ def test_start_branch_then( lambda s: "tool_two_slow" if s["market"] == "DE" else "tool_two_fast", then=END ) tool_two = tool_two_graph.compile() - assert tool_two.get_graph().draw_ascii() == snapshot assert tool_two.get_graph().draw_mermaid() == snapshot assert tool_two.invoke({"my_key": "value", "market": "DE"}) == { @@ -3127,7 +3124,7 @@ def test_branch_then(snapshot: SnapshotAssertion, checkpoint_at: CheckpointAt) - tool_two_graph.add_node("tool_two_fast", lambda s: {"my_key": " fast"}) tool_two_graph.add_node("finish", lambda s: {"my_key": " finished"}) tool_two = tool_two_graph.compile() - assert tool_two.get_graph().draw_ascii() == snapshot + assert tool_two.get_graph().draw_mermaid(with_styles=False) == snapshot assert tool_two.get_graph().draw_mermaid() == snapshot assert tool_two.invoke({"my_key": "value", "market": "DE"}, debug=1) == { @@ -3300,7 +3297,7 @@ def test_in_one_fan_out_state_graph_waiting_edge( app = workflow.compile() - assert app.get_graph().draw_ascii() == snapshot + assert app.get_graph().draw_mermaid(with_styles=False) == snapshot assert app.invoke({"query": "what is weather in sf"}) == { "query": "analyzed: query: what is weather in sf", @@ -3396,7 +3393,7 @@ def test_in_one_fan_out_state_graph_waiting_edge_via_branch( app = workflow.compile() - assert app.get_graph().draw_ascii() == snapshot + assert app.get_graph().draw_mermaid(with_styles=False) == snapshot assert app.invoke({"query": "what is weather in sf"}, debug=True) == { "query": "analyzed: query: what is weather in sf", @@ -3497,7 +3494,7 @@ def test_in_one_fan_out_state_graph_waiting_edge_custom_state_class( app = workflow.compile() - assert app.get_graph().draw_ascii() == snapshot + assert app.get_graph().draw_mermaid(with_styles=False) == snapshot with pytest.raises(ValidationError): app.invoke({"query": {}}) @@ -3834,7 +3831,7 @@ def test_simple_multi_edge(snapshot: SnapshotAssertion) -> None: app = graph.compile() - assert app.get_graph().draw_ascii() == snapshot + assert app.get_graph().draw_mermaid(with_styles=False) == snapshot assert app.invoke({"my_key": "my_value"}) == {"my_key": "my_value"} @@ -3863,7 +3860,6 @@ def test_nested_graph_xray(snapshot: SnapshotAssertion) -> None: app = graph.compile() assert app.get_graph(xray=True).to_json() == snapshot - assert app.get_graph().draw_ascii() == snapshot assert app.get_graph(xray=True).draw_mermaid() == snapshot @@ -3901,7 +3897,7 @@ def test_nested_graph(snapshot: SnapshotAssertion) -> None: app = graph.compile() - assert app.get_graph().draw_ascii() == snapshot + assert app.get_graph().draw_mermaid(with_styles=False) == snapshot assert app.get_graph(xray=True).draw_mermaid() == snapshot assert app.invoke( {"my_key": "my value", "never_called": never_called}, debug=True