mars_rb 0.1.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +38 -0
- data/README.md +183 -12
- data/examples/complex_llm_workflow/diagram.md +32 -0
- data/examples/complex_llm_workflow/generator.rb +112 -0
- data/examples/complex_workflow/diagram.md +47 -0
- data/examples/complex_workflow/generator.rb +108 -0
- data/examples/parallel_workflow/diagram.md +21 -0
- data/examples/parallel_workflow/generator.rb +33 -0
- data/examples/simple_workflow/diagram.md +26 -0
- data/examples/simple_workflow/generator.rb +48 -0
- data/lib/mars/agent_step.rb +15 -0
- data/lib/mars/aggregator.rb +24 -0
- data/lib/mars/execution_context.rb +35 -0
- data/lib/mars/formatter.rb +13 -0
- data/lib/mars/gate.rb +52 -0
- data/lib/mars/hooks.rb +35 -0
- data/lib/mars/rendering/graph/agent_step.rb +18 -0
- data/lib/mars/rendering/graph/aggregator.rb +17 -0
- data/lib/mars/rendering/graph/base.rb +32 -0
- data/lib/mars/rendering/graph/builder.rb +43 -0
- data/lib/mars/rendering/graph/gate.rb +22 -0
- data/lib/mars/rendering/graph/node.rb +22 -0
- data/lib/mars/rendering/graph/parallel_workflow.rb +34 -0
- data/lib/mars/rendering/graph/runnable.rb +18 -0
- data/lib/mars/rendering/graph/sequential_workflow.rb +39 -0
- data/lib/mars/rendering/graph/subgraph.rb +17 -0
- data/lib/mars/rendering/graph.rb +16 -0
- data/lib/mars/rendering/html.rb +37 -0
- data/lib/mars/rendering/mermaid.rb +71 -0
- data/lib/mars/runnable.rb +26 -2
- data/lib/mars/version.rb +2 -2
- data/lib/mars/workflows/aggregate_error.rb +14 -0
- data/lib/mars/workflows/parallel.rb +61 -0
- data/lib/mars/workflows/sequential.rb +26 -8
- data/lib/mars.rb +12 -3
- data/lib/mars_rb.rb +3 -0
- data/sig/mars.rbs +1 -1
- metadata +81 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d9e07825e09769cad8d18f5b603b35031e2b1865f3cefab4232820e6740b5d09
|
|
4
|
+
data.tar.gz: 1245c01d79b166086c1138ce225dac87930f2f0e0cb24c8019908212a7235c34
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ef9998fc5881a51188f3ba533271cfee3f362fc42f7cba7fec34e03d93043758048fde5b9f5ea9e1a5a581c0539f64e1b1828fe5dc5bcb86dcb06aa9ed744e2d
|
|
7
|
+
data.tar.gz: 63473c75d3bd7b990c4aded7589c6b6edc201ffa36c6aef0bae62640d210278b913b5a45b229890f54956ce2e749620dd3f256c1be4773cf000b0d8b84675a0c
|
data/.rubocop.yml
CHANGED
|
@@ -1,9 +1,35 @@
|
|
|
1
|
+
plugins:
|
|
2
|
+
- rubocop-rake
|
|
3
|
+
- rubocop-rspec
|
|
4
|
+
|
|
1
5
|
AllCops:
|
|
2
6
|
TargetRubyVersion: 3.1
|
|
7
|
+
NewCops: enable
|
|
8
|
+
Exclude:
|
|
9
|
+
- "examples/**/*"
|
|
10
|
+
- "vendor/**/*"
|
|
3
11
|
|
|
4
12
|
Lint/MissingSuper:
|
|
5
13
|
Enabled: false
|
|
6
14
|
|
|
15
|
+
Metrics/MethodLength:
|
|
16
|
+
Enabled: true
|
|
17
|
+
Exclude:
|
|
18
|
+
- "lib/mars/workflows/parallel.rb"
|
|
19
|
+
- "lib/mars/rendering/html.rb"
|
|
20
|
+
- "lib/mars/workflows/sequential.rb"
|
|
21
|
+
|
|
22
|
+
Metrics/ParameterLists:
|
|
23
|
+
Enabled: true
|
|
24
|
+
Exclude:
|
|
25
|
+
- "lib/mars/agent.rb"
|
|
26
|
+
|
|
27
|
+
Metrics/AbcSize:
|
|
28
|
+
Enabled: true
|
|
29
|
+
Exclude:
|
|
30
|
+
- "lib/mars/rendering/graph/parallel_workflow.rb"
|
|
31
|
+
- "lib/mars/workflows/sequential.rb"
|
|
32
|
+
|
|
7
33
|
Style/Documentation:
|
|
8
34
|
Enabled: false
|
|
9
35
|
|
|
@@ -12,3 +38,15 @@ Style/StringLiterals:
|
|
|
12
38
|
|
|
13
39
|
Style/StringLiteralsInInterpolation:
|
|
14
40
|
EnforcedStyle: double_quotes
|
|
41
|
+
|
|
42
|
+
RSpec/ExampleLength:
|
|
43
|
+
Enabled: false
|
|
44
|
+
|
|
45
|
+
RSpec/MultipleExpectations:
|
|
46
|
+
Enabled: false
|
|
47
|
+
|
|
48
|
+
RSpec/MultipleMemoizedHelpers:
|
|
49
|
+
Enabled: false
|
|
50
|
+
|
|
51
|
+
RSpec/VerifiedDoubleReference:
|
|
52
|
+
Enabled: false
|
data/README.md
CHANGED
|
@@ -1,28 +1,165 @@
|
|
|
1
|
-
#
|
|
1
|
+
# MARS (Multi-Agent Ruby SDK)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://badge.fury.io/rb/mars_rb)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
MARS (Multi-Agent Ruby SDK) provides a comprehensive framework for developers to implement multi-agent solutions using pure Ruby. It offers a simple, intuitive API for orchestrating multiple agents with support for sequential and parallel workflows, conditional branching, and visual workflow diagrams.
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- 🤖 **Agent Orchestration**: Coordinate multiple agents with ease
|
|
11
|
+
- 🔄 **Sequential Workflows**: Chain agents to execute tasks in order
|
|
12
|
+
- ⚡ **Parallel Workflows**: Run multiple agents concurrently
|
|
13
|
+
- 🚦 **Conditional Gates**: Branch workflows based on runtime conditions
|
|
14
|
+
- 🔧 **Aggregators**: Combine results from parallel operations
|
|
15
|
+
- 📊 **Visual Diagrams**: Generate Mermaid diagrams of your workflows
|
|
16
|
+
- 🔌 **LLM Integration**: Built-in support for LLM agents via [ruby_llm](https://github.com/gbaptista/ruby_llm)
|
|
17
|
+
- 🛠️ **Tools & Schemas**: Define custom tools and structured outputs for agents
|
|
18
|
+
|
|
19
|
+
## Requirements
|
|
20
|
+
|
|
21
|
+
- Ruby >= 3.1.0
|
|
6
22
|
|
|
7
23
|
## Installation
|
|
8
24
|
|
|
9
|
-
|
|
25
|
+
Add this line to your application's Gemfile:
|
|
10
26
|
|
|
11
|
-
|
|
27
|
+
```ruby
|
|
28
|
+
gem 'mars_rb'
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
And then execute:
|
|
12
32
|
|
|
13
33
|
```bash
|
|
14
|
-
bundle
|
|
34
|
+
bundle install
|
|
15
35
|
```
|
|
16
36
|
|
|
17
|
-
|
|
37
|
+
Or install it yourself as:
|
|
18
38
|
|
|
19
39
|
```bash
|
|
20
|
-
gem install
|
|
40
|
+
gem install mars_rb
|
|
21
41
|
```
|
|
22
42
|
|
|
23
|
-
##
|
|
43
|
+
## Quick Start
|
|
44
|
+
|
|
45
|
+
Here's a simple example to get you started:
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
require 'mars'
|
|
49
|
+
|
|
50
|
+
# Define agents
|
|
51
|
+
class Agent1 < MARS::Agent
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
class Agent2 < MARS::Agent
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
class Agent3 < MARS::Agent
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Create agents
|
|
61
|
+
agent1 = Agent1.new
|
|
62
|
+
agent2 = Agent2.new
|
|
63
|
+
agent3 = Agent3.new
|
|
64
|
+
|
|
65
|
+
# Create a sequential workflow
|
|
66
|
+
workflow = MARS::Workflows::Sequential.new(
|
|
67
|
+
"My First Workflow",
|
|
68
|
+
steps: [agent1, agent2, agent3]
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# Run the workflow
|
|
72
|
+
result = workflow.run("Your input here")
|
|
73
|
+
```
|
|
24
74
|
|
|
25
|
-
|
|
75
|
+
## Core Concepts
|
|
76
|
+
|
|
77
|
+
### Agents
|
|
78
|
+
|
|
79
|
+
Agents are the basic building blocks of MARS. They represent individual units of work:
|
|
80
|
+
|
|
81
|
+
```ruby
|
|
82
|
+
class CustomAgent < MARS::Agent
|
|
83
|
+
def system_prompt
|
|
84
|
+
"You are a helpful assistant"
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
agent = CustomAgent.new(
|
|
89
|
+
options: { model: "gpt-4o" }
|
|
90
|
+
)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Sequential Workflows
|
|
94
|
+
|
|
95
|
+
Execute agents one after another, passing outputs as inputs:
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
sequential = MARS::Workflows::Sequential.new(
|
|
99
|
+
"Sequential Pipeline",
|
|
100
|
+
steps: [agent1, agent2, agent3]
|
|
101
|
+
)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Parallel Workflows
|
|
105
|
+
|
|
106
|
+
Run multiple agents concurrently and aggregate their results:
|
|
107
|
+
|
|
108
|
+
```ruby
|
|
109
|
+
aggregator = MARS::Aggregator.new(
|
|
110
|
+
"Results Aggregator",
|
|
111
|
+
operation: lambda { |results| results.join(", ") }
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
parallel = MARS::Workflows::Parallel.new(
|
|
115
|
+
"Parallel Pipeline",
|
|
116
|
+
steps: [agent1, agent2, agent3],
|
|
117
|
+
aggregator: aggregator
|
|
118
|
+
)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Gates
|
|
122
|
+
|
|
123
|
+
Gates act as guards that either let the workflow continue or divert to a fallback path:
|
|
124
|
+
|
|
125
|
+
```ruby
|
|
126
|
+
gate = MARS::Gate.new(
|
|
127
|
+
"Validation Gate",
|
|
128
|
+
check: ->(input) { :failure unless input[:score] > 0.5 },
|
|
129
|
+
fallbacks: {
|
|
130
|
+
failure: failure_workflow
|
|
131
|
+
}
|
|
132
|
+
)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Control halt scope — `:local` (default) stops only the parent workflow, `:global` propagates to the root:
|
|
136
|
+
|
|
137
|
+
```ruby
|
|
138
|
+
gate = MARS::Gate.new(
|
|
139
|
+
"Critical Gate",
|
|
140
|
+
check: ->(input) { :error unless input[:valid] },
|
|
141
|
+
fallbacks: { error: error_workflow },
|
|
142
|
+
halt_scope: :global
|
|
143
|
+
)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Visualization
|
|
147
|
+
|
|
148
|
+
Generate Mermaid diagrams to visualize your workflows:
|
|
149
|
+
|
|
150
|
+
```ruby
|
|
151
|
+
diagram = MARS::Rendering::Mermaid.new(workflow).render
|
|
152
|
+
File.write("workflow_diagram.md", diagram)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Examples
|
|
156
|
+
|
|
157
|
+
Check out the [examples](examples/) directory for more detailed examples:
|
|
158
|
+
|
|
159
|
+
- [Simple Workflow](examples/simple_workflow/) - Basic sequential workflow with gates
|
|
160
|
+
- [Parallel Workflow](examples/parallel_workflow/) - Concurrent agent execution
|
|
161
|
+
- [Complex Workflow](examples/complex_workflow/) - Nested workflows with multiple gates
|
|
162
|
+
- [Complex LLM Workflow](examples/complex_llm_workflow/) - Real-world LLM integration with tools and schemas
|
|
26
163
|
|
|
27
164
|
## Development
|
|
28
165
|
|
|
@@ -30,14 +167,48 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
|
30
167
|
|
|
31
168
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
32
169
|
|
|
170
|
+
## Testing
|
|
171
|
+
|
|
172
|
+
Run the test suite with:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
bundle exec rake spec
|
|
176
|
+
```
|
|
177
|
+
|
|
33
178
|
## Contributing
|
|
34
179
|
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
|
180
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/rootstrap/mars. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/rootstrap/mars/blob/main/CODE_OF_CONDUCT.md).
|
|
181
|
+
|
|
182
|
+
To contribute:
|
|
183
|
+
|
|
184
|
+
1. Fork the repository
|
|
185
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
186
|
+
3. Commit your changes (`git commit -am 'Add some amazing feature'`)
|
|
187
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
188
|
+
5. Open a Pull Request
|
|
189
|
+
|
|
190
|
+
Please make sure to:
|
|
191
|
+
- Write tests for new features
|
|
192
|
+
- Update documentation as needed
|
|
193
|
+
- Follow the existing code style
|
|
194
|
+
- Ensure all tests pass before submitting
|
|
36
195
|
|
|
37
196
|
## License
|
|
38
197
|
|
|
39
198
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
40
199
|
|
|
200
|
+
## Support
|
|
201
|
+
|
|
202
|
+
- 🐛 [Issue Tracker](https://github.com/rootstrap/mars/issues)
|
|
203
|
+
|
|
204
|
+
## Related Projects
|
|
205
|
+
|
|
206
|
+
- [ruby_llm](https://github.com/gbaptista/ruby_llm) - Ruby interface for LLM providers
|
|
207
|
+
|
|
41
208
|
## Code of Conduct
|
|
42
209
|
|
|
43
|
-
Everyone interacting in the
|
|
210
|
+
Everyone interacting in the MARS project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/rootstrap/mars/blob/main/CODE_OF_CONDUCT.md).
|
|
211
|
+
|
|
212
|
+
## Credits
|
|
213
|
+
|
|
214
|
+
Created and maintained by [Rootstrap](https://www.rootstrap.com).
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
```mermaid
|
|
2
|
+
flowchart LR
|
|
3
|
+
in((In))
|
|
4
|
+
out((Out))
|
|
5
|
+
agent1[agent1]
|
|
6
|
+
gate{Gate}
|
|
7
|
+
parallel_workflow_aggregator[Parallel workflow Aggregator]
|
|
8
|
+
agent2[agent2]
|
|
9
|
+
agent3[agent3]
|
|
10
|
+
agent4[agent4]
|
|
11
|
+
subgraph parallel_workflow["Parallel workflow"]
|
|
12
|
+
agent2
|
|
13
|
+
agent3
|
|
14
|
+
agent4
|
|
15
|
+
end
|
|
16
|
+
subgraph sequential_workflow["Sequential workflow"]
|
|
17
|
+
agent1
|
|
18
|
+
gate
|
|
19
|
+
parallel_workflow
|
|
20
|
+
parallel_workflow_aggregator
|
|
21
|
+
end
|
|
22
|
+
in --> agent1
|
|
23
|
+
agent1 --> gate
|
|
24
|
+
gate -->|failure| out
|
|
25
|
+
gate --> agent2
|
|
26
|
+
gate --> agent3
|
|
27
|
+
gate --> agent4
|
|
28
|
+
agent2 --> parallel_workflow_aggregator
|
|
29
|
+
parallel_workflow_aggregator --> out
|
|
30
|
+
agent3 --> parallel_workflow_aggregator
|
|
31
|
+
agent4 --> parallel_workflow_aggregator
|
|
32
|
+
```
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "../../lib/mars"
|
|
5
|
+
|
|
6
|
+
RubyLLM.configure do |config|
|
|
7
|
+
config.openai_api_key = ENV.fetch("OPENAI_API_KEY", nil)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Define schema for sports
|
|
11
|
+
class SportsSchema < RubyLLM::Schema
|
|
12
|
+
array :sports do
|
|
13
|
+
object do
|
|
14
|
+
string :name
|
|
15
|
+
array :top_3_players do
|
|
16
|
+
string :name
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Define weather tool
|
|
23
|
+
class Weather < RubyLLM::Tool
|
|
24
|
+
description "Gets current weather for a location"
|
|
25
|
+
|
|
26
|
+
params do
|
|
27
|
+
string :latitude, description: "Latitude (e.g., 52.5200)"
|
|
28
|
+
string :longitude, description: "Longitude (e.g., 13.4050)"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def execute(latitude:, longitude:)
|
|
32
|
+
url = "https://api.open-meteo.com/v1/forecast?latitude=#{latitude}&longitude=#{longitude}¤t=temperature_2m,wind_speed_10m"
|
|
33
|
+
response = Net::HTTP.get_response(URI(url))
|
|
34
|
+
JSON.parse(response.body)
|
|
35
|
+
rescue StandardError => e
|
|
36
|
+
{ error: e.message }
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class ExampleAgent < RubyLLM::Agent
|
|
41
|
+
instructions "You are a helpful assistant that can answer questions.
|
|
42
|
+
When asked about a country, only answer with its name."
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class Agent1 < MARS::AgentStep
|
|
46
|
+
agent ExampleAgent
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class FoodAgent < RubyLLM::Agent
|
|
50
|
+
instructions "You are a helpful assistant that can answer questions and help with tasks.
|
|
51
|
+
Return information about the typical food of the country."
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
class Agent2 < MARS::AgentStep
|
|
55
|
+
agent FoodAgent
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
class SportsAgent < RubyLLM::Agent
|
|
59
|
+
instructions "You are a helpful assistant that can answer questions and help with tasks.
|
|
60
|
+
Return information about the popular sports of the country."
|
|
61
|
+
schema SportsSchema
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
class Agent3 < MARS::AgentStep
|
|
65
|
+
agent SportsAgent
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
class WeatherAgent < RubyLLM::Agent
|
|
69
|
+
instructions "You are a helpful assistant that can answer questions and help with tasks.
|
|
70
|
+
Return the current weather of the country's capital."
|
|
71
|
+
tools Weather
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
class Agent4 < MARS::AgentStep
|
|
75
|
+
agent WeatherAgent
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Create the LLMs
|
|
79
|
+
llm1 = Agent1.new
|
|
80
|
+
llm2 = Agent2.new
|
|
81
|
+
llm3 = Agent3.new
|
|
82
|
+
llm4 = Agent4.new
|
|
83
|
+
|
|
84
|
+
parallel_workflow = MARS::Workflows::Parallel.new(
|
|
85
|
+
"Parallel workflow",
|
|
86
|
+
steps: [llm2, llm3, llm4]
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
error_workflow = MARS::Workflows::Sequential.new(
|
|
90
|
+
"Error workflow",
|
|
91
|
+
steps: []
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
gate = MARS::Gate.new(
|
|
95
|
+
check: ->(input) { :failure unless input.split.length < 10 },
|
|
96
|
+
fallbacks: {
|
|
97
|
+
failure: error_workflow
|
|
98
|
+
}
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
sequential_workflow = MARS::Workflows::Sequential.new(
|
|
102
|
+
"Sequential workflow",
|
|
103
|
+
steps: [llm1, gate, parallel_workflow]
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
# Generate and save the diagram
|
|
107
|
+
diagram = MARS::Rendering::Mermaid.new(sequential_workflow).render
|
|
108
|
+
File.write("examples/complex_llm_workflow/diagram.md", diagram)
|
|
109
|
+
puts "Complex workflow diagram saved to: examples/complex_llm_workflow/diagram.md"
|
|
110
|
+
|
|
111
|
+
# Run the workflow
|
|
112
|
+
puts sequential_workflow.run("Which is the largest country in Europe?")
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
```mermaid
|
|
2
|
+
flowchart LR
|
|
3
|
+
in((In))
|
|
4
|
+
out((Out))
|
|
5
|
+
step1[step1]
|
|
6
|
+
gate{Gate}
|
|
7
|
+
step4[step4]
|
|
8
|
+
parallel_workflow_aggregator[Parallel workflow Aggregator]
|
|
9
|
+
step2[step2]
|
|
10
|
+
step3[step3]
|
|
11
|
+
parallel_workflow_2_aggregator[Parallel workflow 2 Aggregator]
|
|
12
|
+
step5[step5]
|
|
13
|
+
subgraph parallel_workflow_2["Parallel workflow 2"]
|
|
14
|
+
sequential_workflow
|
|
15
|
+
step5
|
|
16
|
+
end
|
|
17
|
+
subgraph parallel_workflow["Parallel workflow"]
|
|
18
|
+
step2
|
|
19
|
+
step3
|
|
20
|
+
end
|
|
21
|
+
subgraph sequential_workflow["Sequential workflow"]
|
|
22
|
+
step4
|
|
23
|
+
parallel_workflow
|
|
24
|
+
parallel_workflow_aggregator
|
|
25
|
+
end
|
|
26
|
+
subgraph main_pipeline["Main Pipeline"]
|
|
27
|
+
step1
|
|
28
|
+
gate
|
|
29
|
+
parallel_workflow_aggregator
|
|
30
|
+
parallel_workflow_2
|
|
31
|
+
parallel_workflow_2_aggregator
|
|
32
|
+
end
|
|
33
|
+
in --> step1
|
|
34
|
+
step1 --> gate
|
|
35
|
+
gate -->|warning| step4
|
|
36
|
+
gate -->|error| step2
|
|
37
|
+
gate -->|error| step3
|
|
38
|
+
gate --> step4
|
|
39
|
+
gate --> step5
|
|
40
|
+
step4 --> step2
|
|
41
|
+
step4 --> step3
|
|
42
|
+
step2 --> parallel_workflow_aggregator
|
|
43
|
+
parallel_workflow_aggregator --> parallel_workflow_2_aggregator
|
|
44
|
+
step3 --> parallel_workflow_aggregator
|
|
45
|
+
parallel_workflow_2_aggregator --> out
|
|
46
|
+
step5 --> parallel_workflow_2_aggregator
|
|
47
|
+
```
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "../../lib/mars"
|
|
5
|
+
|
|
6
|
+
class Step1Formatter < MARS::Formatter
|
|
7
|
+
def format_output(context)
|
|
8
|
+
context + ['formatted']
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class Step1 < MARS::Runnable
|
|
13
|
+
after_run do |context, result|
|
|
14
|
+
puts "after run from Step1 #{result}"
|
|
15
|
+
end
|
|
16
|
+
formatter Step1Formatter
|
|
17
|
+
|
|
18
|
+
def run(context)
|
|
19
|
+
context.current_input + ['step1']
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class Step2 < MARS::Runnable
|
|
24
|
+
after_run do |context, result|
|
|
25
|
+
puts "after run from Step2 #{result}"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def run(context)
|
|
29
|
+
context.current_input + ['step2']
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class Step3 < MARS::Runnable
|
|
34
|
+
after_run do |context, result|
|
|
35
|
+
puts "after run from Step3 #{result}"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def run(context)
|
|
39
|
+
context.current_input + ['step3']
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
class Step4 < MARS::Runnable
|
|
44
|
+
after_run do |context, result|
|
|
45
|
+
puts "after run from Step4 #{result}"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def run(context)
|
|
49
|
+
context.current_input + ['step4']
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
class Step5 < MARS::Runnable
|
|
54
|
+
after_run do |context, result|
|
|
55
|
+
puts "after run from Step5 #{result}"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def run(context)
|
|
59
|
+
context.current_input + ['step5']
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Create the Steps
|
|
64
|
+
step1 = Step1.new
|
|
65
|
+
step2 = Step2.new
|
|
66
|
+
step3 = Step3.new
|
|
67
|
+
step4 = Step4.new
|
|
68
|
+
step5 = Step5.new
|
|
69
|
+
|
|
70
|
+
# Create a parallel workflow (STEP 2 x STEP 3)
|
|
71
|
+
parallel_workflow = MARS::Workflows::Parallel.new(
|
|
72
|
+
"Parallel workflow",
|
|
73
|
+
steps: [step2, step3]
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Create a sequential workflow (Parallel workflow -> LLM 4)
|
|
77
|
+
sequential_workflow = MARS::Workflows::Sequential.new(
|
|
78
|
+
"Sequential workflow",
|
|
79
|
+
steps: [step4, parallel_workflow]
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# Create a parallel workflow (Sequential workflow x LLM 5)
|
|
83
|
+
parallel_workflow2 = MARS::Workflows::Parallel.new(
|
|
84
|
+
"Parallel workflow 2",
|
|
85
|
+
steps: [sequential_workflow, step5]
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# Create the gate that decides between exit or continue
|
|
89
|
+
gate = MARS::Gate.new(
|
|
90
|
+
check: ->(input) { :default if input == ["start", "step1", "formatted"] },
|
|
91
|
+
fallbacks: {
|
|
92
|
+
warning: sequential_workflow,
|
|
93
|
+
error: parallel_workflow
|
|
94
|
+
}
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# Create the main workflow: LLM 1 -> Gate
|
|
98
|
+
main_workflow = MARS::Workflows::Sequential.new(
|
|
99
|
+
"Main Pipeline",
|
|
100
|
+
steps: [step1, gate, parallel_workflow2]
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
main_workflow.run(["start"])
|
|
104
|
+
|
|
105
|
+
# Generate and save the diagram
|
|
106
|
+
diagram = MARS::Rendering::Mermaid.new(main_workflow).render
|
|
107
|
+
File.write("examples/complex_workflow/diagram.md", diagram)
|
|
108
|
+
puts "Complex workflow diagram saved to: examples/complex_workflow/diagram.md"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
```mermaid
|
|
2
|
+
flowchart LR
|
|
3
|
+
in((In))
|
|
4
|
+
out((Out))
|
|
5
|
+
aggregator[Aggregator]
|
|
6
|
+
agent1[agent1]
|
|
7
|
+
agent2[agent2]
|
|
8
|
+
agent3[agent3]
|
|
9
|
+
subgraph parallel_workflow["Parallel workflow"]
|
|
10
|
+
agent1
|
|
11
|
+
agent2
|
|
12
|
+
agent3
|
|
13
|
+
end
|
|
14
|
+
in --> agent1
|
|
15
|
+
in --> agent2
|
|
16
|
+
in --> agent3
|
|
17
|
+
agent1 --> aggregator
|
|
18
|
+
aggregator --> out
|
|
19
|
+
agent2 --> aggregator
|
|
20
|
+
agent3 --> aggregator
|
|
21
|
+
```
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "../../lib/mars"
|
|
5
|
+
|
|
6
|
+
# Define the LLMs
|
|
7
|
+
class Agent1 < MARS::AgentStep
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
class Agent2 < MARS::AgentStep
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class Agent3 < MARS::AgentStep
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Create the LLMs
|
|
17
|
+
llm1 = Agent1.new
|
|
18
|
+
llm2 = Agent2.new
|
|
19
|
+
llm3 = Agent3.new
|
|
20
|
+
|
|
21
|
+
aggregator = MARS::Aggregator.new("Aggregator", operation: lambda(&:sum))
|
|
22
|
+
|
|
23
|
+
# Create the parallel workflow (LLM 1, LLM 2, LLM 3)
|
|
24
|
+
parallel_workflow = MARS::Workflows::Parallel.new(
|
|
25
|
+
"Parallel workflow",
|
|
26
|
+
steps: [llm1, llm2, llm3],
|
|
27
|
+
aggregator: aggregator
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
# Generate and save the diagram
|
|
31
|
+
diagram = MARS::Rendering::Mermaid.new(parallel_workflow).render
|
|
32
|
+
File.write("examples/parallel_workflow/diagram.md", diagram)
|
|
33
|
+
puts "Parallel workflow diagram saved to: examples/parallel_workflow/diagram.md"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
```mermaid
|
|
2
|
+
flowchart LR
|
|
3
|
+
in((In))
|
|
4
|
+
out((Out))
|
|
5
|
+
agent1[agent1]
|
|
6
|
+
gate{Gate}
|
|
7
|
+
agent4[agent4]
|
|
8
|
+
agent2[agent2]
|
|
9
|
+
agent3[agent3]
|
|
10
|
+
subgraph failure_workflow["Failure workflow"]
|
|
11
|
+
agent4
|
|
12
|
+
end
|
|
13
|
+
subgraph main_pipeline["Main Pipeline"]
|
|
14
|
+
agent1
|
|
15
|
+
gate
|
|
16
|
+
agent4
|
|
17
|
+
agent2
|
|
18
|
+
agent3
|
|
19
|
+
end
|
|
20
|
+
in --> agent1
|
|
21
|
+
agent1 --> gate
|
|
22
|
+
gate -->|failure| agent4
|
|
23
|
+
gate --> agent2
|
|
24
|
+
agent2 --> agent3
|
|
25
|
+
agent3 --> out
|
|
26
|
+
```
|