Skip to content

Commit 9cf5ad6

Browse files
committed
Improvements
1 parent 36dde67 commit 9cf5ad6

10 files changed

Lines changed: 117 additions & 14 deletions

File tree

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Sequential workflow</title>
7+
<style>
8+
body { font-family: system-ui, sans-serif; margin: 2rem; background: #fafafa; }
9+
h1 { color: #333; }
10+
#diagram { background: white; padding: 2rem; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
11+
</style>
12+
</head>
13+
<body>
14+
<h1>Sequential workflow</h1>
15+
<div id="diagram"></div>
16+
<script type="module">
17+
import { renderMermaidSVG } from "https://esm.sh/beautiful-mermaid@1";
18+
const diagram = `flowchart LR
19+
in((In))
20+
out((Out))
21+
subgraph sequential_workflow["Sequential workflow"]
22+
country[Country]
23+
gate{Gate}
24+
subgraph parallel_workflow["Parallel workflow"]
25+
food[Food]
26+
sports[Sports]
27+
weather[Weather]
28+
end
29+
parallel_workflow_aggregator[Parallel workflow Aggregator]
30+
end
31+
in --> country
32+
country --> gate
33+
gate -->|failure| out
34+
gate --> food
35+
gate --> sports
36+
gate --> weather
37+
food --> parallel_workflow_aggregator
38+
parallel_workflow_aggregator --> out
39+
sports --> parallel_workflow_aggregator
40+
weather --> parallel_workflow_aggregator`;
41+
const svg = renderMermaidSVG(diagram);
42+
document.getElementById("diagram").innerHTML = svg;
43+
</script>
44+
</body>
45+
</html>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
```mermaid
2+
flowchart LR
3+
in((In))
4+
out((Out))
5+
subgraph sequential_workflow["Sequential workflow"]
6+
country[Country]
7+
gate{Gate}
8+
subgraph parallel_workflow["Parallel workflow"]
9+
food[Food]
10+
sports[Sports]
11+
weather[Weather]
12+
end
13+
parallel_workflow_aggregator[Parallel workflow Aggregator]
14+
end
15+
in --> country
16+
country --> gate
17+
gate -->|failure| out
18+
gate --> food
19+
gate --> sports
20+
gate --> weather
21+
food --> parallel_workflow_aggregator
22+
parallel_workflow_aggregator --> out
23+
sports --> parallel_workflow_aggregator
24+
weather --> parallel_workflow_aggregator
25+
```

examples/complex_llm_workflow/generator.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,9 @@ class WeatherStep < MARS::AgentStep
109109
# Generate and save the diagram
110110
diagram = MARS::Rendering::Mermaid.new(sequential_workflow).render
111111
File.write("examples/complex_llm_workflow/diagram.md", diagram)
112-
MARS::Rendering::Html.new(sequential_workflow).write("examples/complex_llm_workflow/diagram.html")
113112
puts "Complex workflow diagram saved to: examples/complex_llm_workflow/diagram.md"
113+
MARS::Rendering::Html.new(sequential_workflow).write("examples/complex_llm_workflow/diagram.html")
114+
puts "Complex workflow beautiful mermaid diagram saved to: examples/complex_llm_workflow/diagram.html"
114115

115116
# Run the workflow
116117
puts sequential_workflow.run("Which is the largest country in Europe?")

examples/complex_workflow/diagram.html

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,11 @@ <h1>Main Pipeline</h1>
3939
gate -->|warning| agent4
4040
gate -->|error| agent2
4141
gate -->|error| agent3
42-
gate --> agent4
43-
gate --> agent5
4442
agent4 --> agent2
4543
agent4 --> agent3
4644
agent2 --> parallel_workflow_aggregator
4745
parallel_workflow_aggregator --> parallel_workflow_2_aggregator
46+
parallel_workflow_aggregator --> agent5
4847
agent3 --> parallel_workflow_aggregator
4948
parallel_workflow_2_aggregator --> out
5049
agent5 --> parallel_workflow_2_aggregator`;

examples/complex_workflow/diagram.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,11 @@ agent1 --> gate
2323
gate -->|warning| agent4
2424
gate -->|error| agent2
2525
gate -->|error| agent3
26-
gate --> agent4
27-
gate --> agent5
2826
agent4 --> agent2
2927
agent4 --> agent3
3028
agent2 --> parallel_workflow_aggregator
3129
parallel_workflow_aggregator --> parallel_workflow_2_aggregator
30+
parallel_workflow_aggregator --> agent5
3231
agent3 --> parallel_workflow_aggregator
3332
parallel_workflow_2_aggregator --> out
3433
agent5 --> parallel_workflow_2_aggregator

examples/complex_workflow/generator.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,6 @@ class Agent5 < MARS::AgentStep
6262
# Generate and save the diagram
6363
diagram = MARS::Rendering::Mermaid.new(main_workflow).render
6464
File.write("examples/complex_workflow/diagram.md", diagram)
65-
MARS::Rendering::Html.new(main_workflow).write("examples/complex_workflow/diagram.html")
6665
puts "Complex workflow diagram saved to: examples/complex_workflow/diagram.md"
66+
MARS::Rendering::Html.new(main_workflow).write("examples/complex_workflow/diagram.html")
67+
puts "Complex workflow beautiful mermaid diagram saved to: examples/complex_workflow/diagram.html"

examples/parallel_workflow/generator.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,6 @@ class Agent3 < MARS::AgentStep
3030
# Generate and save the diagram
3131
diagram = MARS::Rendering::Mermaid.new(parallel_workflow).render
3232
File.write("examples/parallel_workflow/diagram.md", diagram)
33-
MARS::Rendering::Html.new(parallel_workflow).write("examples/parallel_workflow/diagram.html")
3433
puts "Parallel workflow diagram saved to: examples/parallel_workflow/diagram.md"
34+
MARS::Rendering::Html.new(parallel_workflow).write("examples/parallel_workflow/diagram.html")
35+
puts "Parallel workflow beautiful mermaid diagram saved to: examples/parallel_workflow/diagram.html"

examples/simple_workflow/generator.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,6 @@ class Agent3 < MARS::AgentStep
4141
# Generate and save the diagram
4242
diagram = MARS::Rendering::Mermaid.new(main_workflow).render
4343
File.write("examples/simple_workflow/diagram.md", diagram)
44-
MARS::Rendering::Html.new(main_workflow).write("examples/simple_workflow/diagram.html")
4544
puts "Simple workflow diagram saved to: examples/simple_workflow/diagram.md"
45+
MARS::Rendering::Html.new(main_workflow).write("examples/simple_workflow/diagram.html")
46+
puts "Simple workflow beautiful mermaid diagram saved to: examples/simple_workflow/diagram.html"

lib/mars/rendering/graph/builder.rb

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# frozen_string_literal: true
22

3+
require "set"
4+
35
module MARS
46
module Rendering
57
module Graph
@@ -14,9 +16,10 @@ def initialize
1416

1517
def add_edge(from, to, value = nil)
1618
return unless from && to
19+
return if adjacency[from].include?([to, value])
20+
return if reachable?(to, from)
1721

18-
# can we avoid visiting the node twice instead?
19-
adjacency[from] << [to, value] unless adjacency[from].include?([to, value])
22+
adjacency[from] << [to, value]
2023
adjacency[to] = [] unless adjacency[to]
2124
end
2225

@@ -43,6 +46,23 @@ def add_node_to_subgraph(id, node_id)
4346
def node_in_any_subgraph?(node_id)
4447
subgraphs.values.any? { |sg| sg.nodes.include?(node_id) }
4548
end
49+
50+
def reachable?(from, target)
51+
visited = Set.new
52+
queue = [from]
53+
54+
while queue.any?
55+
current = queue.shift
56+
next if visited.include?(current)
57+
58+
visited << current
59+
return true if current == target
60+
61+
adjacency[current]&.each { |(to, _)| queue << to }
62+
end
63+
64+
false
65+
end
4666
end
4767
end
4868
end

lib/mars/rendering/graph/sequential_workflow.rb

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,30 @@ def to_graph(builder, parent_id: nil, value: nil)
2020

2121
def build_steps_graph(builder, parent_id, value)
2222
sink_nodes = []
23+
extra_parents = []
2324

2425
steps.each do |step|
2526
sink_nodes = step.to_graph(builder, parent_id: parent_id, value: value)
26-
value = nil # We don't want to pass the value to subsequent steps
27-
parent_id = step.node_id
27+
extra_parents.each { |ep| builder.add_edge(ep, step.node_id) }
2828

29-
builder.add_node_to_subgraph(node_id, step.node_id)
29+
value = nil
30+
parent_id, extra_parents = process_sink_nodes(sink_nodes, step)
3031

31-
sink_nodes.each { |sink_node| builder.add_node_to_subgraph(node_id, sink_node) }
32+
add_to_subgraph(builder, step, sink_nodes)
3233
end
3334

3435
[parent_id, value, sink_nodes]
3536
end
37+
38+
def process_sink_nodes(sink_nodes, step)
39+
unique_sinks = sink_nodes.uniq
40+
[unique_sinks.first || step.node_id, unique_sinks.drop(1)]
41+
end
42+
43+
def add_to_subgraph(builder, step, sink_nodes)
44+
builder.add_node_to_subgraph(node_id, step.node_id)
45+
sink_nodes.each { |sink_node| builder.add_node_to_subgraph(node_id, sink_node) }
46+
end
3647
end
3748
end
3849
end

0 commit comments

Comments
 (0)