From 5865c6fbde22c71abb4c8be90f7374a0a8b03c0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bravo?= <123977407+MartinEBravo@users.noreply.github.com> Date: Thu, 13 Mar 2025 18:33:08 +0100 Subject: [PATCH 01/31] Add graphviz as a dependency in pyproject.toml --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 262ce17c..116671f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,6 +15,7 @@ dependencies = [ "typing-extensions>=4.12.2, <5", "requests>=2.0, <3", "types-requests>=2.0, <3", + "graphviz>=0.17", ] classifiers = [ "Typing :: Typed", From cecdcd0af4e767d90492a21b7052eae56307080c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bravo?= <123977407+MartinEBravo@users.noreply.github.com> Date: Thu, 13 Mar 2025 18:33:12 +0100 Subject: [PATCH 02/31] Add visualization functions for agents using Graphviz --- src/agents/visualizations.py | 113 +++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 src/agents/visualizations.py diff --git a/src/agents/visualizations.py b/src/agents/visualizations.py new file mode 100644 index 00000000..934647f0 --- /dev/null +++ b/src/agents/visualizations.py @@ -0,0 +1,113 @@ +import graphviz + +from src.agents.agent import Agent + + +def get_main_graph(agent: Agent) -> str: + """ + Generates the main graph structure in DOT format for the given agent. + + Args: + agent (Agent): The agent for which the graph is to be generated. + + Returns: + str: The DOT format string representing the graph. + """ + parts = [""" + digraph G { + graph [splines=true]; + node [fontname="Arial"]; + edge [penwidth=1.5]; + + "__start__" [shape=ellipse, style=filled, fillcolor=lightblue]; + "__end__" [shape=ellipse, style=filled, fillcolor=lightblue]; + """] + parts.append(get_all_nodes(agent)) + parts.append(get_all_edges(agent)) + parts.append("}") + return "".join(parts) + + +def get_all_nodes(agent: Agent, parent: Agent = None) -> str: + """ + Recursively generates the nodes for the given agent and its handoffs in DOT format. + + Args: + agent (Agent): The agent for which the nodes are to be generated. + + Returns: + str: The DOT format string representing the nodes. + """ + parts = [] + + # Ensure parent agent node is colored + if not parent: + parts.append(f""" + "{agent.name}" [label="{agent.name}", shape=box, style=filled, fillcolor=lightyellow, width=1.5, height=0.8];""") + + # Smaller tools (ellipse, green) + for tool in agent.tools: + parts.append(f""" + "{tool.name}" [label="{tool.name}", shape=ellipse, style=filled, fillcolor=lightgreen, width=0.5, height=0.3];""") + + # Bigger handoffs (rounded box, yellow) + for handoff in agent.handoffs: + parts.append(f""" + "{handoff.name}" [label="{handoff.name}", shape=box, style=filled, style=rounded, fillcolor=lightyellow, width=1.5, height=0.8];""") + parts.append(get_all_nodes(handoff)) + + return "".join(parts) + + +def get_all_edges(agent: Agent, parent: Agent = None) -> str: + """ + Recursively generates the edges for the given agent and its handoffs in DOT format. + + Args: + agent (Agent): The agent for which the edges are to be generated. + parent (Agent, optional): The parent agent. Defaults to None. + + Returns: + str: The DOT format string representing the edges. + """ + parts = [] + + if not parent: + parts.append(f""" + "__start__" -> "{agent.name}";""") + + for tool in agent.tools: + parts.append(f""" + "{agent.name}" -> "{tool.name}" [style=dotted, penwidth=1.5]; + "{tool.name}" -> "{agent.name}" [style=dotted, penwidth=1.5];""") + + if not agent.handoffs: + parts.append(f""" + "{agent.name}" -> "__end__";""") + + for handoff in agent.handoffs: + parts.append(f""" + "{agent.name}" -> "{handoff.name}";""") + parts.append(get_all_edges(handoff, agent)) + + return "".join(parts) + + +def draw_graph(agent: Agent, filename: str = None) -> graphviz.Source: + """ + Draws the graph for the given agent and optionally saves it as a PNG file. + + Args: + agent (Agent): The agent for which the graph is to be drawn. + filename (str): The name of the file to save the graph as a PNG. + + Returns: + graphviz.Source: The graphviz Source object representing the graph. + """ + dot_code = get_main_graph(agent) + graph = graphviz.Source(dot_code) + + if filename: + graph.render(filename, format='png') + + return graph \ No newline at end of file From 9b972b33fa3ddd301fb639ebf39f9be04abd8ec2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bravo?= <123977407+MartinEBravo@users.noreply.github.com> Date: Thu, 13 Mar 2025 18:33:17 +0100 Subject: [PATCH 03/31] Add unit tests for visualization functions in test_visualizations.py --- tests/test_visualizations.py | 68 ++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 tests/test_visualizations.py diff --git a/tests/test_visualizations.py b/tests/test_visualizations.py new file mode 100644 index 00000000..0062f85b --- /dev/null +++ b/tests/test_visualizations.py @@ -0,0 +1,68 @@ +import pytest +from unittest.mock import Mock +from src.agents.visualizations import get_main_graph, get_all_nodes, get_all_edges, draw_graph +from src.agents.agent import Agent +import graphviz + +@pytest.fixture +def mock_agent(): + tool1 = Mock() + tool1.name = "Tool1" + tool2 = Mock() + tool2.name = "Tool2" + + handoff1 = Mock() + handoff1.name = "Handoff1" + handoff1.tools = [] + handoff1.handoffs = [] + + agent = Mock(spec=Agent) + agent.name = "Agent1" + agent.tools = [tool1, tool2] + agent.handoffs = [handoff1] + + return agent + +def test_get_main_graph(mock_agent): + result = get_main_graph(mock_agent) + assert "digraph G" in result + assert 'graph [splines=true];' in result + assert 'node [fontname="Arial"];' in result + assert 'edge [penwidth=1.5];' in result + assert '"__start__" [shape=ellipse, style=filled, fillcolor=lightblue];' in result + assert '"__end__" [shape=ellipse, style=filled, fillcolor=lightblue];' in result + assert '"Agent1" [label="Agent1", shape=box, style=filled, fillcolor=lightyellow, width=1.5, height=0.8];' in result + assert '"Tool1" [label="Tool1", shape=ellipse, style=filled, fillcolor=lightgreen, width=0.5, height=0.3];' in result + assert '"Tool2" [label="Tool2", shape=ellipse, style=filled, fillcolor=lightgreen, width=0.5, height=0.3];' in result + assert '"Handoff1" [label="Handoff1", shape=box, style=filled, style=rounded, fillcolor=lightyellow, width=1.5, height=0.8];' in result + +def test_get_all_nodes(mock_agent): + result = get_all_nodes(mock_agent) + assert '"Agent1" [label="Agent1", shape=box, style=filled, fillcolor=lightyellow, width=1.5, height=0.8];' in result + assert '"Tool1" [label="Tool1", shape=ellipse, style=filled, fillcolor=lightgreen, width=0.5, height=0.3];' in result + assert '"Tool2" [label="Tool2", shape=ellipse, style=filled, fillcolor=lightgreen, width=0.5, height=0.3];' in result + assert '"Handoff1" [label="Handoff1", shape=box, style=filled, style=rounded, fillcolor=lightyellow, width=1.5, height=0.8];' in result + +def test_get_all_edges(mock_agent): + result = get_all_edges(mock_agent) + assert '"__start__" -> "Agent1";' in result + assert '"Agent1" -> "Tool1" [style=dotted, penwidth=1.5];' in result + assert '"Tool1" -> "Agent1" [style=dotted, penwidth=1.5];' in result + assert '"Agent1" -> "Tool2" [style=dotted, penwidth=1.5];' in result + assert '"Tool2" -> "Agent1" [style=dotted, penwidth=1.5];' in result + assert '"Agent1" -> "Handoff1";' in result + assert '"Handoff1" -> "__end__";' in result + +def test_draw_graph(mock_agent): + graph = draw_graph(mock_agent) + assert isinstance(graph, graphviz.Source) + assert "digraph G" in graph.source + assert 'graph [splines=true];' in graph.source + assert 'node [fontname="Arial"];' in graph.source + assert 'edge [penwidth=1.5];' in graph.source + assert '"__start__" [shape=ellipse, style=filled, fillcolor=lightblue];' in graph.source + assert '"__end__" [shape=ellipse, style=filled, fillcolor=lightblue];' in graph.source + assert '"Agent1" [label="Agent1", shape=box, style=filled, fillcolor=lightyellow, width=1.5, height=0.8];' in graph.source + assert '"Tool1" [label="Tool1", shape=ellipse, style=filled, fillcolor=lightgreen, width=0.5, height=0.3];' in graph.source + assert '"Tool2" [label="Tool2", shape=ellipse, style=filled, fillcolor=lightgreen, width=0.5, height=0.3];' in graph.source + assert '"Handoff1" [label="Handoff1", shape=box, style=filled, style=rounded, fillcolor=lightyellow, width=1.5, height=0.8];' in graph.source From 2993d26d58d3bee7c01fbebb62f48eb3d3068c5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bravo?= <123977407+MartinEBravo@users.noreply.github.com> Date: Thu, 13 Mar 2025 18:48:12 +0100 Subject: [PATCH 04/31] Add documentation and example for agent visualization using Graphviz --- docs/assets/images/graph.png | Bin 0 -> 95039 bytes docs/visualizations.md | 106 +++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 docs/assets/images/graph.png create mode 100644 docs/visualizations.md diff --git a/docs/assets/images/graph.png b/docs/assets/images/graph.png new file mode 100644 index 0000000000000000000000000000000000000000..13e2d6eb4fedc41781631260364386b20d5cc5d6 GIT binary patch literal 95039 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 literal 0 HcmV?d00001 diff --git a/docs/visualizations.md b/docs/visualizations.md new file mode 100644 index 00000000..2a8bb288 --- /dev/null +++ b/docs/visualizations.md @@ -0,0 +1,106 @@ +# Agent Visualization + +Agent visualization allows you to generate a structured graphical representation of agents and their relationships using **Graphviz**. This is useful for understanding how agents, tools, and handoffs interact within an application. + +## Installation + +The visualization functionality relies on the **Graphviz** package. To use it, ensure you have Graphviz installed and add it as a dependency in `pyproject.toml`. Alternatively, install it directly via pip: + +```bash +pip install graphviz +``` + +## Generating a Graph + +You can generate an agent visualization using the `draw_graph` function. This function creates a directed graph where: + +- **Agents** are represented as yellow boxes. +- **Tools** are represented as green ellipses. +- **Handoffs** are directed edges from one agent to another. + +### Example Usage + +```python +from agents import Agent, function_tool +from agents.visualizations import draw_graph + +@function_tool +def get_weather(city: str) -> str: + return f"The weather in {city} is sunny." + +spanish_agent = Agent( + name="Spanish agent", + instructions="You only speak Spanish.", +) + +english_agent = Agent( + name="English agent", + instructions="You only speak English", +) + +triage_agent = Agent( + name="Triage agent", + instructions="Handoff to the appropriate agent based on the language of the request.", + handoffs=[spanish_agent, english_agent], + tools=[get_weather], +) + +draw_graph(triage_agent) +``` + + + +This generates a graph that visually represents the structure of the **triage agent** and its connections to sub-agents and tools. + + +## Understanding the Visualization + +The generated graph includes: + +- A **start node** (`__start__`) indicating the entry point. +- Agents represented as **rectangles** with yellow fill. +- Tools represented as **ellipses** with green fill. +- Directed edges indicating interactions: + - **Solid arrows** for agent-to-agent handoffs. + - **Dotted arrows** for tool invocations. +- An **end node** (`__end__`) indicating where execution terminates. + +## Customizing the Graph + +### Showing the Graph +By default, `draw_graph` displays the graph inline. To show the graph in a separate window, write the following: + +```python +draw_graph(triage_agent).view() +``` + +### Saving the Graph +By default, `draw_graph` displays the graph inline. To save it as a file, specify a filename: + +```python +draw_graph(triage_agent, filename="agent_graph.png") +``` + +This will generate `agent_graph.png` in the working directory. + +## Testing the Visualization + +The visualization functionality includes test coverage to ensure correctness. Tests are located in `tests/test_visualizations.py` and verify: + +- Node and edge correctness in `get_main_graph()`. +- Proper agent and tool representation in `get_all_nodes()`. +- Accurate relationship mapping in `get_all_edges()`. +- Graph rendering functionality in `draw_graph()`. + +Run tests using: + +```bash +pytest tests/test_visualizations.py +``` + +## Conclusion + +Agent visualization provides a powerful way to **understand, debug, and communicate** how agents interact within an application. By leveraging **Graphviz**, you can generate intuitive visual representations of complex agent structures effortlessly. + +For further details on agent functionality, see the [Agents documentation](agents.md). + From 29e9983ae80a425899b183a75881b7d9d63e6c7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bravo?= <123977407+MartinEBravo@users.noreply.github.com> Date: Thu, 13 Mar 2025 18:49:12 +0100 Subject: [PATCH 05/31] Linting --- src/agents/visualizations.py | 32 +++++++------- tests/test_visualizations.py | 85 +++++++++++++++++++++++++++--------- 2 files changed, 81 insertions(+), 36 deletions(-) diff --git a/src/agents/visualizations.py b/src/agents/visualizations.py index 934647f0..42019d4d 100644 --- a/src/agents/visualizations.py +++ b/src/agents/visualizations.py @@ -13,7 +13,8 @@ def get_main_graph(agent: Agent) -> str: Returns: str: The DOT format string representing the graph. """ - parts = [""" + parts = [ + """ digraph G { graph [splines=true]; node [fontname="Arial"]; @@ -21,7 +22,8 @@ def get_main_graph(agent: Agent) -> str: "__start__" [shape=ellipse, style=filled, fillcolor=lightblue]; "__end__" [shape=ellipse, style=filled, fillcolor=lightblue]; - """] + """ + ] parts.append(get_all_nodes(agent)) parts.append(get_all_edges(agent)) parts.append("}") @@ -39,23 +41,23 @@ def get_all_nodes(agent: Agent, parent: Agent = None) -> str: str: The DOT format string representing the nodes. """ parts = [] - + # Ensure parent agent node is colored if not parent: parts.append(f""" "{agent.name}" [label="{agent.name}", shape=box, style=filled, fillcolor=lightyellow, width=1.5, height=0.8];""") - + # Smaller tools (ellipse, green) for tool in agent.tools: parts.append(f""" "{tool.name}" [label="{tool.name}", shape=ellipse, style=filled, fillcolor=lightgreen, width=0.5, height=0.3];""") - + # Bigger handoffs (rounded box, yellow) for handoff in agent.handoffs: parts.append(f""" "{handoff.name}" [label="{handoff.name}", shape=box, style=filled, style=rounded, fillcolor=lightyellow, width=1.5, height=0.8];""") parts.append(get_all_nodes(handoff)) - + return "".join(parts) @@ -71,25 +73,25 @@ def get_all_edges(agent: Agent, parent: Agent = None) -> str: str: The DOT format string representing the edges. """ parts = [] - + if not parent: parts.append(f""" "__start__" -> "{agent.name}";""") - + for tool in agent.tools: parts.append(f""" "{agent.name}" -> "{tool.name}" [style=dotted, penwidth=1.5]; "{tool.name}" -> "{agent.name}" [style=dotted, penwidth=1.5];""") - + if not agent.handoffs: parts.append(f""" "{agent.name}" -> "__end__";""") - + for handoff in agent.handoffs: parts.append(f""" "{agent.name}" -> "{handoff.name}";""") parts.append(get_all_edges(handoff, agent)) - + return "".join(parts) @@ -106,8 +108,8 @@ def draw_graph(agent: Agent, filename: str = None) -> graphviz.Source: """ dot_code = get_main_graph(agent) graph = graphviz.Source(dot_code) - + if filename: - graph.render(filename, format='png') - - return graph \ No newline at end of file + graph.render(filename, format="png") + + return graph diff --git a/tests/test_visualizations.py b/tests/test_visualizations.py index 0062f85b..d8139780 100644 --- a/tests/test_visualizations.py +++ b/tests/test_visualizations.py @@ -1,8 +1,11 @@ -import pytest from unittest.mock import Mock -from src.agents.visualizations import get_main_graph, get_all_nodes, get_all_edges, draw_graph -from src.agents.agent import Agent + import graphviz +import pytest + +from src.agents.agent import Agent +from src.agents.visualizations import draw_graph, get_all_edges, get_all_nodes, get_main_graph + @pytest.fixture def mock_agent(): @@ -10,7 +13,7 @@ def mock_agent(): tool1.name = "Tool1" tool2 = Mock() tool2.name = "Tool2" - + handoff1 = Mock() handoff1.name = "Handoff1" handoff1.tools = [] @@ -20,28 +23,55 @@ def mock_agent(): agent.name = "Agent1" agent.tools = [tool1, tool2] agent.handoffs = [handoff1] - + return agent + def test_get_main_graph(mock_agent): result = get_main_graph(mock_agent) assert "digraph G" in result - assert 'graph [splines=true];' in result + assert "graph [splines=true];" in result assert 'node [fontname="Arial"];' in result - assert 'edge [penwidth=1.5];' in result + assert "edge [penwidth=1.5];" in result assert '"__start__" [shape=ellipse, style=filled, fillcolor=lightblue];' in result assert '"__end__" [shape=ellipse, style=filled, fillcolor=lightblue];' in result - assert '"Agent1" [label="Agent1", shape=box, style=filled, fillcolor=lightyellow, width=1.5, height=0.8];' in result - assert '"Tool1" [label="Tool1", shape=ellipse, style=filled, fillcolor=lightgreen, width=0.5, height=0.3];' in result - assert '"Tool2" [label="Tool2", shape=ellipse, style=filled, fillcolor=lightgreen, width=0.5, height=0.3];' in result - assert '"Handoff1" [label="Handoff1", shape=box, style=filled, style=rounded, fillcolor=lightyellow, width=1.5, height=0.8];' in result + assert ( + '"Agent1" [label="Agent1", shape=box, style=filled, fillcolor=lightyellow, width=1.5, height=0.8];' + in result + ) + assert ( + '"Tool1" [label="Tool1", shape=ellipse, style=filled, fillcolor=lightgreen, width=0.5, height=0.3];' + in result + ) + assert ( + '"Tool2" [label="Tool2", shape=ellipse, style=filled, fillcolor=lightgreen, width=0.5, height=0.3];' + in result + ) + assert ( + '"Handoff1" [label="Handoff1", shape=box, style=filled, style=rounded, fillcolor=lightyellow, width=1.5, height=0.8];' + in result + ) + def test_get_all_nodes(mock_agent): result = get_all_nodes(mock_agent) - assert '"Agent1" [label="Agent1", shape=box, style=filled, fillcolor=lightyellow, width=1.5, height=0.8];' in result - assert '"Tool1" [label="Tool1", shape=ellipse, style=filled, fillcolor=lightgreen, width=0.5, height=0.3];' in result - assert '"Tool2" [label="Tool2", shape=ellipse, style=filled, fillcolor=lightgreen, width=0.5, height=0.3];' in result - assert '"Handoff1" [label="Handoff1", shape=box, style=filled, style=rounded, fillcolor=lightyellow, width=1.5, height=0.8];' in result + assert ( + '"Agent1" [label="Agent1", shape=box, style=filled, fillcolor=lightyellow, width=1.5, height=0.8];' + in result + ) + assert ( + '"Tool1" [label="Tool1", shape=ellipse, style=filled, fillcolor=lightgreen, width=0.5, height=0.3];' + in result + ) + assert ( + '"Tool2" [label="Tool2", shape=ellipse, style=filled, fillcolor=lightgreen, width=0.5, height=0.3];' + in result + ) + assert ( + '"Handoff1" [label="Handoff1", shape=box, style=filled, style=rounded, fillcolor=lightyellow, width=1.5, height=0.8];' + in result + ) + def test_get_all_edges(mock_agent): result = get_all_edges(mock_agent) @@ -53,16 +83,29 @@ def test_get_all_edges(mock_agent): assert '"Agent1" -> "Handoff1";' in result assert '"Handoff1" -> "__end__";' in result + def test_draw_graph(mock_agent): graph = draw_graph(mock_agent) assert isinstance(graph, graphviz.Source) assert "digraph G" in graph.source - assert 'graph [splines=true];' in graph.source + assert "graph [splines=true];" in graph.source assert 'node [fontname="Arial"];' in graph.source - assert 'edge [penwidth=1.5];' in graph.source + assert "edge [penwidth=1.5];" in graph.source assert '"__start__" [shape=ellipse, style=filled, fillcolor=lightblue];' in graph.source assert '"__end__" [shape=ellipse, style=filled, fillcolor=lightblue];' in graph.source - assert '"Agent1" [label="Agent1", shape=box, style=filled, fillcolor=lightyellow, width=1.5, height=0.8];' in graph.source - assert '"Tool1" [label="Tool1", shape=ellipse, style=filled, fillcolor=lightgreen, width=0.5, height=0.3];' in graph.source - assert '"Tool2" [label="Tool2", shape=ellipse, style=filled, fillcolor=lightgreen, width=0.5, height=0.3];' in graph.source - assert '"Handoff1" [label="Handoff1", shape=box, style=filled, style=rounded, fillcolor=lightyellow, width=1.5, height=0.8];' in graph.source + assert ( + '"Agent1" [label="Agent1", shape=box, style=filled, fillcolor=lightyellow, width=1.5, height=0.8];' + in graph.source + ) + assert ( + '"Tool1" [label="Tool1", shape=ellipse, style=filled, fillcolor=lightgreen, width=0.5, height=0.3];' + in graph.source + ) + assert ( + '"Tool2" [label="Tool2", shape=ellipse, style=filled, fillcolor=lightgreen, width=0.5, height=0.3];' + in graph.source + ) + assert ( + '"Handoff1" [label="Handoff1", shape=box, style=filled, style=rounded, fillcolor=lightyellow, width=1.5, height=0.8];' + in graph.source + ) From f7c594da083477e5eefbbf8cfe78cfda052bdaae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bravo?= <123977407+MartinEBravo@users.noreply.github.com> Date: Tue, 18 Mar 2025 09:53:48 +0100 Subject: [PATCH 06/31] feat: add visualization functions for agent graphs --- src/agents/{ => extensions}/visualizations.py | 2 +- tests/test_visualizations.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/agents/{ => extensions}/visualizations.py (98%) diff --git a/src/agents/visualizations.py b/src/agents/extensions/visualizations.py similarity index 98% rename from src/agents/visualizations.py rename to src/agents/extensions/visualizations.py index 42019d4d..c020708e 100644 --- a/src/agents/visualizations.py +++ b/src/agents/extensions/visualizations.py @@ -1,6 +1,6 @@ import graphviz -from src.agents.agent import Agent +from agents import Agent def get_main_graph(agent: Agent) -> str: diff --git a/tests/test_visualizations.py b/tests/test_visualizations.py index d8139780..ac02cee8 100644 --- a/tests/test_visualizations.py +++ b/tests/test_visualizations.py @@ -3,8 +3,8 @@ import graphviz import pytest -from src.agents.agent import Agent -from src.agents.visualizations import draw_graph, get_all_edges, get_all_nodes, get_main_graph +from agents import Agent +from agents.extensions.visualizations import draw_graph, get_all_edges, get_all_nodes, get_main_graph @pytest.fixture From 6f2f7293a01a684b2bed22378d0fb7032df43fed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bravo?= <123977407+MartinEBravo@users.noreply.github.com> Date: Tue, 18 Mar 2025 09:53:53 +0100 Subject: [PATCH 07/31] refactor: move graphviz dependency to visualization section --- pyproject.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d4404247..554771ae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,6 @@ dependencies = [ "typing-extensions>=4.12.2, <5", "requests>=2.0, <3", "types-requests>=2.0, <3", - "graphviz>=0.17", ] classifiers = [ "Typing :: Typed", @@ -50,6 +49,10 @@ dev = [ "playwright==1.50.0", "inline-snapshot>=0.20.7", ] +visualization = [ + "graphviz>=0.17", +] + [tool.uv.workspace] members = ["agents"] From c745fe156ab4163a3508564cf5dd30d6f995cb9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bravo?= <123977407+MartinEBravo@users.noreply.github.com> Date: Tue, 18 Mar 2025 09:55:23 +0100 Subject: [PATCH 08/31] feat: add documentation for agent visualization using Graphviz --- docs/{visualizations.md => visualization.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename docs/{visualizations.md => visualization.md} (97%) diff --git a/docs/visualizations.md b/docs/visualization.md similarity index 97% rename from docs/visualizations.md rename to docs/visualization.md index 2a8bb288..6bb5ee5d 100644 --- a/docs/visualizations.md +++ b/docs/visualization.md @@ -7,7 +7,7 @@ Agent visualization allows you to generate a structured graphical representation The visualization functionality relies on the **Graphviz** package. To use it, ensure you have Graphviz installed and add it as a dependency in `pyproject.toml`. Alternatively, install it directly via pip: ```bash -pip install graphviz +pip install openai-agents[visualization] ``` ## Generating a Graph @@ -22,7 +22,7 @@ You can generate an agent visualization using the `draw_graph` function. This fu ```python from agents import Agent, function_tool -from agents.visualizations import draw_graph +from agents.extensions.visualization import draw_graph @function_tool def get_weather(city: str) -> str: From 53367be893c9e7e956e2b51a57c60c8618df5daa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bravo?= <123977407+MartinEBravo@users.noreply.github.com> Date: Tue, 18 Mar 2025 09:55:30 +0100 Subject: [PATCH 09/31] feat: add visualization module for agent graphs using Graphviz --- src/agents/extensions/{visualizations.py => visualization.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/agents/extensions/{visualizations.py => visualization.py} (100%) diff --git a/src/agents/extensions/visualizations.py b/src/agents/extensions/visualization.py similarity index 100% rename from src/agents/extensions/visualizations.py rename to src/agents/extensions/visualization.py From 39ff00dd9dd617514c7a6ca1747f6cb17e7030d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bravo?= <123977407+MartinEBravo@users.noreply.github.com> Date: Tue, 18 Mar 2025 09:59:47 +0100 Subject: [PATCH 10/31] rename: test_visualization.py --- tests/{test_visualizations.py => test_visualization.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename tests/{test_visualizations.py => test_visualization.py} (97%) diff --git a/tests/test_visualizations.py b/tests/test_visualization.py similarity index 97% rename from tests/test_visualizations.py rename to tests/test_visualization.py index ac02cee8..8505b619 100644 --- a/tests/test_visualizations.py +++ b/tests/test_visualization.py @@ -4,7 +4,7 @@ import pytest from agents import Agent -from agents.extensions.visualizations import draw_graph, get_all_edges, get_all_nodes, get_main_graph +from agents.extensions.visualization import draw_graph, get_all_edges, get_all_nodes, get_main_graph @pytest.fixture From 0079bca71726d4f1cff6a7817e297cce3e613a2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bravo?= <123977407+MartinEBravo@users.noreply.github.com> Date: Tue, 18 Mar 2025 10:09:44 +0100 Subject: [PATCH 11/31] style: improve code formatting and readability in visualization functions --- src/agents/extensions/visualization.py | 11 +++--- tests/test_visualization.py | 55 ++++++++++++++------------ 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/src/agents/extensions/visualization.py b/src/agents/extensions/visualization.py index c020708e..985731b8 100644 --- a/src/agents/extensions/visualization.py +++ b/src/agents/extensions/visualization.py @@ -19,7 +19,6 @@ def get_main_graph(agent: Agent) -> str: graph [splines=true]; node [fontname="Arial"]; edge [penwidth=1.5]; - "__start__" [shape=ellipse, style=filled, fillcolor=lightblue]; "__end__" [shape=ellipse, style=filled, fillcolor=lightblue]; """ @@ -41,21 +40,23 @@ def get_all_nodes(agent: Agent, parent: Agent = None) -> str: str: The DOT format string representing the nodes. """ parts = [] - # Ensure parent agent node is colored if not parent: parts.append(f""" - "{agent.name}" [label="{agent.name}", shape=box, style=filled, fillcolor=lightyellow, width=1.5, height=0.8];""") + "{agent.name}" [label="{agent.name}", shape=box, style=filled, + fillcolor=lightyellow, width=1.5, height=0.8];""") # Smaller tools (ellipse, green) for tool in agent.tools: parts.append(f""" - "{tool.name}" [label="{tool.name}", shape=ellipse, style=filled, fillcolor=lightgreen, width=0.5, height=0.3];""") + "{tool.name}" [label="{tool.name}", shape=ellipse, style=filled, + fillcolor=lightgreen, width=0.5, height=0.3];""") # Bigger handoffs (rounded box, yellow) for handoff in agent.handoffs: parts.append(f""" - "{handoff.name}" [label="{handoff.name}", shape=box, style=filled, style=rounded, fillcolor=lightyellow, width=1.5, height=0.8];""") + "{handoff.name}" [label="{handoff.name}", shape=box, style=filled, + style=rounded, fillcolor=lightyellow, width=1.5, height=0.8];""") parts.append(get_all_nodes(handoff)) return "".join(parts) diff --git a/tests/test_visualization.py b/tests/test_visualization.py index 8505b619..12d67fb0 100644 --- a/tests/test_visualization.py +++ b/tests/test_visualization.py @@ -4,7 +4,12 @@ import pytest from agents import Agent -from agents.extensions.visualization import draw_graph, get_all_edges, get_all_nodes, get_main_graph +from agents.extensions.visualization import ( + draw_graph, + get_all_edges, + get_all_nodes, + get_main_graph, +) @pytest.fixture @@ -36,40 +41,40 @@ def test_get_main_graph(mock_agent): assert '"__start__" [shape=ellipse, style=filled, fillcolor=lightblue];' in result assert '"__end__" [shape=ellipse, style=filled, fillcolor=lightblue];' in result assert ( - '"Agent1" [label="Agent1", shape=box, style=filled, fillcolor=lightyellow, width=1.5, height=0.8];' - in result + '"Agent1" [label="Agent1", shape=box, style=filled, ' + "fillcolor=lightyellow, width=1.5, height=0.8];" in result ) assert ( - '"Tool1" [label="Tool1", shape=ellipse, style=filled, fillcolor=lightgreen, width=0.5, height=0.3];' - in result + '"Tool1" [label="Tool1", shape=ellipse, style=filled, ' + "fillcolor=lightgreen, width=0.5, height=0.3];" in result ) assert ( - '"Tool2" [label="Tool2", shape=ellipse, style=filled, fillcolor=lightgreen, width=0.5, height=0.3];' - in result + '"Tool2" [label="Tool2", shape=ellipse, style=filled, ' + "fillcolor=lightgreen, width=0.5, height=0.3];" in result ) assert ( - '"Handoff1" [label="Handoff1", shape=box, style=filled, style=rounded, fillcolor=lightyellow, width=1.5, height=0.8];' - in result + '"Handoff1" [label="Handoff1", shape=box, style=filled, style=rounded, ' + "fillcolor=lightyellow, width=1.5, height=0.8];" in result ) def test_get_all_nodes(mock_agent): result = get_all_nodes(mock_agent) assert ( - '"Agent1" [label="Agent1", shape=box, style=filled, fillcolor=lightyellow, width=1.5, height=0.8];' - in result + '"Agent1" [label="Agent1", shape=box, style=filled, ' + "fillcolor=lightyellow, width=1.5, height=0.8];" in result ) assert ( - '"Tool1" [label="Tool1", shape=ellipse, style=filled, fillcolor=lightgreen, width=0.5, height=0.3];' - in result + '"Tool1" [label="Tool1", shape=ellipse, style=filled, ' + "fillcolor=lightgreen, width=0.5, height=0.3];" in result ) assert ( - '"Tool2" [label="Tool2", shape=ellipse, style=filled, fillcolor=lightgreen, width=0.5, height=0.3];' - in result + '"Tool2" [label="Tool2", shape=ellipse, style=filled, ' + "fillcolor=lightgreen, width=0.5, height=0.3];" in result ) assert ( - '"Handoff1" [label="Handoff1", shape=box, style=filled, style=rounded, fillcolor=lightyellow, width=1.5, height=0.8];' - in result + '"Handoff1" [label="Handoff1", shape=box, style=filled, style=rounded, ' + "fillcolor=lightyellow, width=1.5, height=0.8];" in result ) @@ -94,18 +99,18 @@ def test_draw_graph(mock_agent): assert '"__start__" [shape=ellipse, style=filled, fillcolor=lightblue];' in graph.source assert '"__end__" [shape=ellipse, style=filled, fillcolor=lightblue];' in graph.source assert ( - '"Agent1" [label="Agent1", shape=box, style=filled, fillcolor=lightyellow, width=1.5, height=0.8];' - in graph.source + '"Agent1" [label="Agent1", shape=box, style=filled, ' + "fillcolor=lightyellow, width=1.5, height=0.8];" in graph.source ) assert ( - '"Tool1" [label="Tool1", shape=ellipse, style=filled, fillcolor=lightgreen, width=0.5, height=0.3];' - in graph.source + '"Tool1" [label="Tool1", shape=ellipse, style=filled, ' + "fillcolor=lightgreen, width=0.5, height=0.3];" in graph.source ) assert ( - '"Tool2" [label="Tool2", shape=ellipse, style=filled, fillcolor=lightgreen, width=0.5, height=0.3];' - in graph.source + '"Tool2" [label="Tool2", shape=ellipse, style=filled, ' + "fillcolor=lightgreen, width=0.5, height=0.3];" in graph.source ) assert ( - '"Handoff1" [label="Handoff1", shape=box, style=filled, style=rounded, fillcolor=lightyellow, width=1.5, height=0.8];' - in graph.source + '"Handoff1" [label="Handoff1", shape=box, style=filled, style=rounded, ' + "fillcolor=lightyellow, width=1.5, height=0.8];" in graph.source ) From b7627cb642e3cc50fc95d35b387e1b62870fec00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bravo?= <123977407+MartinEBravo@users.noreply.github.com> Date: Tue, 18 Mar 2025 10:14:52 +0100 Subject: [PATCH 12/31] style: improve string formatting --- src/agents/extensions/visualization.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/agents/extensions/visualization.py b/src/agents/extensions/visualization.py index 985731b8..1b5b7e57 100644 --- a/src/agents/extensions/visualization.py +++ b/src/agents/extensions/visualization.py @@ -42,21 +42,24 @@ def get_all_nodes(agent: Agent, parent: Agent = None) -> str: parts = [] # Ensure parent agent node is colored if not parent: - parts.append(f""" - "{agent.name}" [label="{agent.name}", shape=box, style=filled, - fillcolor=lightyellow, width=1.5, height=0.8];""") + parts.append( + f'"{agent.name}" [label="{agent.name}", shape=box, style=filled, ' + 'fillcolor=lightyellow, width=1.5, height=0.8];' + ) # Smaller tools (ellipse, green) for tool in agent.tools: - parts.append(f""" - "{tool.name}" [label="{tool.name}", shape=ellipse, style=filled, - fillcolor=lightgreen, width=0.5, height=0.3];""") + parts.append( + f'"{tool.name}" [label="{tool.name}", shape=ellipse, style=filled, ' + f'fillcolor=lightgreen, width=0.5, height=0.3];' + ) # Bigger handoffs (rounded box, yellow) for handoff in agent.handoffs: - parts.append(f""" - "{handoff.name}" [label="{handoff.name}", shape=box, style=filled, - style=rounded, fillcolor=lightyellow, width=1.5, height=0.8];""") + parts.append( + f'"{handoff.name}" [label="{handoff.name}", shape=box, style=filled, style=rounded, ' + f'fillcolor=lightyellow, width=1.5, height=0.8];' + ) parts.append(get_all_nodes(handoff)) return "".join(parts) From f4edc1f3726a2038458e5fe1ca192de28c46d26c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bravo?= <123977407+MartinEBravo@users.noreply.github.com> Date: Tue, 18 Mar 2025 10:15:41 +0100 Subject: [PATCH 13/31] style: improve string formatting in visualization functions --- src/agents/extensions/visualization.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/agents/extensions/visualization.py b/src/agents/extensions/visualization.py index 1b5b7e57..81b63382 100644 --- a/src/agents/extensions/visualization.py +++ b/src/agents/extensions/visualization.py @@ -44,21 +44,21 @@ def get_all_nodes(agent: Agent, parent: Agent = None) -> str: if not parent: parts.append( f'"{agent.name}" [label="{agent.name}", shape=box, style=filled, ' - 'fillcolor=lightyellow, width=1.5, height=0.8];' + "fillcolor=lightyellow, width=1.5, height=0.8];" ) # Smaller tools (ellipse, green) for tool in agent.tools: parts.append( f'"{tool.name}" [label="{tool.name}", shape=ellipse, style=filled, ' - f'fillcolor=lightgreen, width=0.5, height=0.3];' + f"fillcolor=lightgreen, width=0.5, height=0.3];" ) # Bigger handoffs (rounded box, yellow) for handoff in agent.handoffs: parts.append( f'"{handoff.name}" [label="{handoff.name}", shape=box, style=filled, style=rounded, ' - f'fillcolor=lightyellow, width=1.5, height=0.8];' + f"fillcolor=lightyellow, width=1.5, height=0.8];" ) parts.append(get_all_nodes(handoff)) From 9f7d596d148340e16da6a5c4327836e1acc3f743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bravo?= <123977407+MartinEBravo@users.noreply.github.com> Date: Mon, 24 Mar 2025 09:08:29 +0100 Subject: [PATCH 14/31] feat: add optional dependency for visualization using Graphviz --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 554771ae..ed117233 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,6 +49,8 @@ dev = [ "playwright==1.50.0", "inline-snapshot>=0.20.7", ] + +[project.optional-dependencies] visualization = [ "graphviz>=0.17", ] From a5b7abe8b4978815ad3694bb3555a4c61e8a844b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bravo?= <123977407+MartinEBravo@users.noreply.github.com> Date: Mon, 24 Mar 2025 09:30:13 +0100 Subject: [PATCH 15/31] feat: enhance visualization functions with optional type hints and improved handling of agents and handoffs --- src/agents/extensions/visualization.py | 39 +++++++++++++++++--------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/src/agents/extensions/visualization.py b/src/agents/extensions/visualization.py index 81b63382..a05dcd39 100644 --- a/src/agents/extensions/visualization.py +++ b/src/agents/extensions/visualization.py @@ -1,6 +1,7 @@ import graphviz from agents import Agent +from agents.handoffs import Handoff def get_main_graph(agent: Agent) -> str: @@ -29,7 +30,9 @@ def get_main_graph(agent: Agent) -> str: return "".join(parts) -def get_all_nodes(agent: Agent, parent: Agent = None) -> str: +from typing import Optional + +def get_all_nodes(agent: Agent, parent: Optional[Agent] = None) -> str: """ Recursively generates the nodes for the given agent and its handoffs in DOT format. @@ -47,25 +50,29 @@ def get_all_nodes(agent: Agent, parent: Agent = None) -> str: "fillcolor=lightyellow, width=1.5, height=0.8];" ) - # Smaller tools (ellipse, green) for tool in agent.tools: parts.append( f'"{tool.name}" [label="{tool.name}", shape=ellipse, style=filled, ' f"fillcolor=lightgreen, width=0.5, height=0.3];" ) - # Bigger handoffs (rounded box, yellow) for handoff in agent.handoffs: - parts.append( - f'"{handoff.name}" [label="{handoff.name}", shape=box, style=filled, style=rounded, ' - f"fillcolor=lightyellow, width=1.5, height=0.8];" - ) - parts.append(get_all_nodes(handoff)) + if isinstance(handoff, Handoff): + parts.append( + f'"{handoff.agent_name}" [label="{handoff.agent_name}", shape=box, style=filled, style=rounded, ' + f"fillcolor=lightyellow, width=1.5, height=0.8];" + ) + if isinstance(handoff, Agent): + parts.append( + f'"{handoff.name}" [label="{handoff.name}", shape=box, style=filled, style=rounded, ' + f"fillcolor=lightyellow, width=1.5, height=0.8];" + ) + parts.append(get_all_nodes(handoff)) return "".join(parts) -def get_all_edges(agent: Agent, parent: Agent = None) -> str: +def get_all_edges(agent: Agent, parent: Optional[Agent] = None) -> str: """ Recursively generates the edges for the given agent and its handoffs in DOT format. @@ -92,14 +99,20 @@ def get_all_edges(agent: Agent, parent: Agent = None) -> str: "{agent.name}" -> "__end__";""") for handoff in agent.handoffs: - parts.append(f""" - "{agent.name}" -> "{handoff.name}";""") - parts.append(get_all_edges(handoff, agent)) + if isinstance(handoff, Handoff): + parts.append(f""" + "{agent.name}" -> "{handoff.agent_name}";""") + if isinstance(handoff, Agent): + parts.append(f""" + "{agent.name}" -> "{handoff.name}";""") + parts.append(get_all_edges(handoff, agent)) return "".join(parts) -def draw_graph(agent: Agent, filename: str = None) -> graphviz.Source: +from typing import Optional + +def draw_graph(agent: Agent, filename: Optional[str] = None) -> graphviz.Source: """ Draws the graph for the given agent and optionally saves it as a PNG file. From 623063b633bb0f4ed4da1d31f341f3ecee3ba25f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bravo?= <123977407+MartinEBravo@users.noreply.github.com> Date: Mon, 24 Mar 2025 09:47:21 +0100 Subject: [PATCH 16/31] refactor: clean up visualization functions by removing unused nodes and improving type hints --- src/agents/extensions/visualization.py | 22 ++++++---------------- tests/test_visualization.py | 14 ++++---------- 2 files changed, 10 insertions(+), 26 deletions(-) diff --git a/src/agents/extensions/visualization.py b/src/agents/extensions/visualization.py index a05dcd39..dd75acfc 100644 --- a/src/agents/extensions/visualization.py +++ b/src/agents/extensions/visualization.py @@ -1,3 +1,5 @@ +from typing import Optional + import graphviz from agents import Agent @@ -20,8 +22,6 @@ def get_main_graph(agent: Agent) -> str: graph [splines=true]; node [fontname="Arial"]; edge [penwidth=1.5]; - "__start__" [shape=ellipse, style=filled, fillcolor=lightblue]; - "__end__" [shape=ellipse, style=filled, fillcolor=lightblue]; """ ] parts.append(get_all_nodes(agent)) @@ -30,8 +30,6 @@ def get_main_graph(agent: Agent) -> str: return "".join(parts) -from typing import Optional - def get_all_nodes(agent: Agent, parent: Optional[Agent] = None) -> str: """ Recursively generates the nodes for the given agent and its handoffs in DOT format. @@ -59,12 +57,14 @@ def get_all_nodes(agent: Agent, parent: Optional[Agent] = None) -> str: for handoff in agent.handoffs: if isinstance(handoff, Handoff): parts.append( - f'"{handoff.agent_name}" [label="{handoff.agent_name}", shape=box, style=filled, style=rounded, ' + f'"{handoff.agent_name}" [label="{handoff.agent_name}", shape=box, ' + f"shape=box, style=filled, style=rounded, " f"fillcolor=lightyellow, width=1.5, height=0.8];" ) if isinstance(handoff, Agent): parts.append( - f'"{handoff.name}" [label="{handoff.name}", shape=box, style=filled, style=rounded, ' + f'"{handoff.name}" [label="{handoff.name}", ' + f"shape=box, style=filled, style=rounded, " f"fillcolor=lightyellow, width=1.5, height=0.8];" ) parts.append(get_all_nodes(handoff)) @@ -85,19 +85,11 @@ def get_all_edges(agent: Agent, parent: Optional[Agent] = None) -> str: """ parts = [] - if not parent: - parts.append(f""" - "__start__" -> "{agent.name}";""") - for tool in agent.tools: parts.append(f""" "{agent.name}" -> "{tool.name}" [style=dotted, penwidth=1.5]; "{tool.name}" -> "{agent.name}" [style=dotted, penwidth=1.5];""") - if not agent.handoffs: - parts.append(f""" - "{agent.name}" -> "__end__";""") - for handoff in agent.handoffs: if isinstance(handoff, Handoff): parts.append(f""" @@ -110,8 +102,6 @@ def get_all_edges(agent: Agent, parent: Optional[Agent] = None) -> str: return "".join(parts) -from typing import Optional - def draw_graph(agent: Agent, filename: Optional[str] = None) -> graphviz.Source: """ Draws the graph for the given agent and optionally saves it as a PNG file. diff --git a/tests/test_visualization.py b/tests/test_visualization.py index 12d67fb0..1f83d858 100644 --- a/tests/test_visualization.py +++ b/tests/test_visualization.py @@ -10,6 +10,7 @@ get_all_nodes, get_main_graph, ) +from agents.handoffs import Handoff @pytest.fixture @@ -19,10 +20,8 @@ def mock_agent(): tool2 = Mock() tool2.name = "Tool2" - handoff1 = Mock() - handoff1.name = "Handoff1" - handoff1.tools = [] - handoff1.handoffs = [] + handoff1 = Mock(spec=Handoff) + handoff1.agent_name = "Handoff1" agent = Mock(spec=Agent) agent.name = "Agent1" @@ -34,12 +33,11 @@ def mock_agent(): def test_get_main_graph(mock_agent): result = get_main_graph(mock_agent) + print(result) assert "digraph G" in result assert "graph [splines=true];" in result assert 'node [fontname="Arial"];' in result assert "edge [penwidth=1.5];" in result - assert '"__start__" [shape=ellipse, style=filled, fillcolor=lightblue];' in result - assert '"__end__" [shape=ellipse, style=filled, fillcolor=lightblue];' in result assert ( '"Agent1" [label="Agent1", shape=box, style=filled, ' "fillcolor=lightyellow, width=1.5, height=0.8];" in result @@ -80,13 +78,11 @@ def test_get_all_nodes(mock_agent): def test_get_all_edges(mock_agent): result = get_all_edges(mock_agent) - assert '"__start__" -> "Agent1";' in result assert '"Agent1" -> "Tool1" [style=dotted, penwidth=1.5];' in result assert '"Tool1" -> "Agent1" [style=dotted, penwidth=1.5];' in result assert '"Agent1" -> "Tool2" [style=dotted, penwidth=1.5];' in result assert '"Tool2" -> "Agent1" [style=dotted, penwidth=1.5];' in result assert '"Agent1" -> "Handoff1";' in result - assert '"Handoff1" -> "__end__";' in result def test_draw_graph(mock_agent): @@ -96,8 +92,6 @@ def test_draw_graph(mock_agent): assert "graph [splines=true];" in graph.source assert 'node [fontname="Arial"];' in graph.source assert "edge [penwidth=1.5];" in graph.source - assert '"__start__" [shape=ellipse, style=filled, fillcolor=lightblue];' in graph.source - assert '"__end__" [shape=ellipse, style=filled, fillcolor=lightblue];' in graph.source assert ( '"Agent1" [label="Agent1", shape=box, style=filled, ' "fillcolor=lightyellow, width=1.5, height=0.8];" in graph.source From b3addcff13c0c913b0747e3eade2b7d3a01eb2dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bravo?= <123977407+MartinEBravo@users.noreply.github.com> Date: Tue, 25 Mar 2025 16:59:17 +0100 Subject: [PATCH 17/31] Add visualization optional dependency for graphviz --- pyproject.toml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index bea34e5a..876551d9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,6 +34,7 @@ Repository = "https://github.com/openai/openai-agents-python" [project.optional-dependencies] voice = ["numpy>=2.2.0, <3; python_version>='3.10'", "websockets>=15.0, <16"] +visualization = ["graphviz>=0.17"] [dependency-groups] dev = [ @@ -57,11 +58,6 @@ dev = [ "websockets", ] -[project.optional-dependencies] -visualization = [ - "graphviz>=0.17", -] - [tool.uv.workspace] members = ["agents"] From 9d0467109521d669f978a3479b4fd502bda53a08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bravo?= <123977407+MartinEBravo@users.noreply.github.com> Date: Tue, 25 Mar 2025 18:01:45 +0100 Subject: [PATCH 18/31] Rename visualization dependency to viz in pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 876551d9..b3910d1b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,7 +34,7 @@ Repository = "https://github.com/openai/openai-agents-python" [project.optional-dependencies] voice = ["numpy>=2.2.0, <3; python_version>='3.10'", "websockets>=15.0, <16"] -visualization = ["graphviz>=0.17"] +viz = ["graphviz>=0.17"] [dependency-groups] dev = [ From 57ecebfe35209cf6052d87d3569dfc3983a5845a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bravo?= <123977407+MartinEBravo@users.noreply.github.com> Date: Tue, 25 Mar 2025 18:16:29 +0100 Subject: [PATCH 19/31] Refactor visualization node label formatting in get_all_nodes function --- src/agents/extensions/visualization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/agents/extensions/visualization.py b/src/agents/extensions/visualization.py index dd75acfc..f6079857 100644 --- a/src/agents/extensions/visualization.py +++ b/src/agents/extensions/visualization.py @@ -57,7 +57,7 @@ def get_all_nodes(agent: Agent, parent: Optional[Agent] = None) -> str: for handoff in agent.handoffs: if isinstance(handoff, Handoff): parts.append( - f'"{handoff.agent_name}" [label="{handoff.agent_name}", shape=box, ' + f'"{handoff.agent_name}" [label="{handoff.agent_name}", ' f"shape=box, style=filled, style=rounded, " f"fillcolor=lightyellow, width=1.5, height=0.8];" ) From 698fd69eb4035a863932b871897558e773954386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bravo?= <123977407+MartinEBravo@users.noreply.github.com> Date: Tue, 25 Mar 2025 18:29:55 +0100 Subject: [PATCH 20/31] Update installation instructions for visualization dependency to use 'viz' --- docs/visualization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/visualization.md b/docs/visualization.md index 6bb5ee5d..2e3b188b 100644 --- a/docs/visualization.md +++ b/docs/visualization.md @@ -7,7 +7,7 @@ Agent visualization allows you to generate a structured graphical representation The visualization functionality relies on the **Graphviz** package. To use it, ensure you have Graphviz installed and add it as a dependency in `pyproject.toml`. Alternatively, install it directly via pip: ```bash -pip install openai-agents[visualization] +pip install openai-agents[viz] ``` ## Generating a Graph From 6be9b2a222378ec099852ea0d17d5e69a5030442 Mon Sep 17 00:00:00 2001 From: Rohan Mehta <rm@openai.com> Date: Tue, 25 Mar 2025 13:34:45 -0400 Subject: [PATCH 21/31] Update visualization.md --- docs/visualization.md | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/docs/visualization.md b/docs/visualization.md index 2e3b188b..00f3126d 100644 --- a/docs/visualization.md +++ b/docs/visualization.md @@ -4,10 +4,10 @@ Agent visualization allows you to generate a structured graphical representation ## Installation -The visualization functionality relies on the **Graphviz** package. To use it, ensure you have Graphviz installed and add it as a dependency in `pyproject.toml`. Alternatively, install it directly via pip: +Install the optional `viz` dependency group: ```bash -pip install openai-agents[viz] +pip install "openai-agents[viz]" ``` ## Generating a Graph @@ -83,24 +83,4 @@ draw_graph(triage_agent, filename="agent_graph.png") This will generate `agent_graph.png` in the working directory. -## Testing the Visualization - -The visualization functionality includes test coverage to ensure correctness. Tests are located in `tests/test_visualizations.py` and verify: - -- Node and edge correctness in `get_main_graph()`. -- Proper agent and tool representation in `get_all_nodes()`. -- Accurate relationship mapping in `get_all_edges()`. -- Graph rendering functionality in `draw_graph()`. - -Run tests using: - -```bash -pytest tests/test_visualizations.py -``` - -## Conclusion - -Agent visualization provides a powerful way to **understand, debug, and communicate** how agents interact within an application. By leveraging **Graphviz**, you can generate intuitive visual representations of complex agent structures effortlessly. - -For further details on agent functionality, see the [Agents documentation](agents.md). From 59aed3490d5a7d9aac538d129f8f5689400d6157 Mon Sep 17 00:00:00 2001 From: Rohan Mehta <rm@openai.com> Date: Tue, 25 Mar 2025 13:37:01 -0400 Subject: [PATCH 22/31] Update pyproject.toml --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index d2b56046..eb0bae39 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,6 +57,7 @@ dev = [ "pynput", "textual", "websockets", + "graphviz", ] [tool.uv.workspace] From 2f2606e5eab135bcf958fa14ba09ae0b64f0aabc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bravo?= <123977407+MartinEBravo@users.noreply.github.com> Date: Tue, 25 Mar 2025 18:46:23 +0100 Subject: [PATCH 23/31] Add graphviz as a dependency and update import statements --- src/agents/extensions/visualization.py | 2 +- tests/test_visualization.py | 2 +- uv.lock | 17 +++++++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/agents/extensions/visualization.py b/src/agents/extensions/visualization.py index f6079857..236bec9b 100644 --- a/src/agents/extensions/visualization.py +++ b/src/agents/extensions/visualization.py @@ -1,6 +1,6 @@ from typing import Optional -import graphviz +import graphviz # type: ignore from agents import Agent from agents.handoffs import Handoff diff --git a/tests/test_visualization.py b/tests/test_visualization.py index 1f83d858..046cdd6e 100644 --- a/tests/test_visualization.py +++ b/tests/test_visualization.py @@ -1,6 +1,6 @@ from unittest.mock import Mock -import graphviz +import graphviz # type: ignore import pytest from agents import Agent diff --git a/uv.lock b/uv.lock index d6eba43f..3ee7f047 100644 --- a/uv.lock +++ b/uv.lock @@ -1,4 +1,5 @@ version = 1 +revision = 1 requires-python = ">=3.9" resolution-markers = [ "python_full_version >= '3.10'", @@ -348,6 +349,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034 }, ] +[[package]] +name = "graphviz" +version = "0.20.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/83/5a40d19b8347f017e417710907f824915fba411a9befd092e52746b63e9f/graphviz-0.20.3.zip", hash = "sha256:09d6bc81e6a9fa392e7ba52135a9d49f1ed62526f96499325930e87ca1b5925d", size = 256455 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/be/d59db2d1d52697c6adc9eacaf50e8965b6345cc143f671e1ed068818d5cf/graphviz-0.20.3-py3-none-any.whl", hash = "sha256:81f848f2904515d8cd359cc611faba817598d2feaac4027b266aa3eda7b3dde5", size = 47126 }, +] + [[package]] name = "greenlet" version = "3.1.1" @@ -1090,6 +1100,9 @@ dependencies = [ ] [package.optional-dependencies] +viz = [ + { name = "graphviz" }, +] voice = [ { name = "numpy", version = "2.2.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "websockets" }, @@ -1098,6 +1111,7 @@ voice = [ [package.dev-dependencies] dev = [ { name = "coverage" }, + { name = "graphviz" }, { name = "inline-snapshot" }, { name = "mkdocs" }, { name = "mkdocs-material" }, @@ -1118,6 +1132,7 @@ dev = [ [package.metadata] requires-dist = [ + { name = "graphviz", marker = "extra == 'viz'", specifier = ">=0.17" }, { name = "griffe", specifier = ">=1.5.6,<2" }, { name = "mcp", marker = "python_full_version >= '3.10'" }, { name = "numpy", marker = "python_full_version >= '3.10' and extra == 'voice'", specifier = ">=2.2.0,<3" }, @@ -1128,10 +1143,12 @@ requires-dist = [ { name = "typing-extensions", specifier = ">=4.12.2,<5" }, { name = "websockets", marker = "extra == 'voice'", specifier = ">=15.0,<16" }, ] +provides-extras = ["voice", "viz"] [package.metadata.requires-dev] dev = [ { name = "coverage", specifier = ">=7.6.12" }, + { name = "graphviz" }, { name = "inline-snapshot", specifier = ">=0.20.7" }, { name = "mkdocs", specifier = ">=1.6.0" }, { name = "mkdocs-material", specifier = ">=9.6.0" }, From d8922ff4722bae45acf00af1d3eb2adbcb2c7a5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bravo?= <123977407+MartinEBravo@users.noreply.github.com> Date: Tue, 25 Mar 2025 18:56:01 +0100 Subject: [PATCH 24/31] Add visualization.md to navigation in mkdocs.yml --- mkdocs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/mkdocs.yml b/mkdocs.yml index 941f29ed..a27a6369 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -35,6 +35,7 @@ nav: - multi_agent.md - models.md - config.md + - visualization.md - Voice agents: - voice/quickstart.md - voice/pipeline.md From b9d32cda0ff4376ffae45d140ac7b957b248a6a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bravo?= <123977407+MartinEBravo@users.noreply.github.com> Date: Tue, 25 Mar 2025 19:02:21 +0100 Subject: [PATCH 25/31] Refactor get_all_edges function to remove unused parent parameter --- src/agents/extensions/visualization.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/agents/extensions/visualization.py b/src/agents/extensions/visualization.py index 236bec9b..e2de019d 100644 --- a/src/agents/extensions/visualization.py +++ b/src/agents/extensions/visualization.py @@ -72,7 +72,7 @@ def get_all_nodes(agent: Agent, parent: Optional[Agent] = None) -> str: return "".join(parts) -def get_all_edges(agent: Agent, parent: Optional[Agent] = None) -> str: +def get_all_edges(agent: Agent) -> str: """ Recursively generates the edges for the given agent and its handoffs in DOT format. @@ -97,7 +97,7 @@ def get_all_edges(agent: Agent, parent: Optional[Agent] = None) -> str: if isinstance(handoff, Agent): parts.append(f""" "{agent.name}" -> "{handoff.name}";""") - parts.append(get_all_edges(handoff, agent)) + parts.append(get_all_edges(handoff)) return "".join(parts) From 29103caba98c05a93f16ca7986c6975934f8743a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bravo?= <123977407+MartinEBravo@users.noreply.github.com> Date: Tue, 25 Mar 2025 19:03:39 +0100 Subject: [PATCH 26/31] Add Jupyter Notebook files to .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 2ab5a819..7ad013cb 100644 --- a/.gitignore +++ b/.gitignore @@ -142,3 +142,6 @@ cython_debug/ # PyPI configuration file .pypirc + +# Jupyter Notebook +*.ipynb From 5ad53d80008ba7632b58981928e788231754e903 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bravo?= <123977407+MartinEBravo@users.noreply.github.com> Date: Tue, 25 Mar 2025 19:11:43 +0100 Subject: [PATCH 27/31] Add start and end nodes to graph visualization and update edge generation --- src/agents/extensions/visualization.py | 23 ++++++++++++++++++++-- tests/test_visualization.py | 27 ++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/agents/extensions/visualization.py b/src/agents/extensions/visualization.py index e2de019d..08b185e1 100644 --- a/src/agents/extensions/visualization.py +++ b/src/agents/extensions/visualization.py @@ -4,6 +4,7 @@ from agents import Agent from agents.handoffs import Handoff +from agents.tool import Tool def get_main_graph(agent: Agent) -> str: @@ -41,6 +42,14 @@ def get_all_nodes(agent: Agent, parent: Optional[Agent] = None) -> str: str: The DOT format string representing the nodes. """ parts = [] + + # Start and end the graph + parts.append( + f'"__start__" [label="__start__", shape=ellipse, style=filled, ' + f"fillcolor=lightblue, width=0.5, height=0.3];" + f'"__end__" [label="__end__", shape=ellipse, style=filled, ' + f"fillcolor=lightblue, width=0.5, height=0.3];" + ) # Ensure parent agent node is colored if not parent: parts.append( @@ -72,7 +81,7 @@ def get_all_nodes(agent: Agent, parent: Optional[Agent] = None) -> str: return "".join(parts) -def get_all_edges(agent: Agent) -> str: +def get_all_edges(agent: Agent, parent: Optional[Agent] = None) -> str: """ Recursively generates the edges for the given agent and its handoffs in DOT format. @@ -85,6 +94,11 @@ def get_all_edges(agent: Agent) -> str: """ parts = [] + if not parent: + parts.append( + f'"__start__" -> "{agent.name}";' + ) + for tool in agent.tools: parts.append(f""" "{agent.name}" -> "{tool.name}" [style=dotted, penwidth=1.5]; @@ -97,7 +111,12 @@ def get_all_edges(agent: Agent) -> str: if isinstance(handoff, Agent): parts.append(f""" "{agent.name}" -> "{handoff.name}";""") - parts.append(get_all_edges(handoff)) + parts.append(get_all_edges(handoff, agent)) + + if not agent.handoffs and not isinstance(agent, Tool): + parts.append( + f'"{agent.name}" -> "__end__";' + ) return "".join(parts) diff --git a/tests/test_visualization.py b/tests/test_visualization.py index 046cdd6e..b530f50c 100644 --- a/tests/test_visualization.py +++ b/tests/test_visualization.py @@ -38,6 +38,14 @@ def test_get_main_graph(mock_agent): assert "graph [splines=true];" in result assert 'node [fontname="Arial"];' in result assert "edge [penwidth=1.5];" in result + assert ( + '"__start__" [label="__start__", shape=ellipse, style=filled, ' + "fillcolor=lightblue, width=0.5, height=0.3];" in result + ) + assert ( + '"__end__" [label="__end__", shape=ellipse, style=filled, ' + "fillcolor=lightblue, width=0.5, height=0.3];" in result + ) assert ( '"Agent1" [label="Agent1", shape=box, style=filled, ' "fillcolor=lightyellow, width=1.5, height=0.8];" in result @@ -58,6 +66,14 @@ def test_get_main_graph(mock_agent): def test_get_all_nodes(mock_agent): result = get_all_nodes(mock_agent) + assert ( + '"__start__" [label="__start__", shape=ellipse, style=filled, ' + "fillcolor=lightblue, width=0.5, height=0.3];" in result + ) + assert ( + '"__end__" [label="__end__", shape=ellipse, style=filled, ' + "fillcolor=lightblue, width=0.5, height=0.3];" in result + ) assert ( '"Agent1" [label="Agent1", shape=box, style=filled, ' "fillcolor=lightyellow, width=1.5, height=0.8];" in result @@ -78,6 +94,8 @@ def test_get_all_nodes(mock_agent): def test_get_all_edges(mock_agent): result = get_all_edges(mock_agent) + assert '"__start__" -> "Agent1";' in result + assert '"Agent1" -> "__end__";' assert '"Agent1" -> "Tool1" [style=dotted, penwidth=1.5];' in result assert '"Tool1" -> "Agent1" [style=dotted, penwidth=1.5];' in result assert '"Agent1" -> "Tool2" [style=dotted, penwidth=1.5];' in result @@ -85,6 +103,7 @@ def test_get_all_edges(mock_agent): assert '"Agent1" -> "Handoff1";' in result + def test_draw_graph(mock_agent): graph = draw_graph(mock_agent) assert isinstance(graph, graphviz.Source) @@ -92,6 +111,14 @@ def test_draw_graph(mock_agent): assert "graph [splines=true];" in graph.source assert 'node [fontname="Arial"];' in graph.source assert "edge [penwidth=1.5];" in graph.source + assert ( + '"__start__" [label="__start__", shape=ellipse, style=filled, ' + "fillcolor=lightblue, width=0.5, height=0.3];" in graph.source + ) + assert ( + '"__end__" [label="__end__", shape=ellipse, style=filled, ' + "fillcolor=lightblue, width=0.5, height=0.3];" in graph.source + ) assert ( '"Agent1" [label="Agent1", shape=box, style=filled, ' "fillcolor=lightyellow, width=1.5, height=0.8];" in graph.source From 351b6074e5e69c29e791291e0b2a38e52981d2ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bravo?= <123977407+MartinEBravo@users.noreply.github.com> Date: Tue, 25 Mar 2025 19:12:40 +0100 Subject: [PATCH 28/31] Refactor visualization functions to improve formatting and streamline edge generation --- src/agents/extensions/visualization.py | 16 ++++++---------- tests/test_visualization.py | 1 - 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/agents/extensions/visualization.py b/src/agents/extensions/visualization.py index 08b185e1..013a21eb 100644 --- a/src/agents/extensions/visualization.py +++ b/src/agents/extensions/visualization.py @@ -45,10 +45,10 @@ def get_all_nodes(agent: Agent, parent: Optional[Agent] = None) -> str: # Start and end the graph parts.append( - f'"__start__" [label="__start__", shape=ellipse, style=filled, ' - f"fillcolor=lightblue, width=0.5, height=0.3];" - f'"__end__" [label="__end__", shape=ellipse, style=filled, ' - f"fillcolor=lightblue, width=0.5, height=0.3];" + '"__start__" [label="__start__", shape=ellipse, style=filled, ' + "fillcolor=lightblue, width=0.5, height=0.3];" + '"__end__" [label="__end__", shape=ellipse, style=filled, ' + "fillcolor=lightblue, width=0.5, height=0.3];" ) # Ensure parent agent node is colored if not parent: @@ -95,9 +95,7 @@ def get_all_edges(agent: Agent, parent: Optional[Agent] = None) -> str: parts = [] if not parent: - parts.append( - f'"__start__" -> "{agent.name}";' - ) + parts.append(f'"__start__" -> "{agent.name}";') for tool in agent.tools: parts.append(f""" @@ -114,9 +112,7 @@ def get_all_edges(agent: Agent, parent: Optional[Agent] = None) -> str: parts.append(get_all_edges(handoff, agent)) if not agent.handoffs and not isinstance(agent, Tool): - parts.append( - f'"{agent.name}" -> "__end__";' - ) + parts.append(f'"{agent.name}" -> "__end__";') return "".join(parts) diff --git a/tests/test_visualization.py b/tests/test_visualization.py index b530f50c..6aa86774 100644 --- a/tests/test_visualization.py +++ b/tests/test_visualization.py @@ -103,7 +103,6 @@ def test_get_all_edges(mock_agent): assert '"Agent1" -> "Handoff1";' in result - def test_draw_graph(mock_agent): graph = draw_graph(mock_agent) assert isinstance(graph, graphviz.Source) From 3068e42029cc7a45d977742f648a0c0acba4c36c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bravo?= <123977407+MartinEBravo@users.noreply.github.com> Date: Tue, 25 Mar 2025 19:17:35 +0100 Subject: [PATCH 29/31] Fix type ignore comment for agent check in get_all_edges function --- src/agents/extensions/visualization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/agents/extensions/visualization.py b/src/agents/extensions/visualization.py index 013a21eb..9d0d9f63 100644 --- a/src/agents/extensions/visualization.py +++ b/src/agents/extensions/visualization.py @@ -111,7 +111,7 @@ def get_all_edges(agent: Agent, parent: Optional[Agent] = None) -> str: "{agent.name}" -> "{handoff.name}";""") parts.append(get_all_edges(handoff, agent)) - if not agent.handoffs and not isinstance(agent, Tool): + if not agent.handoffs and not isinstance(agent, Tool): # type: ignore parts.append(f'"{agent.name}" -> "__end__";') return "".join(parts) From 70aff1d39d466b16ca0a6e9c96f09bc7c620558f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bravo?= <123977407+MartinEBravo@users.noreply.github.com> Date: Tue, 25 Mar 2025 19:18:57 +0100 Subject: [PATCH 30/31] linting --- src/agents/extensions/visualization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/agents/extensions/visualization.py b/src/agents/extensions/visualization.py index 9d0d9f63..5fb35062 100644 --- a/src/agents/extensions/visualization.py +++ b/src/agents/extensions/visualization.py @@ -111,7 +111,7 @@ def get_all_edges(agent: Agent, parent: Optional[Agent] = None) -> str: "{agent.name}" -> "{handoff.name}";""") parts.append(get_all_edges(handoff, agent)) - if not agent.handoffs and not isinstance(agent, Tool): # type: ignore + if not agent.handoffs and not isinstance(agent, Tool): # type: ignore parts.append(f'"{agent.name}" -> "__end__";') return "".join(parts) From c16deb22a1f2c07e929442c40c1f726c9ee15f1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bravo?= <123977407+MartinEBravo@users.noreply.github.com> Date: Tue, 25 Mar 2025 19:31:32 +0100 Subject: [PATCH 31/31] Remove Jupyter Notebook files from .gitignore --- .gitignore | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 7ad013cb..7dd22b88 100644 --- a/.gitignore +++ b/.gitignore @@ -141,7 +141,4 @@ cython_debug/ .ruff_cache/ # PyPI configuration file -.pypirc - -# Jupyter Notebook -*.ipynb +.pypirc \ No newline at end of file