ruby-openai-swarm 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/rspec.yml +27 -0
- data/.gitignore +32 -0
- data/.rspec +3 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +56 -0
- data/README.md +141 -0
- data/Rakefile +1 -0
- data/assets/logo-swarm.png +0 -0
- data/bin/console +14 -0
- data/bin/setup +14 -0
- data/examples/basic/README.md +0 -0
- data/examples/basic/agent_handoff.rb +41 -0
- data/examples/basic/bare_minimum.rb +27 -0
- data/examples/basic/context_variables.rb +67 -0
- data/examples/basic/function_calling.rb +32 -0
- data/examples/basic/simple_loop_no_helpers.rb +38 -0
- data/examples/triage_agent/README.md +34 -0
- data/examples/triage_agent/agents.rb +84 -0
- data/examples/triage_agent/main.rb +3 -0
- data/examples/weather_agent/README.md +0 -0
- data/examples/weather_agent/agents.rb +59 -0
- data/examples/weather_agent/run.rb +0 -0
- data/lib/ruby-openai-swarm/agent.rb +21 -0
- data/lib/ruby-openai-swarm/core.rb +269 -0
- data/lib/ruby-openai-swarm/function_descriptor.rb +10 -0
- data/lib/ruby-openai-swarm/repl.rb +90 -0
- data/lib/ruby-openai-swarm/response.rb +11 -0
- data/lib/ruby-openai-swarm/result.rb +11 -0
- data/lib/ruby-openai-swarm/util.rb +78 -0
- data/lib/ruby-openai-swarm/version.rb +4 -0
- data/lib/ruby-openai-swarm.rb +18 -0
- data/ruby-openai-swarm.gemspec +28 -0
- metadata +135 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: df437749d299781cdac584185c29c03d6bcdd0ae8ae547395d23656c7a5ff308
|
4
|
+
data.tar.gz: ad06764c570c061aab9077ae280bad27b174a740f5721616ab0f3fb052eeb638
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 22dff45e435bbe72d598cae52e1924f1921f9d4c07e0795c6b0d58368c72050d33cb63f999c936465fdf5a9bd1065d7dcbeb8696259b9468f8c5b325bfb686d6
|
7
|
+
data.tar.gz: c578b9500e861456618f7a576756963cae6bfa53249123b23c7a57363c4cc4993dd796865b42c43d2ff8edd38d9b9d6823ec6b4388da26d43858a38fec9a922f
|
@@ -0,0 +1,27 @@
|
|
1
|
+
name: RSpec Tests
|
2
|
+
on: [push, pull_request]
|
3
|
+
|
4
|
+
jobs:
|
5
|
+
test:
|
6
|
+
runs-on: ubuntu-latest
|
7
|
+
strategy:
|
8
|
+
matrix:
|
9
|
+
ruby-version: ['2.7', '3.0', '3.1']
|
10
|
+
|
11
|
+
steps:
|
12
|
+
- uses: actions/checkout@v3
|
13
|
+
- name: Set up Ruby ${{ matrix.ruby-version }}
|
14
|
+
uses: ruby/setup-ruby@v1
|
15
|
+
with:
|
16
|
+
ruby-version: ${{ matrix.ruby-version }}
|
17
|
+
bundler-cache: true
|
18
|
+
- name: Install dependencies
|
19
|
+
run: bundle install
|
20
|
+
- name: Run tests
|
21
|
+
run: bundle exec rspec
|
22
|
+
- name: Upload test results
|
23
|
+
uses: actions/upload-artifact@v3
|
24
|
+
if: failure()
|
25
|
+
with:
|
26
|
+
name: rspec-results
|
27
|
+
path: tmp/rspec_results
|
data/.gitignore
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/spec/examples.txt
|
9
|
+
/test/tmp/
|
10
|
+
/test/version_tmp/
|
11
|
+
/tmp/
|
12
|
+
.byebug_history
|
13
|
+
.dat*
|
14
|
+
.repl_history
|
15
|
+
build/
|
16
|
+
*.bridgesupport
|
17
|
+
build-iPhoneOS/
|
18
|
+
build-iPhoneSimulator/
|
19
|
+
/.yardoc/
|
20
|
+
/_yardoc/
|
21
|
+
/doc/
|
22
|
+
/rdoc/
|
23
|
+
/.bundle/
|
24
|
+
/vendor/bundle
|
25
|
+
/lib/bundler/man/
|
26
|
+
.rvmrc
|
27
|
+
.ruby-version
|
28
|
+
.ruby-gemset
|
29
|
+
/tmp
|
30
|
+
|
31
|
+
# rspec failure tracking
|
32
|
+
.rspec_status
|
data/.rspec
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
ruby-openai-swarm (0.1.0)
|
5
|
+
ruby-openai (~> 7.3)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
base64 (0.2.0)
|
11
|
+
coderay (1.1.3)
|
12
|
+
diff-lcs (1.5.1)
|
13
|
+
event_stream_parser (1.0.0)
|
14
|
+
faraday (2.8.1)
|
15
|
+
base64
|
16
|
+
faraday-net_http (>= 2.0, < 3.1)
|
17
|
+
ruby2_keywords (>= 0.0.4)
|
18
|
+
faraday-multipart (1.0.4)
|
19
|
+
multipart-post (~> 2)
|
20
|
+
faraday-net_http (3.0.2)
|
21
|
+
method_source (1.1.0)
|
22
|
+
multipart-post (2.4.1)
|
23
|
+
pry (0.14.2)
|
24
|
+
coderay (~> 1.1)
|
25
|
+
method_source (~> 1.0)
|
26
|
+
rake (13.2.1)
|
27
|
+
rspec (3.13.0)
|
28
|
+
rspec-core (~> 3.13.0)
|
29
|
+
rspec-expectations (~> 3.13.0)
|
30
|
+
rspec-mocks (~> 3.13.0)
|
31
|
+
rspec-core (3.13.2)
|
32
|
+
rspec-support (~> 3.13.0)
|
33
|
+
rspec-expectations (3.13.3)
|
34
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
35
|
+
rspec-support (~> 3.13.0)
|
36
|
+
rspec-mocks (3.13.2)
|
37
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
38
|
+
rspec-support (~> 3.13.0)
|
39
|
+
rspec-support (3.13.1)
|
40
|
+
ruby-openai (7.3.1)
|
41
|
+
event_stream_parser (>= 0.3.0, < 2.0.0)
|
42
|
+
faraday (>= 1)
|
43
|
+
faraday-multipart (>= 1)
|
44
|
+
ruby2_keywords (0.0.5)
|
45
|
+
|
46
|
+
PLATFORMS
|
47
|
+
ruby
|
48
|
+
|
49
|
+
DEPENDENCIES
|
50
|
+
pry
|
51
|
+
rake (~> 13.0)
|
52
|
+
rspec (~> 3.0)
|
53
|
+
ruby-openai-swarm!
|
54
|
+
|
55
|
+
BUNDLED WITH
|
56
|
+
2.1.4
|
data/README.md
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
![Swarm Logo](assets/logo-swarm.png)
|
2
|
+
|
3
|
+
# Ruby OpenAI Swarm
|
4
|
+
|
5
|
+
[![rspec](https://github.com/graysonchen/ruby-openai-swarm/actions/workflows/rspec.yml/badge.svg)](https://github.com/graysonchen/ruby-openai-swarm/actions)
|
6
|
+
|
7
|
+
A Ruby-based educational framework adapted from OpenAI’s [Swarm](https://github.com/openai/swarm), exploring ergonomic, lightweight multi-agent orchestration.
|
8
|
+
|
9
|
+
> The primary goal of Swarm is to showcase the handoff & routines patterns explored in the [Orchestrating Agents: Handoffs & Routines](https://cookbook.openai.com/examples/orchestrating_agents) cookbook. It is not meant as a standalone library, and is primarily for educational purposes.
|
10
|
+
|
11
|
+
## Contents
|
12
|
+
- [Ruby OpenAI Swarm](#ruby-openai-swarm)
|
13
|
+
- [Table of Contents](#table-of-contents)
|
14
|
+
- [Installation](#installation)
|
15
|
+
- [Bundler](#bundler)
|
16
|
+
- [Gem install](#gem-install)
|
17
|
+
- [examples](#examples)
|
18
|
+
- [Documentation](#documentation)
|
19
|
+
|
20
|
+
## Installation
|
21
|
+
|
22
|
+
### Bundler
|
23
|
+
|
24
|
+
Add this line to your application's Gemfile:
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
gem "ruby-openai-swarm"
|
28
|
+
```
|
29
|
+
|
30
|
+
And then execute:
|
31
|
+
|
32
|
+
```bash
|
33
|
+
$ bundle install
|
34
|
+
```
|
35
|
+
|
36
|
+
### Gem install
|
37
|
+
|
38
|
+
Or install with:
|
39
|
+
|
40
|
+
```bash
|
41
|
+
$ gem install ruby-openai-swarm
|
42
|
+
```
|
43
|
+
|
44
|
+
and require with:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
require "ruby-openai-swarm"
|
48
|
+
```
|
49
|
+
|
50
|
+
## Usage
|
51
|
+
|
52
|
+
### With Config
|
53
|
+
|
54
|
+
For a more robust setup, you can configure the gem with your API keys, for example in an `openai.rb` initializer file. Never hardcode secrets into your codebase - instead use something like [dotenv](https://github.com/motdotla/dotenv) to pass the keys safely into your environments.
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
OpenAI.configure do |config|
|
58
|
+
config.access_token = ENV['OPENAI_ACCESS_TOKEN']
|
59
|
+
end
|
60
|
+
```
|
61
|
+
|
62
|
+
OR
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
# https://openrouter.ai
|
66
|
+
OpenAI.configure do |config|
|
67
|
+
config.access_token = ENV['OPEN_ROUTER_ACCESS_TOKEN']
|
68
|
+
config.uri_base = "https://openrouter.ai/api/v1"
|
69
|
+
end
|
70
|
+
```
|
71
|
+
|
72
|
+
more see: https://github.com/alexrudall/ruby-openai/tree/main?tab=readme-ov-file#ollama
|
73
|
+
|
74
|
+
Then you can create a client like this:
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
client = OpenAISwarm.new
|
78
|
+
|
79
|
+
def spanish_agent
|
80
|
+
OpenAISwarm::Agent.new(
|
81
|
+
name: "Spanish Agent",
|
82
|
+
instructions: "You only speak Spanish.",
|
83
|
+
model: "gpt-4o-mini"
|
84
|
+
)
|
85
|
+
end
|
86
|
+
|
87
|
+
transfer_to_spanish_agent = OpenAISwarm::FunctionDescriptor.new(
|
88
|
+
target_method: :spanish_agent,
|
89
|
+
description: 'Transfer spanish speaking users immediately.'
|
90
|
+
)
|
91
|
+
|
92
|
+
english_agent = OpenAISwarm::Agent.new(
|
93
|
+
name: "English Agent",
|
94
|
+
instructions: "You only speak English.",
|
95
|
+
model: "gpt-4o-mini",
|
96
|
+
functions: [transfer_to_spanish_agent]
|
97
|
+
)
|
98
|
+
|
99
|
+
messages = [{"role": "user", "content": "Hola. ¿Como estás?"}]
|
100
|
+
response = client.run(agent: english_agent, messages: messages, debug: true)
|
101
|
+
|
102
|
+
pp response.messages.last
|
103
|
+
```
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
{"role"=>"assistant",
|
107
|
+
"content"=>"¡Hola! Estoy bien, gracias. ¿Y tú?",
|
108
|
+
"refusal"=>nil,
|
109
|
+
:sender=>"Spanish Agent"}
|
110
|
+
```
|
111
|
+
|
112
|
+
# Examples
|
113
|
+
|
114
|
+
Check out `/examples` for inspiration! Learn more about each one in its README.
|
115
|
+
|
116
|
+
- [X] [`basic`](examples/basic): Simple examples of fundamentals like setup, function calling, handoffs, and context variables
|
117
|
+
- [X] [`triage_agent`](examples/triage_agent): Simple example of setting up a basic triage step to hand off to the right agent
|
118
|
+
- [X] [`weather_agent`](examples/weather_agent): Simple example of function calling
|
119
|
+
- [ ] [`airline`](examples/airline): A multi-agent setup for handling different customer service requests in an airline context.
|
120
|
+
- [ ] [`support_bot`](examples/support_bot): A customer service bot which includes a user interface agent and a help center agent with several tools
|
121
|
+
- [ ] [`personal_shopper`](examples/personal_shopper): A personal shopping agent that can help with making sales and refunding orders
|
122
|
+
|
123
|
+
link: https://github.com/openai/swarm/tree/main?tab=readme-ov-file#examples
|
124
|
+
|
125
|
+
## Documentation
|
126
|
+
![Swarm Diagram](https://raw.githubusercontent.com/openai/swarm/refs/heads/main/assets/swarm_diagram.png)
|
127
|
+
|
128
|
+
|
129
|
+
## Development
|
130
|
+
|
131
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
132
|
+
|
133
|
+
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 tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
134
|
+
|
135
|
+
## Contributing
|
136
|
+
|
137
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/graysonchen/ruby-openai-swarm. 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/graysonchen/ruby-openai-swarm/blob/main/CODE_OF_CONDUCT.md).
|
138
|
+
|
139
|
+
## License
|
140
|
+
|
141
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
Binary file
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "ruby-openai-swarm"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "ruby-openai-swarm"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
File without changes
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# link: https://github.com/openai/swarm/blob/main/examples/basic/agent_handoff.py
|
2
|
+
|
3
|
+
# OpenAI.configure do |config|
|
4
|
+
# config.access_token = ENV['OPENAI_ACCESS_TOKEN']
|
5
|
+
# end
|
6
|
+
|
7
|
+
OpenAI.configure do |config|
|
8
|
+
config.access_token = ENV['OPEN_ROUTER_ACCESS_TOKEN']
|
9
|
+
config.uri_base = "https://openrouter.ai/api/v1"
|
10
|
+
end
|
11
|
+
|
12
|
+
client = OpenAISwarm.new
|
13
|
+
|
14
|
+
def spanish_agent
|
15
|
+
OpenAISwarm::Agent.new(
|
16
|
+
name: "Spanish Agent",
|
17
|
+
instructions: "You only speak Spanish.",
|
18
|
+
model: "gpt-4o-mini"
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
transfer_to_spanish_agent = OpenAISwarm::FunctionDescriptor.new(
|
23
|
+
target_method: :spanish_agent,
|
24
|
+
description: 'Transfer spanish speaking users immediately.'
|
25
|
+
)
|
26
|
+
|
27
|
+
english_agent = OpenAISwarm::Agent.new(
|
28
|
+
name: "English Agent",
|
29
|
+
instructions: "You only speak English.",
|
30
|
+
model: "gpt-4o-mini",
|
31
|
+
functions: [transfer_to_spanish_agent]
|
32
|
+
)
|
33
|
+
|
34
|
+
messages = [{"role": "user", "content": "Hola. ¿Como estás?"}]
|
35
|
+
response = client.run(agent: english_agent, messages: messages, debug: true)
|
36
|
+
|
37
|
+
p response.messages.last
|
38
|
+
# => {"role"=>"assistant", "content"=>"¡Hola! Estoy bien, gracias. ¿Y tú?", "refusal"=>nil, :sender=>"Spanish Agent"}
|
39
|
+
|
40
|
+
msg = response.messages.last
|
41
|
+
msg['sender'] == "Spanish Agent"
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "bundler/setup"
|
2
|
+
require "ruby-openai-swarm"
|
3
|
+
|
4
|
+
# link: https://github.com/openai/swarm/blob/main/examples/basic/bare_minimum.py
|
5
|
+
|
6
|
+
# OpenAI.configure do |config|
|
7
|
+
# config.access_token = ENV['OPENAI_ACCESS_TOKEN']
|
8
|
+
# end
|
9
|
+
|
10
|
+
OpenAI.configure do |config|
|
11
|
+
config.access_token = ENV['OPEN_ROUTER_ACCESS_TOKEN']
|
12
|
+
config.uri_base = "https://openrouter.ai/api/v1"
|
13
|
+
end
|
14
|
+
|
15
|
+
client = OpenAISwarm.new
|
16
|
+
|
17
|
+
agent = OpenAISwarm::Agent.new(
|
18
|
+
name: "Agent",
|
19
|
+
instructions: "You are a helpful agent.",
|
20
|
+
model: "gpt-4o-mini"
|
21
|
+
)
|
22
|
+
messages = [{"role": "user", "content": "Hi!"}]
|
23
|
+
response = client.run(agent: agent, messages: messages)
|
24
|
+
p response.messages.last["content"]
|
25
|
+
# => "Hello! How can I assist you today?"
|
26
|
+
p response.messages
|
27
|
+
# => [{"role"=>"assistant", "content"=>"Hello! How can I assist you today?", "refusal"=>nil, :sender=>"Agent"}]
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# link: https://github.com/openai/swarm/blob/main/examples/basic/context_variables.py
|
2
|
+
|
3
|
+
# OpenAI.configure do |config|
|
4
|
+
# config.access_token = ENV['OPENAI_ACCESS_TOKEN']
|
5
|
+
# end
|
6
|
+
OpenAI.configure do |config|
|
7
|
+
config.access_token = ENV['OPEN_ROUTER_ACCESS_TOKEN']
|
8
|
+
config.uri_base = "https://openrouter.ai/api/v1"
|
9
|
+
end
|
10
|
+
|
11
|
+
client = OpenAISwarm.new
|
12
|
+
|
13
|
+
def instructions(context_variables)
|
14
|
+
name = context_variables.fetch(:name, :User)
|
15
|
+
"You are a helpful agent. Greet the user by name (#{name})."
|
16
|
+
end
|
17
|
+
|
18
|
+
def print_account_details(context_variables: {})
|
19
|
+
puts "print_account_details context_variables: #{context_variables.inspect}"
|
20
|
+
|
21
|
+
user_id = context_variables[:user_id]
|
22
|
+
name = context_variables[:name]
|
23
|
+
puts "Account Details: name: #{name}, user_id: #{user_id}"
|
24
|
+
"Success"
|
25
|
+
end
|
26
|
+
|
27
|
+
function_instance = OpenAISwarm::FunctionDescriptor.new(
|
28
|
+
target_method: :print_account_details,
|
29
|
+
)
|
30
|
+
|
31
|
+
agent = OpenAISwarm::Agent.new(
|
32
|
+
name: "Agent",
|
33
|
+
instructions: method(:instructions),
|
34
|
+
model: "gpt-4o-mini",
|
35
|
+
functions: [function_instance]
|
36
|
+
)
|
37
|
+
|
38
|
+
context_variables = { 'name': 'James', 'user_id': 123 }
|
39
|
+
|
40
|
+
# debugger logger: {:model=>"gpt-4o-mini", :messages=>[{:role=>"system", :content=>"You are a helpful agent. Greet the user by name (James)."}, {:role=>"user", :content=>"Hi!"}], :tools=>[{:type=>"function", :function=>{:name=>"print_account_details", :description=>"", :parameters=>{:type=>"object", :properties=>{}, :required=>[]}}}], :stream=>false, :parallel_tool_calls=>true}
|
41
|
+
response = client.run(
|
42
|
+
messages: [{"role": "user", "content": "Hi!"}],
|
43
|
+
agent: agent,
|
44
|
+
context_variables: context_variables,
|
45
|
+
debug: true,
|
46
|
+
)
|
47
|
+
msg = response.messages.last
|
48
|
+
# Hello, James! How can I assist you today?
|
49
|
+
|
50
|
+
msg['content'].include?('James')
|
51
|
+
|
52
|
+
# debugger logger: {:model=>"gpt-4o-mini", :messages=>[{:role=>"system", :content=>"You are a helpful agent. Greet the user by name (James)."}, {:role=>"user", :content=>"Print my account details!"}], :tools=>[{:type=>"function", :function=>{:name=>"print_account_details", :description=>"", :parameters=>{:type=>"object", :properties=>{}, :required=>[]}}}], :stream=>false, :parallel_tool_calls=>true
|
53
|
+
response = client.run(
|
54
|
+
messages: [{"role": "user", "content": "Print my account details!"}],
|
55
|
+
agent: agent,
|
56
|
+
context_variables: context_variables,
|
57
|
+
debug: true,
|
58
|
+
)
|
59
|
+
msg = response.messages.last
|
60
|
+
msg['content']
|
61
|
+
response.context_variables == {:name=>"James", :user_id=>123}
|
62
|
+
|
63
|
+
# print(response.messages[-1]["content"])
|
64
|
+
# Hello, James! Your account details have been printed successfully. If you need anything else, just let me know!
|
65
|
+
#
|
66
|
+
# print_account_details context_variables: {:name=>"James", :user_id=>123}
|
67
|
+
# Account Details: name: James, user_id: 123
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
OpenAI.configure do |config|
|
3
|
+
config.access_token = ENV['OPEN_ROUTER_ACCESS_TOKEN']
|
4
|
+
config.uri_base = "https://openrouter.ai/api/v1"
|
5
|
+
end
|
6
|
+
|
7
|
+
client = OpenAISwarm.new
|
8
|
+
|
9
|
+
def get_weather(location:)
|
10
|
+
"{'temp':67, 'unit':'F'}"
|
11
|
+
end
|
12
|
+
|
13
|
+
function_instance = OpenAISwarm::FunctionDescriptor.new(
|
14
|
+
target_method: :get_weather,
|
15
|
+
description: 'Simulate fetching weather data'
|
16
|
+
)
|
17
|
+
|
18
|
+
agent = OpenAISwarm::Agent.new(
|
19
|
+
name: "Agent",
|
20
|
+
instructions: "You are a helpful agent.",
|
21
|
+
model: "gpt-4o-mini",
|
22
|
+
functions: [function_instance]
|
23
|
+
)
|
24
|
+
# debugger logger: {:model=>"gpt-4o-mini", :messages=>[{:role=>"system", :content=>"You are a helpful agent."}, {"role"=>"user", "content"=>"What's the weather in NYC?"}], :tools=>[{:type=>"function", :function=>{:name=>"get_weather", :description=>"", :parameters=>{:type=>"object", :properties=>{:location=>{:type=>"string"}}, :required=>["location"]}}}], :stream=>false, :parallel_tool_calls=>true}
|
25
|
+
response = client.run(
|
26
|
+
messages: [{"role" => "user", "content" => "What's the weather in NYC?"}],
|
27
|
+
agent: agent,
|
28
|
+
debug: true,
|
29
|
+
)
|
30
|
+
|
31
|
+
# print(response.messages[-1]["content"])
|
32
|
+
# The current temperature in New York City is 67°F. => nil
|
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
OpenAI.configure do |config|
|
3
|
+
config.access_token = ENV['OPEN_ROUTER_ACCESS_TOKEN']
|
4
|
+
config.uri_base = "https://openrouter.ai/api/v1"
|
5
|
+
end
|
6
|
+
|
7
|
+
client = OpenAISwarm.new
|
8
|
+
|
9
|
+
agent = OpenAISwarm::Agent.new(
|
10
|
+
name: "Agent",
|
11
|
+
instructions: "You are a helpful agent.",
|
12
|
+
model: "gpt-4o-mini",
|
13
|
+
)
|
14
|
+
|
15
|
+
def pretty_print_messages(messages)
|
16
|
+
messages.each do |message|
|
17
|
+
next if message["content"].nil?
|
18
|
+
puts "#{message["sender"]}: #{message["content"]}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
messages = []
|
23
|
+
agent = my_agent
|
24
|
+
loop do
|
25
|
+
print "> "
|
26
|
+
user_input = gets.chomp
|
27
|
+
|
28
|
+
break if user_input.downcase == "exit"
|
29
|
+
|
30
|
+
messages << { "role": "user", "content": user_input }
|
31
|
+
response = client.run(agent: agent, messages: messages)
|
32
|
+
|
33
|
+
messages.concat(response.messages)
|
34
|
+
agent = response.agent
|
35
|
+
|
36
|
+
pretty_print_messages(response.messages)
|
37
|
+
end
|
38
|
+
puts "Goodbye!"
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Triage agent
|
2
|
+
|
3
|
+
This example is a Swarm containing a triage agent, which takes in user inputs and chooses whether to respond directly, or triage the request
|
4
|
+
to a sales or refunds agent.
|
5
|
+
|
6
|
+
## Setup
|
7
|
+
|
8
|
+
To run the triage agent Swarm:
|
9
|
+
|
10
|
+
1. Run
|
11
|
+
|
12
|
+
```shell
|
13
|
+
ruby main.rb
|
14
|
+
```
|
15
|
+
or
|
16
|
+
|
17
|
+
```shell
|
18
|
+
ruby examples/triage_agent/main.rb
|
19
|
+
```
|
20
|
+
|
21
|
+
## Evals (TODO)
|
22
|
+
|
23
|
+
> [!NOTE]
|
24
|
+
> These evals are intended to be examples to demonstrate functionality, but will have to be updated and catered to your particular use case.
|
25
|
+
|
26
|
+
This example uses `Pytest` to run eval unit tests. We have two tests in the `evals.py` file, one which
|
27
|
+
tests if we call the correct triage function when expected, and one which assesses if a conversation
|
28
|
+
is 'successful', as defined in our prompt in `evals.py`.
|
29
|
+
|
30
|
+
To run the evals, run
|
31
|
+
|
32
|
+
```shell
|
33
|
+
ruby evals.rb
|
34
|
+
```
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require "bundler/setup"
|
2
|
+
require "ruby-openai-swarm"
|
3
|
+
|
4
|
+
OpenAI.configure do |config|
|
5
|
+
config.access_token = ENV['OPEN_ROUTER_ACCESS_TOKEN']
|
6
|
+
config.uri_base = "https://openrouter.ai/api/v1"
|
7
|
+
end
|
8
|
+
|
9
|
+
def client
|
10
|
+
OpenAISwarm.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def process_refund(item_id, reason = "NOT SPECIFIED")
|
14
|
+
# Refund an item. Make sure you have the item_id of the form item_...
|
15
|
+
# Ask for user confirmation before processing the refund.
|
16
|
+
puts "[mock] Refunding item #{item_id} because #{reason}..."
|
17
|
+
"Success!"
|
18
|
+
end
|
19
|
+
|
20
|
+
def process_refund_function_instance
|
21
|
+
OpenAISwarm::FunctionDescriptor.new(
|
22
|
+
target_method: :process_refund,
|
23
|
+
description: "Refund an item. Make sure you have the item_id of the form item_...Ask for user confirmation before processing the refund."
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
def apply_discount
|
28
|
+
# Apply a discount to the user's cart.
|
29
|
+
puts "[mock] Applying discount..."
|
30
|
+
"Applied discount of 11%"
|
31
|
+
end
|
32
|
+
|
33
|
+
def apply_discount_function_instance
|
34
|
+
OpenAISwarm::FunctionDescriptor.new(
|
35
|
+
target_method: :apply_discount,
|
36
|
+
description: "Apply a discount to the user's cart."
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
def triage_agent
|
41
|
+
@triage_agent ||=
|
42
|
+
OpenAISwarm::Agent.new(
|
43
|
+
name: "Triage Agent",
|
44
|
+
instructions: "Determine which agent is best suited to handle the user's request, and transfer the conversation to that agent."
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
def sales_agent
|
49
|
+
@sales_agent ||=
|
50
|
+
OpenAISwarm::Agent.new(
|
51
|
+
name: "Sales Agent",
|
52
|
+
instructions: "Be super enthusiastic about selling bees."
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
def refunds_agent
|
57
|
+
@refunds_agent ||=
|
58
|
+
OpenAISwarm::Agent.new(
|
59
|
+
name: "Refunds Agent",
|
60
|
+
instructions: "Help the user with a refund. If the reason is that it was too expensive, offer the user a refund code. If they insist, then process the refund.",
|
61
|
+
functions: [
|
62
|
+
process_refund_function_instance,
|
63
|
+
apply_discount_function_instance
|
64
|
+
]
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
transfer_back_to_triage = OpenAISwarm::FunctionDescriptor.new(
|
69
|
+
target_method: :triage_agent,
|
70
|
+
description: "Call this function if a user is asking about a topic that is not handled by the current agent."
|
71
|
+
)
|
72
|
+
|
73
|
+
def transfer_to_sales
|
74
|
+
sales_agent
|
75
|
+
end
|
76
|
+
|
77
|
+
def transfer_to_refunds
|
78
|
+
refunds_agent
|
79
|
+
end
|
80
|
+
|
81
|
+
# Assign functions to agents
|
82
|
+
triage_agent.functions = [method(:transfer_to_sales), method(:transfer_to_refunds)]
|
83
|
+
sales_agent.functions << transfer_back_to_triage
|
84
|
+
refunds_agent.functions << transfer_back_to_triage
|
File without changes
|