mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-09-13 13:17:52 +02:00
Merge pull request #323 from angeligareta/improve_visualization_notebook
Enrich visualization notebook to showcase new capabilities
This commit is contained in:
+378
-66
File diff suppressed because one or more lines are too long
@@ -413,9 +413,11 @@ class CompiledGraph(Pregel):
|
||||
cond = graph.add_node(branch.condition, name)
|
||||
graph.add_edge(start_nodes[start], cond)
|
||||
for label, end in ends.items():
|
||||
graph.add_edge(cond, end_nodes[end], label)
|
||||
graph.add_edge(cond, end_nodes[end], label, conditional=True)
|
||||
else:
|
||||
for label, end in ends.items():
|
||||
graph.add_edge(start_nodes[start], end_nodes[end], label)
|
||||
graph.add_edge(
|
||||
start_nodes[start], end_nodes[end], label, conditional=True
|
||||
)
|
||||
|
||||
return graph
|
||||
|
||||
Generated
+3
-3
@@ -1712,13 +1712,13 @@ extended-testing = ["aiosqlite (>=0.19.0,<0.20.0)", "aleph-alpha-client (>=2.15.
|
||||
|
||||
[[package]]
|
||||
name = "langchain-core"
|
||||
version = "0.1.42"
|
||||
version = "0.1.44"
|
||||
description = "Building applications with LLMs through composability"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8.1"
|
||||
files = [
|
||||
{file = "langchain_core-0.1.42-py3-none-any.whl", hash = "sha256:c5653ffa08a44f740295c157a24c0def4a753333f6a2c41f76bf431cd00be8b5"},
|
||||
{file = "langchain_core-0.1.42.tar.gz", hash = "sha256:40751bf60ea5d8e2b2efe65290db434717ee3834870c002e40e2811f09d814e6"},
|
||||
{file = "langchain_core-0.1.44-py3-none-any.whl", hash = "sha256:d8772dccef95fc97bfa2dcd19412e620ebe14def1f0e218374971f6e30a46a49"},
|
||||
{file = "langchain_core-0.1.44.tar.gz", hash = "sha256:e313975d9ae2926342e6f2ad760338d31f18b1223e9b8b4dc408daeeade46a83"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,174 @@
|
||||
# serializer version: 1
|
||||
# name: test_in_one_fan_out_state_graph_waiting_edge_custom_state_class[end_of_run]
|
||||
'''
|
||||
+-----------+
|
||||
| __start__ |
|
||||
+-----------+
|
||||
*
|
||||
*
|
||||
*
|
||||
+---------------+
|
||||
| rewrite_query |
|
||||
+---------------+
|
||||
*** ***
|
||||
* *
|
||||
** **
|
||||
+--------------+ +---------+
|
||||
| analyzer_one | | decider |
|
||||
+--------------+ +---------+
|
||||
* .
|
||||
* .
|
||||
* .
|
||||
+---------------+ +---------------+
|
||||
| retriever_one | | retriever_two |
|
||||
+---------------+ +---------------+
|
||||
*** ***
|
||||
* *
|
||||
** **
|
||||
+----+
|
||||
| qa |
|
||||
+----+
|
||||
*
|
||||
*
|
||||
*
|
||||
+---------+
|
||||
| __end__ |
|
||||
+---------+
|
||||
'''
|
||||
# ---
|
||||
# name: test_in_one_fan_out_state_graph_waiting_edge_custom_state_class[end_of_step]
|
||||
'''
|
||||
+-----------+
|
||||
| __start__ |
|
||||
+-----------+
|
||||
*
|
||||
*
|
||||
*
|
||||
+---------------+
|
||||
| rewrite_query |
|
||||
+---------------+
|
||||
*** ***
|
||||
* *
|
||||
** **
|
||||
+--------------+ +---------+
|
||||
| analyzer_one | | decider |
|
||||
+--------------+ +---------+
|
||||
* .
|
||||
* .
|
||||
* .
|
||||
+---------------+ +---------------+
|
||||
| retriever_one | | retriever_two |
|
||||
+---------------+ +---------------+
|
||||
*** ***
|
||||
* *
|
||||
** **
|
||||
+----+
|
||||
| qa |
|
||||
+----+
|
||||
*
|
||||
*
|
||||
*
|
||||
+---------+
|
||||
| __end__ |
|
||||
+---------+
|
||||
'''
|
||||
# ---
|
||||
# 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__ |
|
||||
+---------+
|
||||
'''
|
||||
# ---
|
||||
# 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__ |
|
||||
+---------+
|
||||
'''
|
||||
# ---
|
||||
# name: test_nested_graph
|
||||
'''
|
||||
+-----------+
|
||||
| __start__ |
|
||||
+-----------+
|
||||
*
|
||||
*
|
||||
*
|
||||
+-------+
|
||||
| inner |
|
||||
+-------+
|
||||
*
|
||||
*
|
||||
*
|
||||
+------+
|
||||
| side |
|
||||
+------+
|
||||
*
|
||||
*
|
||||
*
|
||||
+---------+
|
||||
| __end__ |
|
||||
+---------+
|
||||
'''
|
||||
# ---
|
||||
+23
-163
@@ -796,10 +796,18 @@ 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_ascii() == snapshot
|
||||
assert (
|
||||
app.get_graph(add_condition_nodes=False).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_ascii() == snapshot
|
||||
assert (
|
||||
app.get_graph(xray=True, add_condition_nodes=False).draw_mermaid(
|
||||
with_styles=False
|
||||
)
|
||||
== snapshot
|
||||
)
|
||||
|
||||
assert app.invoke({"input": "what is weather in sf"}) == {
|
||||
"input": "what is weather in sf",
|
||||
@@ -1819,7 +1827,7 @@ def test_conditional_entrypoint_graph_state(snapshot: SnapshotAssertion) -> None
|
||||
should_start, {"go-left": "left", "go-right": "right"}
|
||||
)
|
||||
|
||||
workflow.add_conditional_edges("left", lambda data: END)
|
||||
workflow.add_conditional_edges("left", lambda data: END, {END: END})
|
||||
workflow.add_edge("right", END)
|
||||
|
||||
app = workflow.compile()
|
||||
@@ -2912,7 +2920,9 @@ def test_in_one_fan_out_out_one_graph_state() -> None:
|
||||
@pytest.mark.parametrize(
|
||||
"checkpoint_at", [CheckpointAt.END_OF_RUN, CheckpointAt.END_OF_STEP]
|
||||
)
|
||||
def test_in_one_fan_out_state_graph_waiting_edge(checkpoint_at: CheckpointAt) -> None:
|
||||
def test_in_one_fan_out_state_graph_waiting_edge(
|
||||
snapshot: SnapshotAssertion, checkpoint_at: CheckpointAt
|
||||
) -> None:
|
||||
def sorted_add(
|
||||
x: list[str], y: Union[list[str], list[tuple[str, str]]]
|
||||
) -> list[str]:
|
||||
@@ -2959,41 +2969,7 @@ def test_in_one_fan_out_state_graph_waiting_edge(checkpoint_at: CheckpointAt) ->
|
||||
|
||||
app = workflow.compile()
|
||||
|
||||
assert app.get_graph().draw_ascii() == (
|
||||
""" +-----------+
|
||||
| __start__ |
|
||||
+-----------+
|
||||
*
|
||||
*
|
||||
*
|
||||
+---------------+
|
||||
| rewrite_query |
|
||||
+---------------+
|
||||
*** ***
|
||||
* *
|
||||
** ***
|
||||
+--------------+ *
|
||||
| analyzer_one | *
|
||||
+--------------+ *
|
||||
* *
|
||||
* *
|
||||
* *
|
||||
+---------------+ +---------------+
|
||||
| retriever_one | | retriever_two |
|
||||
+---------------+ +---------------+
|
||||
*** ***
|
||||
* *
|
||||
** **
|
||||
+----+
|
||||
| qa |
|
||||
+----+
|
||||
*
|
||||
*
|
||||
*
|
||||
+---------+
|
||||
| __end__ |
|
||||
+---------+ """
|
||||
)
|
||||
assert app.get_graph().draw_ascii() == snapshot
|
||||
|
||||
assert app.invoke({"query": "what is weather in sf"}) == {
|
||||
"query": "analyzed: query: what is weather in sf",
|
||||
@@ -3037,6 +3013,7 @@ def test_in_one_fan_out_state_graph_waiting_edge(checkpoint_at: CheckpointAt) ->
|
||||
"checkpoint_at", [CheckpointAt.END_OF_RUN, CheckpointAt.END_OF_STEP]
|
||||
)
|
||||
def test_in_one_fan_out_state_graph_waiting_edge_via_branch(
|
||||
snapshot: SnapshotAssertion,
|
||||
checkpoint_at: CheckpointAt,
|
||||
) -> None:
|
||||
def sorted_add(
|
||||
@@ -3087,41 +3064,7 @@ def test_in_one_fan_out_state_graph_waiting_edge_via_branch(
|
||||
|
||||
app = workflow.compile()
|
||||
|
||||
assert app.get_graph().draw_ascii() == (
|
||||
""" +-----------+
|
||||
| __start__ |
|
||||
+-----------+
|
||||
*
|
||||
*
|
||||
*
|
||||
+---------------+
|
||||
| rewrite_query |
|
||||
+---------------+
|
||||
*** ***
|
||||
* *
|
||||
** **
|
||||
+--------------+ +-----------+
|
||||
| analyzer_one | | condition |
|
||||
+--------------+ +-----------+
|
||||
* *
|
||||
* *
|
||||
* *
|
||||
+---------------+ +---------------+
|
||||
| retriever_one | | retriever_two |
|
||||
+---------------+ +---------------+
|
||||
*** ***
|
||||
* *
|
||||
** **
|
||||
+----+
|
||||
| qa |
|
||||
+----+
|
||||
*
|
||||
*
|
||||
*
|
||||
+---------+
|
||||
| __end__ |
|
||||
+---------+ """
|
||||
)
|
||||
assert app.get_graph().draw_ascii() == snapshot
|
||||
|
||||
assert app.invoke({"query": "what is weather in sf"}, debug=True) == {
|
||||
"query": "analyzed: query: what is weather in sf",
|
||||
@@ -3165,6 +3108,7 @@ def test_in_one_fan_out_state_graph_waiting_edge_via_branch(
|
||||
"checkpoint_at", [CheckpointAt.END_OF_RUN, CheckpointAt.END_OF_STEP]
|
||||
)
|
||||
def test_in_one_fan_out_state_graph_waiting_edge_custom_state_class(
|
||||
snapshot: SnapshotAssertion,
|
||||
checkpoint_at: CheckpointAt,
|
||||
) -> None:
|
||||
from langchain_core.pydantic_v1 import BaseModel, ValidationError
|
||||
@@ -3221,41 +3165,7 @@ def test_in_one_fan_out_state_graph_waiting_edge_custom_state_class(
|
||||
|
||||
app = workflow.compile()
|
||||
|
||||
assert app.get_graph().draw_ascii() == (
|
||||
""" +-----------+
|
||||
| __start__ |
|
||||
+-----------+
|
||||
*
|
||||
*
|
||||
*
|
||||
+---------------+
|
||||
| rewrite_query |
|
||||
+---------------+
|
||||
*** ***
|
||||
* *
|
||||
** **
|
||||
+--------------+ +---------+
|
||||
| analyzer_one | | decider |
|
||||
+--------------+ +---------+
|
||||
* *
|
||||
* *
|
||||
* *
|
||||
+---------------+ +---------------+
|
||||
| retriever_one | | retriever_two |
|
||||
+---------------+ +---------------+
|
||||
*** ***
|
||||
* *
|
||||
** **
|
||||
+----+
|
||||
| qa |
|
||||
+----+
|
||||
*
|
||||
*
|
||||
*
|
||||
+---------+
|
||||
| __end__ |
|
||||
+---------+ """
|
||||
)
|
||||
assert app.get_graph().draw_ascii() == snapshot
|
||||
|
||||
with pytest.raises(ValidationError):
|
||||
app.invoke({"query": {}})
|
||||
@@ -3566,7 +3476,7 @@ def test_in_one_fan_out_state_graph_waiting_edge_multiple_cond_edge() -> None:
|
||||
]
|
||||
|
||||
|
||||
def test_simple_multi_edge() -> None:
|
||||
def test_simple_multi_edge(snapshot: SnapshotAssertion) -> None:
|
||||
class State(TypedDict):
|
||||
my_key: Annotated[str, operator.add]
|
||||
|
||||
@@ -3592,39 +3502,11 @@ def test_simple_multi_edge() -> None:
|
||||
|
||||
app = graph.compile()
|
||||
|
||||
assert app.get_graph().draw_ascii() == (
|
||||
""" +-----------+
|
||||
| __start__ |
|
||||
+-----------+
|
||||
*
|
||||
*
|
||||
*
|
||||
+----+
|
||||
| up |
|
||||
+----+
|
||||
** **
|
||||
* *
|
||||
* *
|
||||
+------+ *
|
||||
| side | *
|
||||
+------+ *
|
||||
** **
|
||||
* *
|
||||
* *
|
||||
+------+
|
||||
| down |
|
||||
+------+
|
||||
*
|
||||
*
|
||||
*
|
||||
+---------+
|
||||
| __end__ |
|
||||
+---------+ """
|
||||
)
|
||||
assert app.get_graph().draw_ascii() == snapshot
|
||||
assert app.invoke({"my_key": "my_value"}) == {"my_key": "my_value"}
|
||||
|
||||
|
||||
def test_nested_graph() -> None:
|
||||
def test_nested_graph(snapshot: SnapshotAssertion) -> None:
|
||||
class State(TypedDict):
|
||||
my_key: str
|
||||
|
||||
@@ -3648,29 +3530,7 @@ def test_nested_graph() -> None:
|
||||
|
||||
app = graph.compile()
|
||||
|
||||
assert app.get_graph().draw_ascii() == (
|
||||
"""+-----------+
|
||||
| __start__ |
|
||||
+-----------+
|
||||
*
|
||||
*
|
||||
*
|
||||
+-------+
|
||||
| inner |
|
||||
+-------+
|
||||
*
|
||||
*
|
||||
*
|
||||
+------+
|
||||
| side |
|
||||
+------+
|
||||
*
|
||||
*
|
||||
*
|
||||
+---------+
|
||||
| __end__ |
|
||||
+---------+ """
|
||||
)
|
||||
assert app.get_graph().draw_ascii() == snapshot
|
||||
assert app.invoke({"my_key": "my value"}) == {
|
||||
"my_key": "my value there and back again"
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ from uuid import UUID
|
||||
import pytest
|
||||
from langchain_core.runnables import RunnableLambda, RunnablePassthrough
|
||||
from pytest_mock import MockerFixture
|
||||
from syrupy import SnapshotAssertion
|
||||
|
||||
from langgraph.channels.base import InvalidUpdateError
|
||||
from langgraph.channels.binop import BinaryOperatorAggregate
|
||||
@@ -2775,6 +2776,7 @@ async def test_in_one_fan_out_state_graph_waiting_edge(
|
||||
"checkpoint_at", [CheckpointAt.END_OF_RUN, CheckpointAt.END_OF_STEP]
|
||||
)
|
||||
async def test_in_one_fan_out_state_graph_waiting_edge_via_branch(
|
||||
snapshot: SnapshotAssertion,
|
||||
checkpoint_at: CheckpointAt,
|
||||
) -> None:
|
||||
def sorted_add(
|
||||
@@ -2825,41 +2827,7 @@ async def test_in_one_fan_out_state_graph_waiting_edge_via_branch(
|
||||
|
||||
app = workflow.compile()
|
||||
|
||||
assert app.get_graph().draw_ascii() == (
|
||||
""" +-----------+
|
||||
| __start__ |
|
||||
+-----------+
|
||||
*
|
||||
*
|
||||
*
|
||||
+---------------+
|
||||
| rewrite_query |
|
||||
+---------------+
|
||||
*** ***
|
||||
* *
|
||||
** **
|
||||
+--------------+ +-----------+
|
||||
| analyzer_one | | condition |
|
||||
+--------------+ +-----------+
|
||||
* *
|
||||
* *
|
||||
* *
|
||||
+---------------+ +---------------+
|
||||
| retriever_one | | retriever_two |
|
||||
+---------------+ +---------------+
|
||||
*** ***
|
||||
* *
|
||||
** **
|
||||
+----+
|
||||
| qa |
|
||||
+----+
|
||||
*
|
||||
*
|
||||
*
|
||||
+---------+
|
||||
| __end__ |
|
||||
+---------+ """
|
||||
)
|
||||
assert app.get_graph().draw_ascii() == snapshot
|
||||
|
||||
assert await app.ainvoke({"query": "what is weather in sf"}, debug=True) == {
|
||||
"query": "analyzed: query: what is weather in sf",
|
||||
@@ -2906,6 +2874,7 @@ async def test_in_one_fan_out_state_graph_waiting_edge_via_branch(
|
||||
"checkpoint_at", [CheckpointAt.END_OF_RUN, CheckpointAt.END_OF_STEP]
|
||||
)
|
||||
async def test_in_one_fan_out_state_graph_waiting_edge_custom_state_class(
|
||||
snapshot: SnapshotAssertion,
|
||||
checkpoint_at: CheckpointAt,
|
||||
) -> None:
|
||||
from langchain_core.pydantic_v1 import BaseModel, ValidationError
|
||||
@@ -2962,41 +2931,7 @@ async def test_in_one_fan_out_state_graph_waiting_edge_custom_state_class(
|
||||
|
||||
app = workflow.compile()
|
||||
|
||||
assert app.get_graph().draw_ascii() == (
|
||||
""" +-----------+
|
||||
| __start__ |
|
||||
+-----------+
|
||||
*
|
||||
*
|
||||
*
|
||||
+---------------+
|
||||
| rewrite_query |
|
||||
+---------------+
|
||||
*** ***
|
||||
* *
|
||||
** **
|
||||
+--------------+ +---------+
|
||||
| analyzer_one | | decider |
|
||||
+--------------+ +---------+
|
||||
* *
|
||||
* *
|
||||
* *
|
||||
+---------------+ +---------------+
|
||||
| retriever_one | | retriever_two |
|
||||
+---------------+ +---------------+
|
||||
*** ***
|
||||
* *
|
||||
** **
|
||||
+----+
|
||||
| qa |
|
||||
+----+
|
||||
*
|
||||
*
|
||||
*
|
||||
+---------+
|
||||
| __end__ |
|
||||
+---------+ """
|
||||
)
|
||||
assert app.get_graph().draw_ascii() == snapshot
|
||||
|
||||
with pytest.raises(ValidationError):
|
||||
await app.ainvoke({"query": {}})
|
||||
@@ -3313,7 +3248,7 @@ async def test_in_one_fan_out_state_graph_waiting_edge_multiple_cond_edge() -> N
|
||||
]
|
||||
|
||||
|
||||
async def test_nested_graph() -> None:
|
||||
async def test_nested_graph(snapshot: SnapshotAssertion) -> None:
|
||||
class State(TypedDict):
|
||||
my_key: str
|
||||
|
||||
@@ -3337,29 +3272,7 @@ async def test_nested_graph() -> None:
|
||||
|
||||
app = graph.compile()
|
||||
|
||||
assert app.get_graph().draw_ascii() == (
|
||||
"""+-----------+
|
||||
| __start__ |
|
||||
+-----------+
|
||||
*
|
||||
*
|
||||
*
|
||||
+-------+
|
||||
| inner |
|
||||
+-------+
|
||||
*
|
||||
*
|
||||
*
|
||||
+------+
|
||||
| side |
|
||||
+------+
|
||||
*
|
||||
*
|
||||
*
|
||||
+---------+
|
||||
| __end__ |
|
||||
+---------+ """
|
||||
)
|
||||
assert app.get_graph().draw_ascii() == snapshot
|
||||
assert await app.ainvoke({"my_key": "my value"}) == {
|
||||
"my_key": "my value there and back again"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user