From 15d558ff16a4b0879c701609fae41932e05cf066 Mon Sep 17 00:00:00 2001 From: Caspar Broekhuizen Date: Thu, 11 Sep 2025 14:15:17 -0700 Subject: [PATCH] fix(core): resolve mermaid node id collisions when special chars are used (#32857) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Description * Replace the Mermaid graph node label escaping logic (`_escape_node_label`) with `_to_safe_id`, which converts a string into a unique, Mermaid-compatible node id. Ensures nodes with special characters always render correctly. **Before** * Invalid characters (e.g. `开`) replaced with `_`. Causes collisions between nodes with names that are the same length and contain all non-safe characters: ```python _escape_node_label("开") # '_' _escape_node_label("始") # '_' same as above, but different character passed in. not a unique mapping. ``` **After** ```python _to_safe_id("开") # \5f00 _to_safe_id("始") # \59cb unique! ``` ### Tests * Rename `test_graph_mermaid_escape_node_label()` to `test_graph_mermaid_to_safe_id()` and update function logic to use `_to_safe_id` * Add `test_graph_mermaid_special_chars()` ### Issue Fixes langchain-ai/langgraph#6036 --- .../langchain_core/runnables/graph_mermaid.py | 21 +- .../runnables/__snapshots__/test_graph.ambr | 115 +++-- .../tests/unit_tests/runnables/test_graph.py | 33 +- .../__snapshots__/test_middleware_agent.ambr | 450 +++++++++--------- 4 files changed, 335 insertions(+), 284 deletions(-) diff --git a/libs/core/langchain_core/runnables/graph_mermaid.py b/libs/core/langchain_core/runnables/graph_mermaid.py index fe945dace4d..a27f11d1ff9 100644 --- a/libs/core/langchain_core/runnables/graph_mermaid.py +++ b/libs/core/langchain_core/runnables/graph_mermaid.py @@ -6,6 +6,7 @@ import asyncio import base64 import random import re +import string import time from dataclasses import asdict from pathlib import Path @@ -148,7 +149,7 @@ def draw_mermaid( + "" ) node_label = format_dict.get(key, format_dict[default_class_label]).format( - _escape_node_label(key), label + _to_safe_id(key), label ) return f"{indent}{node_label}\n" @@ -211,8 +212,7 @@ def draw_mermaid( edge_label = " -.-> " if edge.conditional else " --> " mermaid_graph += ( - f"\t{_escape_node_label(source)}{edge_label}" - f"{_escape_node_label(target)};\n" + f"\t{_to_safe_id(source)}{edge_label}{_to_safe_id(target)};\n" ) # Recursively add nested subgraphs @@ -256,9 +256,18 @@ def draw_mermaid( return mermaid_graph -def _escape_node_label(node_label: str) -> str: - """Escapes the node label for Mermaid syntax.""" - return re.sub(r"[^a-zA-Z-_0-9]", "_", node_label) +def _to_safe_id(label: str) -> str: + """Convert a string into a Mermaid-compatible node id. + + Keep [a-zA-Z0-9_-] characters unchanged. + Map every other character -> backslash + lowercase hex codepoint. + + Result is guaranteed to be unique and Mermaid-compatible, + so nodes with special characters always render correctly. + """ + allowed = string.ascii_letters + string.digits + "_-" + out = [ch if ch in allowed else "\\" + format(ord(ch), "x") for ch in label] + return "".join(out) def _generate_mermaid_graph_styles(node_colors: NodeStyles) -> str: diff --git a/libs/core/tests/unit_tests/runnables/__snapshots__/test_graph.ambr b/libs/core/tests/unit_tests/runnables/__snapshots__/test_graph.ambr index a7dcec5b72b..0232a6f9877 100644 --- a/libs/core/tests/unit_tests/runnables/__snapshots__/test_graph.ambr +++ b/libs/core/tests/unit_tests/runnables/__snapshots__/test_graph.ambr @@ -12,16 +12,16 @@ parent_2(parent_2) __end__([

__end__

]):::last __start__ --> parent_1; - child_child_2 --> parent_2; - parent_1 --> child_child_1_grandchild_1; + child\3achild_2 --> parent_2; + parent_1 --> child\3achild_1\3agrandchild_1; parent_2 --> __end__; subgraph child - child_child_2(child_2) - child_child_1_grandchild_2 --> child_child_2; + child\3achild_2(child_2) + child\3achild_1\3agrandchild_2 --> child\3achild_2; subgraph child_1 - child_child_1_grandchild_1(grandchild_1) - child_child_1_grandchild_2(grandchild_2
__interrupt = before) - child_child_1_grandchild_1 --> child_child_1_grandchild_2; + child\3achild_1\3agrandchild_1(grandchild_1) + child\3achild_1\3agrandchild_2(grandchild_2
__interrupt = before) + child\3achild_1\3agrandchild_1 --> child\3achild_1\3agrandchild_2; end end classDef default fill:#f2f0ff,line-height:1.2 @@ -34,13 +34,13 @@ ''' graph TD; PromptInput --> PromptTemplate_1; - Parallel_llm1_llm2_Input --> FakeListLLM_1; - FakeListLLM_1 --> Parallel_llm1_llm2_Output; - Parallel_llm1_llm2_Input --> FakeListLLM_2; - FakeListLLM_2 --> Parallel_llm1_llm2_Output; - PromptTemplate_1 --> Parallel_llm1_llm2_Input; + Parallel\3cllm1\2cllm2\3eInput --> FakeListLLM_1; + FakeListLLM_1 --> Parallel\3cllm1\2cllm2\3eOutput; + Parallel\3cllm1\2cllm2\3eInput --> FakeListLLM_2; + FakeListLLM_2 --> Parallel\3cllm1\2cllm2\3eOutput; + PromptTemplate_1 --> Parallel\3cllm1\2cllm2\3eInput; PromptTemplate_2 --> PromptTemplateOutput; - Parallel_llm1_llm2_Output --> PromptTemplate_2; + Parallel\3cllm1\2cllm2\3eOutput --> PromptTemplate_2; ''' # --- @@ -65,6 +65,27 @@ ''' # --- +# name: test_graph_mermaid_special_chars[mermaid] + ''' + --- + config: + flowchart: + curve: linear + --- + graph TD; + __start__([

__start__

]):::first + \5f00\59cb(开始) + \7ed3\675f(结束) + __end__([

__end__

]):::last + __start__ --> \5f00\59cb; + \5f00\59cb --> \7ed3\675f; + \7ed3\675f --> __end__; + classDef default fill:#f2f0ff,line-height:1.2 + classDef first fill-opacity:0 + classDef last fill:#bfb6fc + + ''' +# --- # name: test_graph_sequence[ascii] ''' +-------------+ @@ -1902,15 +1923,15 @@ graph TD; PromptInput --> PromptTemplate; PromptTemplate --> FakeListLLM; - Parallel_as_list_as_str_Input --> CommaSeparatedListOutputParser; - CommaSeparatedListOutputParser --> Parallel_as_list_as_str_Output; + Parallel\3cas_list\2cas_str\3eInput --> CommaSeparatedListOutputParser; + CommaSeparatedListOutputParser --> Parallel\3cas_list\2cas_str\3eOutput; conditional_str_parser_input --> StrOutputParser; StrOutputParser --> conditional_str_parser_output; conditional_str_parser_input --> XMLOutputParser; XMLOutputParser --> conditional_str_parser_output; - Parallel_as_list_as_str_Input --> conditional_str_parser_input; - conditional_str_parser_output --> Parallel_as_list_as_str_Output; - FakeListLLM --> Parallel_as_list_as_str_Input; + Parallel\3cas_list\2cas_str\3eInput --> conditional_str_parser_input; + conditional_str_parser_output --> Parallel\3cas_list\2cas_str\3eOutput; + FakeListLLM --> Parallel\3cas_list\2cas_str\3eInput; ''' # --- @@ -1925,8 +1946,8 @@ PromptInput([PromptInput]):::first PromptTemplate(PromptTemplate) FakeListLLM(FakeListLLM) - Parallel_as_list_as_str_Input(ParallelInput) - Parallel_as_list_as_str_Output([ParallelOutput]):::last + Parallel\3cas_list\2cas_str\3eInput(ParallelInput) + Parallel\3cas_list\2cas_str\3eOutput([ParallelOutput]):::last CommaSeparatedListOutputParser(CommaSeparatedListOutputParser) conditional_str_parser_input(conditional_str_parser_input) conditional_str_parser_output(conditional_str_parser_output) @@ -1934,15 +1955,15 @@ XMLOutputParser(XMLOutputParser) PromptInput --> PromptTemplate; PromptTemplate --> FakeListLLM; - Parallel_as_list_as_str_Input --> CommaSeparatedListOutputParser; - CommaSeparatedListOutputParser --> Parallel_as_list_as_str_Output; + Parallel\3cas_list\2cas_str\3eInput --> CommaSeparatedListOutputParser; + CommaSeparatedListOutputParser --> Parallel\3cas_list\2cas_str\3eOutput; conditional_str_parser_input --> StrOutputParser; StrOutputParser --> conditional_str_parser_output; conditional_str_parser_input --> XMLOutputParser; XMLOutputParser --> conditional_str_parser_output; - Parallel_as_list_as_str_Input --> conditional_str_parser_input; - conditional_str_parser_output --> Parallel_as_list_as_str_Output; - FakeListLLM --> Parallel_as_list_as_str_Input; + Parallel\3cas_list\2cas_str\3eInput --> conditional_str_parser_input; + conditional_str_parser_output --> Parallel\3cas_list\2cas_str\3eOutput; + FakeListLLM --> Parallel\3cas_list\2cas_str\3eInput; classDef default fill:#f2f0ff,line-height:1.2 classDef first fill-opacity:0 classDef last fill:#bfb6fc @@ -2000,20 +2021,20 @@ outer_2(outer_2) __end__([

__end__

]):::last __start__ --> outer_1; - inner_1_inner_2 --> outer_2; - inner_2_inner_2 --> outer_2; - outer_1 --> inner_1_inner_1; - outer_1 --> inner_2_inner_1; + inner_1\3ainner_2 --> outer_2; + inner_2\3ainner_2 --> outer_2; + outer_1 --> inner_1\3ainner_1; + outer_1 --> inner_2\3ainner_1; outer_2 --> __end__; subgraph inner_1 - inner_1_inner_1(inner_1) - inner_1_inner_2(inner_2
__interrupt = before) - inner_1_inner_1 --> inner_1_inner_2; + inner_1\3ainner_1(inner_1) + inner_1\3ainner_2(inner_2
__interrupt = before) + inner_1\3ainner_1 --> inner_1\3ainner_2; end subgraph inner_2 - inner_2_inner_1(inner_1) - inner_2_inner_2(inner_2) - inner_2_inner_1 --> inner_2_inner_2; + inner_2\3ainner_1(inner_1) + inner_2\3ainner_2(inner_2) + inner_2\3ainner_1 --> inner_2\3ainner_2; end classDef default fill:#f2f0ff,line-height:1.2 classDef first fill-opacity:0 @@ -2031,10 +2052,10 @@ graph TD; __start__([

__start__

]):::first __end__([

__end__

]):::last - __start__ --> sub_meow; - sub_meow --> __end__; + __start__ --> sub\3ameow; + sub\3ameow --> __end__; subgraph sub - sub_meow(meow) + sub\3ameow(meow) end classDef default fill:#f2f0ff,line-height:1.2 classDef first fill-opacity:0 @@ -2117,19 +2138,19 @@ parent_2(parent_2) __end__([

__end__

]):::last __start__ --> parent_1; - child_child_2 --> parent_2; - parent_1 --> child_child_1_grandchild_1; + child\3achild_2 --> parent_2; + parent_1 --> child\3achild_1\3agrandchild_1; parent_2 --> __end__; subgraph child - child_child_2(child_2) - child_child_1_grandchild_2 --> child_child_2; + child\3achild_2(child_2) + child\3achild_1\3agrandchild_2 --> child\3achild_2; subgraph child_1 - child_child_1_grandchild_1(grandchild_1) - child_child_1_grandchild_2(grandchild_2
__interrupt = before) - child_child_1_grandchild_1_greatgrandchild --> child_child_1_grandchild_2; + child\3achild_1\3agrandchild_1(grandchild_1) + child\3achild_1\3agrandchild_2(grandchild_2
__interrupt = before) + child\3achild_1\3agrandchild_1\3agreatgrandchild --> child\3achild_1\3agrandchild_2; subgraph grandchild_1 - child_child_1_grandchild_1_greatgrandchild(greatgrandchild) - child_child_1_grandchild_1 --> child_child_1_grandchild_1_greatgrandchild; + child\3achild_1\3agrandchild_1\3agreatgrandchild(greatgrandchild) + child\3achild_1\3agrandchild_1 --> child\3achild_1\3agrandchild_1\3agreatgrandchild; end end end diff --git a/libs/core/tests/unit_tests/runnables/test_graph.py b/libs/core/tests/unit_tests/runnables/test_graph.py index 398ae45e66e..1bbef74e90f 100644 --- a/libs/core/tests/unit_tests/runnables/test_graph.py +++ b/libs/core/tests/unit_tests/runnables/test_graph.py @@ -15,8 +15,8 @@ from langchain_core.runnables import RunnableConfig from langchain_core.runnables.base import Runnable from langchain_core.runnables.graph import Edge, Graph, MermaidDrawMethod, Node from langchain_core.runnables.graph_mermaid import ( - _escape_node_label, _render_mermaid_using_api, + _to_safe_id, draw_mermaid_png, ) from langchain_core.utils.pydantic import PYDANTIC_VERSION @@ -521,12 +521,12 @@ def test_runnable_get_graph_with_invalid_output_type() -> None: runnable.get_graph() -def test_graph_mermaid_escape_node_label() -> None: +def test_graph_mermaid_to_safe_id() -> None: """Test that node labels are correctly preprocessed for draw_mermaid.""" - assert _escape_node_label("foo") == "foo" - assert _escape_node_label("foo-bar") == "foo-bar" - assert _escape_node_label("foo_1") == "foo_1" - assert _escape_node_label("#foo*&!") == "_foo___" + assert _to_safe_id("foo") == "foo" + assert _to_safe_id("foo-bar") == "foo-bar" + assert _to_safe_id("foo_1") == "foo_1" + assert _to_safe_id("#foo*&!") == "\\23foo\\2a\\26\\21" def test_graph_mermaid_duplicate_nodes(snapshot: SnapshotAssertion) -> None: @@ -653,3 +653,24 @@ def test_graph_draw_mermaid_png_base_url() -> None: args, kwargs = mock_get.call_args url = args[0] # First argument to request.get is the URL assert url.startswith(custom_url) + + +def test_graph_mermaid_special_chars(snapshot: SnapshotAssertion) -> None: + graph = Graph( + nodes={ + "__start__": Node( + id="__start__", name="__start__", data=BaseModel, metadata=None + ), + "开始": Node(id="开始", name="开始", data=BaseModel, metadata=None), + "结束": Node(id="结束", name="结束", data=BaseModel, metadata=None), + "__end__": Node( + id="__end__", name="__end__", data=BaseModel, metadata=None + ), + }, + edges=[ + Edge(source="__start__", target="开始", data=None, conditional=False), + Edge(source="开始", target="结束", data=None, conditional=False), + Edge(source="结束", target="__end__", data=None, conditional=False), + ], + ) + assert graph.draw_mermaid() == snapshot(name="mermaid") diff --git a/libs/langchain_v1/tests/unit_tests/agents/__snapshots__/test_middleware_agent.ambr b/libs/langchain_v1/tests/unit_tests/agents/__snapshots__/test_middleware_agent.ambr index c7ab09b722b..f07cc34c1af 100644 --- a/libs/langchain_v1/tests/unit_tests/agents/__snapshots__/test_middleware_agent.ambr +++ b/libs/langchain_v1/tests/unit_tests/agents/__snapshots__/test_middleware_agent.ambr @@ -28,11 +28,11 @@ graph TD; __start__([

__start__

]):::first model_request(model_request) - NoopOne_before_model(NoopOne.before_model) + NoopOne\2ebefore_model(NoopOne.before_model) __end__([

__end__

]):::last - NoopOne_before_model -.-> __end__; - NoopOne_before_model -.-> model_request; - __start__ --> NoopOne_before_model; + NoopOne\2ebefore_model -.-> __end__; + NoopOne\2ebefore_model -.-> model_request; + __start__ --> NoopOne\2ebefore_model; model_request --> __end__; classDef default fill:#f2f0ff,line-height:1.2 classDef first fill-opacity:0 @@ -50,15 +50,15 @@ graph TD; __start__([

__start__

]):::first model_request(model_request) - NoopTen_before_model(NoopTen.before_model) - NoopTen_after_model(NoopTen.after_model) + NoopTen\2ebefore_model(NoopTen.before_model) + NoopTen\2eafter_model(NoopTen.after_model) __end__([

__end__

]):::last - NoopTen_after_model -.-> NoopTen_before_model; - NoopTen_after_model -.-> __end__; - NoopTen_before_model -.-> __end__; - NoopTen_before_model -.-> model_request; - __start__ --> NoopTen_before_model; - model_request --> NoopTen_after_model; + NoopTen\2eafter_model -.-> NoopTen\2ebefore_model; + NoopTen\2eafter_model -.-> __end__; + NoopTen\2ebefore_model -.-> __end__; + NoopTen\2ebefore_model -.-> model_request; + __start__ --> NoopTen\2ebefore_model; + model_request --> NoopTen\2eafter_model; classDef default fill:#f2f0ff,line-height:1.2 classDef first fill-opacity:0 classDef last fill:#bfb6fc @@ -75,23 +75,23 @@ graph TD; __start__([

__start__

]):::first model_request(model_request) - NoopTen_before_model(NoopTen.before_model) - NoopTen_after_model(NoopTen.after_model) - NoopEleven_before_model(NoopEleven.before_model) - NoopEleven_after_model(NoopEleven.after_model) + NoopTen\2ebefore_model(NoopTen.before_model) + NoopTen\2eafter_model(NoopTen.after_model) + NoopEleven\2ebefore_model(NoopEleven.before_model) + NoopEleven\2eafter_model(NoopEleven.after_model) __end__([

__end__

]):::last - NoopEleven_after_model -.-> NoopTen_after_model; - NoopEleven_after_model -.-> NoopTen_before_model; - NoopEleven_after_model -.-> __end__; - NoopEleven_before_model -.-> NoopTen_before_model; - NoopEleven_before_model -.-> __end__; - NoopEleven_before_model -.-> model_request; - NoopTen_after_model -.-> NoopTen_before_model; - NoopTen_after_model -.-> __end__; - NoopTen_before_model -.-> NoopEleven_before_model; - NoopTen_before_model -.-> __end__; - __start__ --> NoopTen_before_model; - model_request --> NoopEleven_after_model; + NoopEleven\2eafter_model -.-> NoopTen\2eafter_model; + NoopEleven\2eafter_model -.-> NoopTen\2ebefore_model; + NoopEleven\2eafter_model -.-> __end__; + NoopEleven\2ebefore_model -.-> NoopTen\2ebefore_model; + NoopEleven\2ebefore_model -.-> __end__; + NoopEleven\2ebefore_model -.-> model_request; + NoopTen\2eafter_model -.-> NoopTen\2ebefore_model; + NoopTen\2eafter_model -.-> __end__; + NoopTen\2ebefore_model -.-> NoopEleven\2ebefore_model; + NoopTen\2ebefore_model -.-> __end__; + __start__ --> NoopTen\2ebefore_model; + model_request --> NoopEleven\2eafter_model; classDef default fill:#f2f0ff,line-height:1.2 classDef first fill-opacity:0 classDef last fill:#bfb6fc @@ -108,15 +108,15 @@ graph TD; __start__([

__start__

]):::first model_request(model_request) - NoopOne_before_model(NoopOne.before_model) - NoopTwo_before_model(NoopTwo.before_model) + NoopOne\2ebefore_model(NoopOne.before_model) + NoopTwo\2ebefore_model(NoopTwo.before_model) __end__([

__end__

]):::last - NoopOne_before_model -.-> NoopTwo_before_model; - NoopOne_before_model -.-> __end__; - NoopTwo_before_model -.-> NoopOne_before_model; - NoopTwo_before_model -.-> __end__; - NoopTwo_before_model -.-> model_request; - __start__ --> NoopOne_before_model; + NoopOne\2ebefore_model -.-> NoopTwo\2ebefore_model; + NoopOne\2ebefore_model -.-> __end__; + NoopTwo\2ebefore_model -.-> NoopOne\2ebefore_model; + NoopTwo\2ebefore_model -.-> __end__; + NoopTwo\2ebefore_model -.-> model_request; + __start__ --> NoopOne\2ebefore_model; model_request --> __end__; classDef default fill:#f2f0ff,line-height:1.2 classDef first fill-opacity:0 @@ -134,19 +134,19 @@ graph TD; __start__([

__start__

]):::first model_request(model_request) - NoopOne_before_model(NoopOne.before_model) - NoopTwo_before_model(NoopTwo.before_model) - NoopThree_before_model(NoopThree.before_model) + NoopOne\2ebefore_model(NoopOne.before_model) + NoopTwo\2ebefore_model(NoopTwo.before_model) + NoopThree\2ebefore_model(NoopThree.before_model) __end__([

__end__

]):::last - NoopOne_before_model -.-> NoopTwo_before_model; - NoopOne_before_model -.-> __end__; - NoopThree_before_model -.-> NoopOne_before_model; - NoopThree_before_model -.-> __end__; - NoopThree_before_model -.-> model_request; - NoopTwo_before_model -.-> NoopOne_before_model; - NoopTwo_before_model -.-> NoopThree_before_model; - NoopTwo_before_model -.-> __end__; - __start__ --> NoopOne_before_model; + NoopOne\2ebefore_model -.-> NoopTwo\2ebefore_model; + NoopOne\2ebefore_model -.-> __end__; + NoopThree\2ebefore_model -.-> NoopOne\2ebefore_model; + NoopThree\2ebefore_model -.-> __end__; + NoopThree\2ebefore_model -.-> model_request; + NoopTwo\2ebefore_model -.-> NoopOne\2ebefore_model; + NoopTwo\2ebefore_model -.-> NoopThree\2ebefore_model; + NoopTwo\2ebefore_model -.-> __end__; + __start__ --> NoopOne\2ebefore_model; model_request --> __end__; classDef default fill:#f2f0ff,line-height:1.2 classDef first fill-opacity:0 @@ -164,12 +164,12 @@ graph TD; __start__([

__start__

]):::first model_request(model_request) - NoopFour_after_model(NoopFour.after_model) + NoopFour\2eafter_model(NoopFour.after_model) __end__([

__end__

]):::last - NoopFour_after_model -.-> __end__; - NoopFour_after_model -.-> model_request; + NoopFour\2eafter_model -.-> __end__; + NoopFour\2eafter_model -.-> model_request; __start__ --> model_request; - model_request --> NoopFour_after_model; + model_request --> NoopFour\2eafter_model; classDef default fill:#f2f0ff,line-height:1.2 classDef first fill-opacity:0 classDef last fill:#bfb6fc @@ -186,16 +186,16 @@ graph TD; __start__([

__start__

]):::first model_request(model_request) - NoopFour_after_model(NoopFour.after_model) - NoopFive_after_model(NoopFive.after_model) + NoopFour\2eafter_model(NoopFour.after_model) + NoopFive\2eafter_model(NoopFive.after_model) __end__([

__end__

]):::last - NoopFive_after_model -.-> NoopFour_after_model; - NoopFive_after_model -.-> __end__; - NoopFive_after_model -.-> model_request; - NoopFour_after_model -.-> __end__; - NoopFour_after_model -.-> model_request; + NoopFive\2eafter_model -.-> NoopFour\2eafter_model; + NoopFive\2eafter_model -.-> __end__; + NoopFive\2eafter_model -.-> model_request; + NoopFour\2eafter_model -.-> __end__; + NoopFour\2eafter_model -.-> model_request; __start__ --> model_request; - model_request --> NoopFive_after_model; + model_request --> NoopFive\2eafter_model; classDef default fill:#f2f0ff,line-height:1.2 classDef first fill-opacity:0 classDef last fill:#bfb6fc @@ -212,20 +212,20 @@ graph TD; __start__([

__start__

]):::first model_request(model_request) - NoopFour_after_model(NoopFour.after_model) - NoopFive_after_model(NoopFive.after_model) - NoopSix_after_model(NoopSix.after_model) + NoopFour\2eafter_model(NoopFour.after_model) + NoopFive\2eafter_model(NoopFive.after_model) + NoopSix\2eafter_model(NoopSix.after_model) __end__([

__end__

]):::last - NoopFive_after_model -.-> NoopFour_after_model; - NoopFive_after_model -.-> __end__; - NoopFive_after_model -.-> model_request; - NoopFour_after_model -.-> __end__; - NoopFour_after_model -.-> model_request; - NoopSix_after_model -.-> NoopFive_after_model; - NoopSix_after_model -.-> __end__; - NoopSix_after_model -.-> model_request; + NoopFive\2eafter_model -.-> NoopFour\2eafter_model; + NoopFive\2eafter_model -.-> __end__; + NoopFive\2eafter_model -.-> model_request; + NoopFour\2eafter_model -.-> __end__; + NoopFour\2eafter_model -.-> model_request; + NoopSix\2eafter_model -.-> NoopFive\2eafter_model; + NoopSix\2eafter_model -.-> __end__; + NoopSix\2eafter_model -.-> model_request; __start__ --> model_request; - model_request --> NoopSix_after_model; + model_request --> NoopSix\2eafter_model; classDef default fill:#f2f0ff,line-height:1.2 classDef first fill-opacity:0 classDef last fill:#bfb6fc @@ -242,15 +242,15 @@ graph TD; __start__([

__start__

]):::first model_request(model_request) - NoopSeven_before_model(NoopSeven.before_model) - NoopSeven_after_model(NoopSeven.after_model) + NoopSeven\2ebefore_model(NoopSeven.before_model) + NoopSeven\2eafter_model(NoopSeven.after_model) __end__([

__end__

]):::last - NoopSeven_after_model -.-> NoopSeven_before_model; - NoopSeven_after_model -.-> __end__; - NoopSeven_before_model -.-> __end__; - NoopSeven_before_model -.-> model_request; - __start__ --> NoopSeven_before_model; - model_request --> NoopSeven_after_model; + NoopSeven\2eafter_model -.-> NoopSeven\2ebefore_model; + NoopSeven\2eafter_model -.-> __end__; + NoopSeven\2ebefore_model -.-> __end__; + NoopSeven\2ebefore_model -.-> model_request; + __start__ --> NoopSeven\2ebefore_model; + model_request --> NoopSeven\2eafter_model; classDef default fill:#f2f0ff,line-height:1.2 classDef first fill-opacity:0 classDef last fill:#bfb6fc @@ -267,23 +267,23 @@ graph TD; __start__([

__start__

]):::first model_request(model_request) - NoopSeven_before_model(NoopSeven.before_model) - NoopSeven_after_model(NoopSeven.after_model) - NoopEight_before_model(NoopEight.before_model) - NoopEight_after_model(NoopEight.after_model) + NoopSeven\2ebefore_model(NoopSeven.before_model) + NoopSeven\2eafter_model(NoopSeven.after_model) + NoopEight\2ebefore_model(NoopEight.before_model) + NoopEight\2eafter_model(NoopEight.after_model) __end__([

__end__

]):::last - NoopEight_after_model -.-> NoopSeven_after_model; - NoopEight_after_model -.-> NoopSeven_before_model; - NoopEight_after_model -.-> __end__; - NoopEight_before_model -.-> NoopSeven_before_model; - NoopEight_before_model -.-> __end__; - NoopEight_before_model -.-> model_request; - NoopSeven_after_model -.-> NoopSeven_before_model; - NoopSeven_after_model -.-> __end__; - NoopSeven_before_model -.-> NoopEight_before_model; - NoopSeven_before_model -.-> __end__; - __start__ --> NoopSeven_before_model; - model_request --> NoopEight_after_model; + NoopEight\2eafter_model -.-> NoopSeven\2eafter_model; + NoopEight\2eafter_model -.-> NoopSeven\2ebefore_model; + NoopEight\2eafter_model -.-> __end__; + NoopEight\2ebefore_model -.-> NoopSeven\2ebefore_model; + NoopEight\2ebefore_model -.-> __end__; + NoopEight\2ebefore_model -.-> model_request; + NoopSeven\2eafter_model -.-> NoopSeven\2ebefore_model; + NoopSeven\2eafter_model -.-> __end__; + NoopSeven\2ebefore_model -.-> NoopEight\2ebefore_model; + NoopSeven\2ebefore_model -.-> __end__; + __start__ --> NoopSeven\2ebefore_model; + model_request --> NoopEight\2eafter_model; classDef default fill:#f2f0ff,line-height:1.2 classDef first fill-opacity:0 classDef last fill:#bfb6fc @@ -300,31 +300,31 @@ graph TD; __start__([

__start__

]):::first model_request(model_request) - NoopSeven_before_model(NoopSeven.before_model) - NoopSeven_after_model(NoopSeven.after_model) - NoopEight_before_model(NoopEight.before_model) - NoopEight_after_model(NoopEight.after_model) - NoopNine_before_model(NoopNine.before_model) - NoopNine_after_model(NoopNine.after_model) + NoopSeven\2ebefore_model(NoopSeven.before_model) + NoopSeven\2eafter_model(NoopSeven.after_model) + NoopEight\2ebefore_model(NoopEight.before_model) + NoopEight\2eafter_model(NoopEight.after_model) + NoopNine\2ebefore_model(NoopNine.before_model) + NoopNine\2eafter_model(NoopNine.after_model) __end__([

__end__

]):::last - NoopEight_after_model -.-> NoopSeven_after_model; - NoopEight_after_model -.-> NoopSeven_before_model; - NoopEight_after_model -.-> __end__; - NoopEight_before_model -.-> NoopNine_before_model; - NoopEight_before_model -.-> NoopSeven_before_model; - NoopEight_before_model -.-> __end__; - NoopNine_after_model -.-> NoopEight_after_model; - NoopNine_after_model -.-> NoopSeven_before_model; - NoopNine_after_model -.-> __end__; - NoopNine_before_model -.-> NoopSeven_before_model; - NoopNine_before_model -.-> __end__; - NoopNine_before_model -.-> model_request; - NoopSeven_after_model -.-> NoopSeven_before_model; - NoopSeven_after_model -.-> __end__; - NoopSeven_before_model -.-> NoopEight_before_model; - NoopSeven_before_model -.-> __end__; - __start__ --> NoopSeven_before_model; - model_request --> NoopNine_after_model; + NoopEight\2eafter_model -.-> NoopSeven\2eafter_model; + NoopEight\2eafter_model -.-> NoopSeven\2ebefore_model; + NoopEight\2eafter_model -.-> __end__; + NoopEight\2ebefore_model -.-> NoopNine\2ebefore_model; + NoopEight\2ebefore_model -.-> NoopSeven\2ebefore_model; + NoopEight\2ebefore_model -.-> __end__; + NoopNine\2eafter_model -.-> NoopEight\2eafter_model; + NoopNine\2eafter_model -.-> NoopSeven\2ebefore_model; + NoopNine\2eafter_model -.-> __end__; + NoopNine\2ebefore_model -.-> NoopSeven\2ebefore_model; + NoopNine\2ebefore_model -.-> __end__; + NoopNine\2ebefore_model -.-> model_request; + NoopSeven\2eafter_model -.-> NoopSeven\2ebefore_model; + NoopSeven\2eafter_model -.-> __end__; + NoopSeven\2ebefore_model -.-> NoopEight\2ebefore_model; + NoopSeven\2ebefore_model -.-> __end__; + __start__ --> NoopSeven\2ebefore_model; + model_request --> NoopNine\2eafter_model; classDef default fill:#f2f0ff,line-height:1.2 classDef first fill-opacity:0 classDef last fill:#bfb6fc @@ -342,28 +342,28 @@ __start__([

__start__

]):::first model_request(model_request) tools(tools) - NoopSeven_before_model(NoopSeven.before_model) - NoopSeven_after_model(NoopSeven.after_model) - NoopEight_before_model(NoopEight.before_model) - NoopEight_after_model(NoopEight.after_model) + NoopSeven\2ebefore_model(NoopSeven.before_model) + NoopSeven\2eafter_model(NoopSeven.after_model) + NoopEight\2ebefore_model(NoopEight.before_model) + NoopEight\2eafter_model(NoopEight.after_model) __end__([

__end__

]):::last - NoopEight_after_model -.-> NoopSeven_after_model; - NoopEight_after_model -.-> NoopSeven_before_model; - NoopEight_after_model -.-> __end__; - NoopEight_after_model -.-> tools; - NoopEight_before_model -.-> NoopSeven_before_model; - NoopEight_before_model -.-> __end__; - NoopEight_before_model -.-> model_request; - NoopEight_before_model -.-> tools; - NoopSeven_after_model -.-> NoopSeven_before_model; - NoopSeven_after_model -.-> __end__; - NoopSeven_after_model -.-> tools; - NoopSeven_before_model -.-> NoopEight_before_model; - NoopSeven_before_model -.-> __end__; - NoopSeven_before_model -.-> tools; - __start__ --> NoopSeven_before_model; - model_request --> NoopEight_after_model; - tools -.-> NoopSeven_before_model; + NoopEight\2eafter_model -.-> NoopSeven\2eafter_model; + NoopEight\2eafter_model -.-> NoopSeven\2ebefore_model; + NoopEight\2eafter_model -.-> __end__; + NoopEight\2eafter_model -.-> tools; + NoopEight\2ebefore_model -.-> NoopSeven\2ebefore_model; + NoopEight\2ebefore_model -.-> __end__; + NoopEight\2ebefore_model -.-> model_request; + NoopEight\2ebefore_model -.-> tools; + NoopSeven\2eafter_model -.-> NoopSeven\2ebefore_model; + NoopSeven\2eafter_model -.-> __end__; + NoopSeven\2eafter_model -.-> tools; + NoopSeven\2ebefore_model -.-> NoopEight\2ebefore_model; + NoopSeven\2ebefore_model -.-> __end__; + NoopSeven\2ebefore_model -.-> tools; + __start__ --> NoopSeven\2ebefore_model; + model_request --> NoopEight\2eafter_model; + tools -.-> NoopSeven\2ebefore_model; tools -.-> __end__; classDef default fill:#f2f0ff,line-height:1.2 classDef first fill-opacity:0 @@ -382,28 +382,28 @@ __start__([

__start__

]):::first model_request(model_request) tools(tools) - NoopSeven_before_model(NoopSeven.before_model) - NoopSeven_after_model(NoopSeven.after_model) - NoopEight_before_model(NoopEight.before_model) - NoopEight_after_model(NoopEight.after_model) + NoopSeven\2ebefore_model(NoopSeven.before_model) + NoopSeven\2eafter_model(NoopSeven.after_model) + NoopEight\2ebefore_model(NoopEight.before_model) + NoopEight\2eafter_model(NoopEight.after_model) __end__([

__end__

]):::last - NoopEight_after_model -.-> NoopSeven_after_model; - NoopEight_after_model -.-> NoopSeven_before_model; - NoopEight_after_model -.-> __end__; - NoopEight_after_model -.-> tools; - NoopEight_before_model -.-> NoopSeven_before_model; - NoopEight_before_model -.-> __end__; - NoopEight_before_model -.-> model_request; - NoopEight_before_model -.-> tools; - NoopSeven_after_model -.-> NoopSeven_before_model; - NoopSeven_after_model -.-> __end__; - NoopSeven_after_model -.-> tools; - NoopSeven_before_model -.-> NoopEight_before_model; - NoopSeven_before_model -.-> __end__; - NoopSeven_before_model -.-> tools; - __start__ --> NoopSeven_before_model; - model_request --> NoopEight_after_model; - tools -.-> NoopSeven_before_model; + NoopEight\2eafter_model -.-> NoopSeven\2eafter_model; + NoopEight\2eafter_model -.-> NoopSeven\2ebefore_model; + NoopEight\2eafter_model -.-> __end__; + NoopEight\2eafter_model -.-> tools; + NoopEight\2ebefore_model -.-> NoopSeven\2ebefore_model; + NoopEight\2ebefore_model -.-> __end__; + NoopEight\2ebefore_model -.-> model_request; + NoopEight\2ebefore_model -.-> tools; + NoopSeven\2eafter_model -.-> NoopSeven\2ebefore_model; + NoopSeven\2eafter_model -.-> __end__; + NoopSeven\2eafter_model -.-> tools; + NoopSeven\2ebefore_model -.-> NoopEight\2ebefore_model; + NoopSeven\2ebefore_model -.-> __end__; + NoopSeven\2ebefore_model -.-> tools; + __start__ --> NoopSeven\2ebefore_model; + model_request --> NoopEight\2eafter_model; + tools -.-> NoopSeven\2ebefore_model; tools -.-> __end__; classDef default fill:#f2f0ff,line-height:1.2 classDef first fill-opacity:0 @@ -422,28 +422,28 @@ __start__([

__start__

]):::first model_request(model_request) tools(tools) - NoopSeven_before_model(NoopSeven.before_model) - NoopSeven_after_model(NoopSeven.after_model) - NoopEight_before_model(NoopEight.before_model) - NoopEight_after_model(NoopEight.after_model) + NoopSeven\2ebefore_model(NoopSeven.before_model) + NoopSeven\2eafter_model(NoopSeven.after_model) + NoopEight\2ebefore_model(NoopEight.before_model) + NoopEight\2eafter_model(NoopEight.after_model) __end__([

__end__

]):::last - NoopEight_after_model -.-> NoopSeven_after_model; - NoopEight_after_model -.-> NoopSeven_before_model; - NoopEight_after_model -.-> __end__; - NoopEight_after_model -.-> tools; - NoopEight_before_model -.-> NoopSeven_before_model; - NoopEight_before_model -.-> __end__; - NoopEight_before_model -.-> model_request; - NoopEight_before_model -.-> tools; - NoopSeven_after_model -.-> NoopSeven_before_model; - NoopSeven_after_model -.-> __end__; - NoopSeven_after_model -.-> tools; - NoopSeven_before_model -.-> NoopEight_before_model; - NoopSeven_before_model -.-> __end__; - NoopSeven_before_model -.-> tools; - __start__ --> NoopSeven_before_model; - model_request --> NoopEight_after_model; - tools -.-> NoopSeven_before_model; + NoopEight\2eafter_model -.-> NoopSeven\2eafter_model; + NoopEight\2eafter_model -.-> NoopSeven\2ebefore_model; + NoopEight\2eafter_model -.-> __end__; + NoopEight\2eafter_model -.-> tools; + NoopEight\2ebefore_model -.-> NoopSeven\2ebefore_model; + NoopEight\2ebefore_model -.-> __end__; + NoopEight\2ebefore_model -.-> model_request; + NoopEight\2ebefore_model -.-> tools; + NoopSeven\2eafter_model -.-> NoopSeven\2ebefore_model; + NoopSeven\2eafter_model -.-> __end__; + NoopSeven\2eafter_model -.-> tools; + NoopSeven\2ebefore_model -.-> NoopEight\2ebefore_model; + NoopSeven\2ebefore_model -.-> __end__; + NoopSeven\2ebefore_model -.-> tools; + __start__ --> NoopSeven\2ebefore_model; + model_request --> NoopEight\2eafter_model; + tools -.-> NoopSeven\2ebefore_model; tools -.-> __end__; classDef default fill:#f2f0ff,line-height:1.2 classDef first fill-opacity:0 @@ -462,28 +462,28 @@ __start__([

__start__

]):::first model_request(model_request) tools(tools) - NoopSeven_before_model(NoopSeven.before_model) - NoopSeven_after_model(NoopSeven.after_model) - NoopEight_before_model(NoopEight.before_model) - NoopEight_after_model(NoopEight.after_model) + NoopSeven\2ebefore_model(NoopSeven.before_model) + NoopSeven\2eafter_model(NoopSeven.after_model) + NoopEight\2ebefore_model(NoopEight.before_model) + NoopEight\2eafter_model(NoopEight.after_model) __end__([

__end__

]):::last - NoopEight_after_model -.-> NoopSeven_after_model; - NoopEight_after_model -.-> NoopSeven_before_model; - NoopEight_after_model -.-> __end__; - NoopEight_after_model -.-> tools; - NoopEight_before_model -.-> NoopSeven_before_model; - NoopEight_before_model -.-> __end__; - NoopEight_before_model -.-> model_request; - NoopEight_before_model -.-> tools; - NoopSeven_after_model -.-> NoopSeven_before_model; - NoopSeven_after_model -.-> __end__; - NoopSeven_after_model -.-> tools; - NoopSeven_before_model -.-> NoopEight_before_model; - NoopSeven_before_model -.-> __end__; - NoopSeven_before_model -.-> tools; - __start__ --> NoopSeven_before_model; - model_request --> NoopEight_after_model; - tools -.-> NoopSeven_before_model; + NoopEight\2eafter_model -.-> NoopSeven\2eafter_model; + NoopEight\2eafter_model -.-> NoopSeven\2ebefore_model; + NoopEight\2eafter_model -.-> __end__; + NoopEight\2eafter_model -.-> tools; + NoopEight\2ebefore_model -.-> NoopSeven\2ebefore_model; + NoopEight\2ebefore_model -.-> __end__; + NoopEight\2ebefore_model -.-> model_request; + NoopEight\2ebefore_model -.-> tools; + NoopSeven\2eafter_model -.-> NoopSeven\2ebefore_model; + NoopSeven\2eafter_model -.-> __end__; + NoopSeven\2eafter_model -.-> tools; + NoopSeven\2ebefore_model -.-> NoopEight\2ebefore_model; + NoopSeven\2ebefore_model -.-> __end__; + NoopSeven\2ebefore_model -.-> tools; + __start__ --> NoopSeven\2ebefore_model; + model_request --> NoopEight\2eafter_model; + tools -.-> NoopSeven\2ebefore_model; tools -.-> __end__; classDef default fill:#f2f0ff,line-height:1.2 classDef first fill-opacity:0 @@ -502,28 +502,28 @@ __start__([

__start__

]):::first model_request(model_request) tools(tools) - NoopSeven_before_model(NoopSeven.before_model) - NoopSeven_after_model(NoopSeven.after_model) - NoopEight_before_model(NoopEight.before_model) - NoopEight_after_model(NoopEight.after_model) + NoopSeven\2ebefore_model(NoopSeven.before_model) + NoopSeven\2eafter_model(NoopSeven.after_model) + NoopEight\2ebefore_model(NoopEight.before_model) + NoopEight\2eafter_model(NoopEight.after_model) __end__([

__end__

]):::last - NoopEight_after_model -.-> NoopSeven_after_model; - NoopEight_after_model -.-> NoopSeven_before_model; - NoopEight_after_model -.-> __end__; - NoopEight_after_model -.-> tools; - NoopEight_before_model -.-> NoopSeven_before_model; - NoopEight_before_model -.-> __end__; - NoopEight_before_model -.-> model_request; - NoopEight_before_model -.-> tools; - NoopSeven_after_model -.-> NoopSeven_before_model; - NoopSeven_after_model -.-> __end__; - NoopSeven_after_model -.-> tools; - NoopSeven_before_model -.-> NoopEight_before_model; - NoopSeven_before_model -.-> __end__; - NoopSeven_before_model -.-> tools; - __start__ --> NoopSeven_before_model; - model_request --> NoopEight_after_model; - tools -.-> NoopSeven_before_model; + NoopEight\2eafter_model -.-> NoopSeven\2eafter_model; + NoopEight\2eafter_model -.-> NoopSeven\2ebefore_model; + NoopEight\2eafter_model -.-> __end__; + NoopEight\2eafter_model -.-> tools; + NoopEight\2ebefore_model -.-> NoopSeven\2ebefore_model; + NoopEight\2ebefore_model -.-> __end__; + NoopEight\2ebefore_model -.-> model_request; + NoopEight\2ebefore_model -.-> tools; + NoopSeven\2eafter_model -.-> NoopSeven\2ebefore_model; + NoopSeven\2eafter_model -.-> __end__; + NoopSeven\2eafter_model -.-> tools; + NoopSeven\2ebefore_model -.-> NoopEight\2ebefore_model; + NoopSeven\2ebefore_model -.-> __end__; + NoopSeven\2ebefore_model -.-> tools; + __start__ --> NoopSeven\2ebefore_model; + model_request --> NoopEight\2eafter_model; + tools -.-> NoopSeven\2ebefore_model; tools -.-> __end__; classDef default fill:#f2f0ff,line-height:1.2 classDef first fill-opacity:0