ruby-openai-swarm 0.2.6 → 0.2.8
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/Gemfile.lock +1 -1
- data/README.md +13 -0
- data/examples/airline/main.rb +1 -1
- data/examples/basic/function_calling.rb +1 -17
- data/lib/ruby-openai-swarm/configuration.rb +24 -0
- data/lib/ruby-openai-swarm/core.rb +40 -6
- data/lib/ruby-openai-swarm/logger.rb +58 -0
- data/lib/ruby-openai-swarm/version.rb +1 -1
- data/lib/ruby-openai-swarm.rb +2 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bb75077c1cd68a61448103423fec5401c54963c3341b9c7a11aef564584375d7
|
4
|
+
data.tar.gz: 6950967689f250591671095dd296a0e266d3e218b41e618811a3a0c8c8c48ee2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0caeb42cc055869a3b9bd830e3d52cb63add1a07af767cd1e42abe8b5fb7d98565327af96a1bd36ff72616d775f3be72066eeb9298da37762afe1461ff0cd345
|
7
|
+
data.tar.gz: 0d515f09b04a1229e08ee1957e5958805a512f0fd9a7bb79ddf43c0962d868b9415f27714802468be1450d6d55130df7295f4ee3ed791c6c0230e09d2fa149ac
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -15,6 +15,7 @@ A Ruby-based educational framework adapted from OpenAI’s [Swarm](https://githu
|
|
15
15
|
- [Installation](#installation)
|
16
16
|
- [Bundler](#bundler)
|
17
17
|
- [Gem install](#gem-install)
|
18
|
+
- [Logger](#logger)
|
18
19
|
- [examples](#examples)
|
19
20
|
- [Documentation](#documentation)
|
20
21
|
|
@@ -114,6 +115,18 @@ pp response.messages.last
|
|
114
115
|
:sender=>"Spanish Agent"}
|
115
116
|
```
|
116
117
|
|
118
|
+
### Logger
|
119
|
+
|
120
|
+
```
|
121
|
+
OpenAISwarm.configure do |config|
|
122
|
+
# config.logger = Logger.new(STDOUT)
|
123
|
+
# config.logger = Rails.logger
|
124
|
+
config.log_file = Rails.root.join('log', 'openai_swarm.log')
|
125
|
+
# config.logger = Logger.new(Rails.root.join('log', 'openai_swarm.log'))
|
126
|
+
# config.logger = Rails.configuration.lograge.logger
|
127
|
+
end
|
128
|
+
```
|
129
|
+
|
117
130
|
# Examples
|
118
131
|
|
119
132
|
Setting ACCESS_TOKEN for AI Providers in examples
|
data/examples/airline/main.rb
CHANGED
@@ -65,4 +65,4 @@ params:
|
|
65
65
|
GUIDE_EXAMPLES
|
66
66
|
puts guide_examples
|
67
67
|
|
68
|
-
OpenAISwarm::Repl.run_demo_loop(triage_agent, context_variables: context_variables,
|
68
|
+
OpenAISwarm::Repl.run_demo_loop(triage_agent, context_variables: context_variables, debug: env_debug)
|
@@ -46,7 +46,6 @@ Details:
|
|
46
46
|
Example: “What’s the weather in NYC?”
|
47
47
|
Action: Calls get_weather with location “New York City”.
|
48
48
|
Response: Only provides weather details.
|
49
|
-
|
50
49
|
2. Multiple Function Calls
|
51
50
|
Example: “Tell me the weather in New York and the latest news headlines.”
|
52
51
|
Action: Calls get_weather for weather and get_news for news.
|
@@ -59,19 +58,4 @@ GUIDE_EXAMPLES
|
|
59
58
|
|
60
59
|
puts guide_examples
|
61
60
|
|
62
|
-
|
63
|
-
|
64
|
-
OpenAISwarm.new.run_and_stream(
|
65
|
-
agent: agent,
|
66
|
-
debug: true,
|
67
|
-
messages: [{"role" => "user", "content" => "Tell me the weather in New York and the latest news headlines."}]
|
68
|
-
) do |chunk|
|
69
|
-
if chunk.key?("content") && !chunk["content"].nil?
|
70
|
-
puts ">>>#{chunk}"
|
71
|
-
end
|
72
|
-
|
73
|
-
if chunk.key?('response')
|
74
|
-
binding.pry
|
75
|
-
# log_llm_request(chunk['response'])
|
76
|
-
end
|
77
|
-
end
|
61
|
+
OpenAISwarm::Repl.run_demo_loop(agent, stream: true, debug: env_debug)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module OpenAISwarm
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :logger, :log_file
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@log_file = nil
|
7
|
+
@logger = nil
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def configuration
|
13
|
+
@configuration ||= Configuration.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def configure
|
17
|
+
yield(configuration) if block_given?
|
18
|
+
end
|
19
|
+
|
20
|
+
def reset_configuration!
|
21
|
+
@configuration = Configuration.new
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -11,13 +11,15 @@ module OpenAISwarm
|
|
11
11
|
|
12
12
|
def initialize(client = nil)
|
13
13
|
@client = client || OpenAI::Client.new
|
14
|
+
@logger = OpenAISwarm::Logger.instance.logger
|
14
15
|
end
|
15
16
|
|
16
17
|
def get_chat_completion(agent, history, context_variables, model_override, stream, debug)
|
17
18
|
context_variables = context_variables.dup
|
18
19
|
instructions = agent.instructions.respond_to?(:call) ? agent.instructions.call(context_variables) : agent.instructions
|
19
20
|
messages = [{ role: 'system', content: instructions }] + history
|
20
|
-
|
21
|
+
|
22
|
+
# Util.debug_print(debug, "Getting chat completion for...:", messages)
|
21
23
|
|
22
24
|
tools = agent.functions.map { |f| Util.function_to_json(f) }
|
23
25
|
# hide context_variables from model
|
@@ -40,7 +42,9 @@ module OpenAISwarm
|
|
40
42
|
create_params[:tool_choice] = agent.tool_choice if agent.tool_choice
|
41
43
|
create_params[:parallel_tool_calls] = agent.parallel_tool_calls if tools.any?
|
42
44
|
|
43
|
-
Util.debug_print(debug, "
|
45
|
+
Util.debug_print(debug, "Getting chat completion for...:", create_params)
|
46
|
+
log_message(:info, "Getting chat completion for...:", create_params)
|
47
|
+
|
44
48
|
if stream
|
45
49
|
return Enumerator.new do |yielder|
|
46
50
|
@client.chat(parameters: create_params.merge(
|
@@ -56,6 +60,7 @@ module OpenAISwarm
|
|
56
60
|
Util.debug_print(debug, "API Response:", response)
|
57
61
|
response
|
58
62
|
rescue OpenAI::Error => e
|
63
|
+
log_message(:error, "OpenAI API Error: #{e.message}")
|
59
64
|
Util.debug_print(true, "OpenAI API Error:", e.message)
|
60
65
|
raise
|
61
66
|
end
|
@@ -101,6 +106,7 @@ module OpenAISwarm
|
|
101
106
|
name = tool_call.dig('function', 'name')
|
102
107
|
unless function_map.key?(name)
|
103
108
|
Util.debug_print(debug, "Tool #{name} not found in function map.")
|
109
|
+
log_message(:error, "Tool #{name} not found in function map.")
|
104
110
|
partial_response.messages << {
|
105
111
|
'role' => 'tool',
|
106
112
|
'tool_call_id' => tool_call['id'],
|
@@ -112,6 +118,7 @@ module OpenAISwarm
|
|
112
118
|
|
113
119
|
args = JSON.parse(tool_call.dig('function', 'arguments') || '{}')
|
114
120
|
Util.debug_print(debug, "Processing tool call: #{name} with arguments #{args}")
|
121
|
+
log_message(:info, "Processing tool call: #{name} with arguments #{args}")
|
115
122
|
|
116
123
|
func = function_map[name]
|
117
124
|
# pass context_variables to agent functions
|
@@ -166,11 +173,15 @@ module OpenAISwarm
|
|
166
173
|
|
167
174
|
message = completion.dig('choices', 0, 'message')
|
168
175
|
Util.debug_print(debug, "Received completion:", message)
|
176
|
+
log_message(:info, "Received completion:", message)
|
169
177
|
|
170
178
|
message['sender'] = active_agent.name
|
171
179
|
history << message
|
172
180
|
|
173
|
-
|
181
|
+
if !message['tool_calls'] || !execute_tools
|
182
|
+
log_message(:info, "Ending turn.")
|
183
|
+
break
|
184
|
+
end
|
174
185
|
|
175
186
|
partial_response = handle_tool_calls(
|
176
187
|
message['tool_calls'],
|
@@ -191,8 +202,6 @@ module OpenAISwarm
|
|
191
202
|
)
|
192
203
|
end
|
193
204
|
|
194
|
-
# private
|
195
|
-
|
196
205
|
def run_and_stream(agent:, messages:, context_variables: {}, model_override: nil, debug: false, max_turns: Float::INFINITY, execute_tools: true)
|
197
206
|
active_agent = agent
|
198
207
|
context_variables = context_variables.dup
|
@@ -239,9 +248,15 @@ module OpenAISwarm
|
|
239
248
|
message['tool_calls'] = message['tool_calls'].values
|
240
249
|
message['tool_calls'] = nil if message['tool_calls'].empty?
|
241
250
|
Util.debug_print(debug, "Received completion:", message)
|
251
|
+
log_message(:info, "Received completion:", message)
|
252
|
+
|
242
253
|
history << message
|
243
254
|
|
244
|
-
|
255
|
+
|
256
|
+
if !message['tool_calls'] || !execute_tools
|
257
|
+
log_message(:info, "Ending turn.")
|
258
|
+
break
|
259
|
+
end
|
245
260
|
|
246
261
|
# convert tool_calls to objects
|
247
262
|
tool_calls = message['tool_calls'].map do |tool_call|
|
@@ -265,6 +280,14 @@ module OpenAISwarm
|
|
265
280
|
history.concat(partial_response.messages)
|
266
281
|
context_variables.merge!(partial_response.context_variables)
|
267
282
|
active_agent = partial_response.agent if partial_response.agent
|
283
|
+
|
284
|
+
tool_call_messages = (Array.wrap(message) + partial_response.messages)
|
285
|
+
yield(
|
286
|
+
'tool_call_messages' => Response.new(
|
287
|
+
messages: tool_call_messages,
|
288
|
+
agent: active_agent,
|
289
|
+
context_variables: context_variables)
|
290
|
+
) if block_given?
|
268
291
|
end
|
269
292
|
|
270
293
|
yield(
|
@@ -273,5 +296,16 @@ module OpenAISwarm
|
|
273
296
|
context_variables: context_variables)
|
274
297
|
) if block_given?
|
275
298
|
end
|
299
|
+
|
300
|
+
private
|
301
|
+
|
302
|
+
def log_message(level, message, data = nil)
|
303
|
+
return unless @logger
|
304
|
+
|
305
|
+
log_text = message
|
306
|
+
log_text += "\n#{data.inspect}" if data
|
307
|
+
|
308
|
+
@logger.send(level, log_text)
|
309
|
+
end
|
276
310
|
end
|
277
311
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module OpenAISwarm
|
5
|
+
class Logger
|
6
|
+
SEVERITY_COLORS = {
|
7
|
+
'DEBUG' => "\e[36m", # Cyan
|
8
|
+
'INFO' => "\e[32m", # Green
|
9
|
+
'WARN' => "\e[33m", # Yellow
|
10
|
+
'ERROR' => "\e[31m", # Red
|
11
|
+
'FATAL' => "\e[35m", # Purple
|
12
|
+
'ANY' => "\e[0m" # Reset color
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
def self.instance
|
16
|
+
@instance ||= new
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
@loggers = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
def logger(log_path = nil)
|
24
|
+
return OpenAISwarm.configuration.logger if OpenAISwarm.configuration.logger
|
25
|
+
return @loggers[log_path] if @loggers[log_path]
|
26
|
+
|
27
|
+
path = determine_log_path(log_path)
|
28
|
+
ensure_log_directory(path)
|
29
|
+
|
30
|
+
logger = ::Logger.new(path)
|
31
|
+
logger.formatter = proc do |severity, datetime, progname, msg|
|
32
|
+
color = SEVERITY_COLORS[severity] || SEVERITY_COLORS['ANY']
|
33
|
+
reset = SEVERITY_COLORS['ANY']
|
34
|
+
"[#{datetime}] #{color}#{severity}#{reset} OpenAISwarm: #{msg}\n"
|
35
|
+
end
|
36
|
+
|
37
|
+
@loggers[log_path] = logger
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def determine_log_path(log_path)
|
43
|
+
return log_path if log_path
|
44
|
+
return OpenAISwarm.configuration.log_file if OpenAISwarm.configuration.log_file
|
45
|
+
|
46
|
+
if defined?(Rails)
|
47
|
+
Rails.root.join('log', "#{Rails.env}.log").to_s
|
48
|
+
else
|
49
|
+
'log/openai_swarm.log'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def ensure_log_directory(path)
|
54
|
+
dir = File.dirname(path)
|
55
|
+
FileUtils.mkdir_p(dir) unless File.directory?(dir)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/ruby-openai-swarm.rb
CHANGED
@@ -6,6 +6,8 @@ require 'ruby-openai-swarm/util'
|
|
6
6
|
require 'ruby-openai-swarm/core'
|
7
7
|
require 'ruby-openai-swarm/function_descriptor'
|
8
8
|
require 'ruby-openai-swarm/repl'
|
9
|
+
require 'ruby-openai-swarm/configuration'
|
10
|
+
require 'ruby-openai-swarm/logger'
|
9
11
|
|
10
12
|
module OpenAISwarm
|
11
13
|
class Error < StandardError;
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-openai-swarm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Grayson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-12-
|
11
|
+
date: 2024-12-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby-openai
|
@@ -106,8 +106,10 @@ files:
|
|
106
106
|
- examples/weather_agent/run.rb
|
107
107
|
- lib/ruby-openai-swarm.rb
|
108
108
|
- lib/ruby-openai-swarm/agent.rb
|
109
|
+
- lib/ruby-openai-swarm/configuration.rb
|
109
110
|
- lib/ruby-openai-swarm/core.rb
|
110
111
|
- lib/ruby-openai-swarm/function_descriptor.rb
|
112
|
+
- lib/ruby-openai-swarm/logger.rb
|
111
113
|
- lib/ruby-openai-swarm/repl.rb
|
112
114
|
- lib/ruby-openai-swarm/response.rb
|
113
115
|
- lib/ruby-openai-swarm/result.rb
|