ruby-openai-swarm 0.3.0.1 → 0.4.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/Gemfile.lock +1 -1
- data/examples/airline/main.rb +1 -1
- data/examples/basic/function_calling.rb +1 -17
- data/lib/ruby-openai-swarm/agent.rb +4 -1
- data/lib/ruby-openai-swarm/agents/change_tracker.rb +9 -1
- data/lib/ruby-openai-swarm/agents/strategy_options.rb +8 -1
- data/lib/ruby-openai-swarm/core.rb +25 -5
- data/lib/ruby-openai-swarm/repl.rb +16 -13
- data/lib/ruby-openai-swarm/util.rb +10 -0
- data/lib/ruby-openai-swarm/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 49bcf71e27c830e373b9732691667aa4ff8657dc944a8dc113fb4b13e807ccd6
|
4
|
+
data.tar.gz: 38cd26d3670e7a49652b8b52677a2a7bf722c9f5635bfef55bea296d0502c4d1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0f4b422f084d400ef6eea393f20a650fd02fa8b96370e4781c4228113650763c7eb84c6c1aac21daceb8251cfd83d69ec69b536dff16c3bd7be3e8b0f22a353a
|
7
|
+
data.tar.gz: f24c83ea576a7d305069eda9da308f0d39b257065897377b228c785049c41254d3889d4e82b96d40755151b801b95d3a6f8ae5550d73fb871c902c9b21755793
|
data/Gemfile.lock
CHANGED
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)
|
@@ -6,6 +6,7 @@ module OpenAISwarm
|
|
6
6
|
:strategy,
|
7
7
|
:noisy_tool_calls,
|
8
8
|
:temperature,
|
9
|
+
:current_tool_name,
|
9
10
|
:resource
|
10
11
|
# These attributes can be read and written externally. They include:
|
11
12
|
# - name: The name of the agent.
|
@@ -25,7 +26,8 @@ module OpenAISwarm
|
|
25
26
|
parallel_tool_calls: true,
|
26
27
|
resource: nil,
|
27
28
|
noisy_tool_calls: [],
|
28
|
-
strategy: {}
|
29
|
+
strategy: {},
|
30
|
+
current_tool_name: nil
|
29
31
|
)
|
30
32
|
@name = name
|
31
33
|
@model = model
|
@@ -37,6 +39,7 @@ module OpenAISwarm
|
|
37
39
|
@resource = resource
|
38
40
|
@noisy_tool_calls = noisy_tool_calls
|
39
41
|
@strategy = Agents::StrategyOptions.new(strategy)
|
42
|
+
@current_tool_name = current_tool_name.nil? ? nil : current_tool_name.to_s
|
40
43
|
end
|
41
44
|
end
|
42
45
|
end
|
@@ -1,9 +1,11 @@
|
|
1
1
|
module OpenAISwarm
|
2
2
|
module Agents
|
3
3
|
class ChangeTracker
|
4
|
-
attr_reader :current_agent, :previous_agent
|
4
|
+
attr_reader :current_agent, :previous_agent, :tracking_agents_tool_name
|
5
5
|
|
6
6
|
def initialize(agent)
|
7
|
+
@tracking_agents_tool_name = []
|
8
|
+
add_tracking_agents_tool_name(agent.current_tool_name)
|
7
9
|
update(agent)
|
8
10
|
end
|
9
11
|
|
@@ -12,6 +14,12 @@ module OpenAISwarm
|
|
12
14
|
@current_agent = agent
|
13
15
|
end
|
14
16
|
|
17
|
+
def add_tracking_agents_tool_name(tool_name)
|
18
|
+
return if tool_name.nil?
|
19
|
+
|
20
|
+
@tracking_agents_tool_name << tool_name
|
21
|
+
end
|
22
|
+
|
15
23
|
def agent_changed?
|
16
24
|
previous_agent&.name != current_agent&.name
|
17
25
|
end
|
@@ -1,10 +1,17 @@
|
|
1
1
|
module OpenAISwarm
|
2
2
|
module Agents
|
3
3
|
class StrategyOptions
|
4
|
-
attr_accessor :switch_agent_reset_message
|
4
|
+
attr_accessor :switch_agent_reset_message,
|
5
|
+
:prevent_agent_reentry
|
5
6
|
|
6
7
|
def initialize(strategy = {})
|
7
8
|
@switch_agent_reset_message = strategy[:switch_agent_reset_message] || false
|
9
|
+
# INFO:
|
10
|
+
# 1. When `prevent_agent_reentry` is false, LLM is used to control the agent's jump.
|
11
|
+
# - In this case, there is a possibility of an infinite loop, so additional mechanisms (e.g., jump count limit) are needed to avoid it.
|
12
|
+
# 2. When `prevent_agent_reentry` is true, it prevents the agent from being called again if it has already been called.
|
13
|
+
# - In this case, if an agent has already been called, it will not be called again.
|
14
|
+
@prevent_agent_reentry = strategy[:prevent_agent_reentry] || false
|
8
15
|
end
|
9
16
|
end
|
10
17
|
end
|
@@ -30,12 +30,12 @@ module OpenAISwarm
|
|
30
30
|
params[:required]&.delete(CTX_VARS_NAME.to_sym)
|
31
31
|
end
|
32
32
|
|
33
|
-
cleaned_messages =
|
33
|
+
cleaned_messages = Util.clean_message_tools(messages, agent.noisy_tool_calls)
|
34
34
|
|
35
35
|
create_params = {
|
36
36
|
model: model_override || agent.model,
|
37
37
|
messages: cleaned_messages,
|
38
|
-
tools: tools.
|
38
|
+
tools: Util.request_tools_excluded(tools, agent_tracker.tracking_agents_tool_name, agent.strategy.prevent_agent_reentry),
|
39
39
|
}
|
40
40
|
|
41
41
|
# TODO: https://platform.openai.com/docs/guides/function-calling/how-do-functions-differ-from-tools
|
@@ -51,9 +51,11 @@ module OpenAISwarm
|
|
51
51
|
|
52
52
|
if stream
|
53
53
|
return Enumerator.new do |yielder|
|
54
|
+
yielder << { 'parameters' => create_params }
|
55
|
+
|
54
56
|
@client.chat(parameters: create_params.merge(
|
55
57
|
stream: proc do |chunk, _bytesize|
|
56
|
-
yielder << chunk
|
58
|
+
yielder << { chunk: chunk }
|
57
59
|
end
|
58
60
|
))
|
59
61
|
end
|
@@ -198,6 +200,11 @@ module OpenAISwarm
|
|
198
200
|
debug
|
199
201
|
)
|
200
202
|
|
203
|
+
if partial_response.agent
|
204
|
+
agent_tool_name = message['tool_calls'].dig(0, 'function', 'name')
|
205
|
+
agent_tracker.add_tracking_agents_tool_name(agent_tool_name)
|
206
|
+
end
|
207
|
+
|
201
208
|
history.concat(partial_response.messages)
|
202
209
|
context_variables.merge!(partial_response.context_variables)
|
203
210
|
active_agent = partial_response.agent if partial_response.agent
|
@@ -233,7 +240,15 @@ module OpenAISwarm
|
|
233
240
|
)
|
234
241
|
|
235
242
|
yield({ delim: "start" }) if block_given?
|
236
|
-
completion.each do |
|
243
|
+
completion.each do |stream|
|
244
|
+
|
245
|
+
# TODO(Grayson): will refactor it
|
246
|
+
if stream['parameters']
|
247
|
+
yield({ 'parameters' => stream['parameters'] }) if block_given?
|
248
|
+
end
|
249
|
+
next if stream.key?('parameters')
|
250
|
+
|
251
|
+
chunk = stream[:chunk]
|
237
252
|
if chunk['error']
|
238
253
|
details = {
|
239
254
|
'response' =>
|
@@ -250,7 +265,7 @@ module OpenAISwarm
|
|
250
265
|
delta['sender'] = active_agent.name
|
251
266
|
end
|
252
267
|
|
253
|
-
yield delta if block_given?
|
268
|
+
yield({ 'delta' => delta }) if block_given?
|
254
269
|
|
255
270
|
delta.delete('role')
|
256
271
|
delta.delete('sender')
|
@@ -290,6 +305,11 @@ module OpenAISwarm
|
|
290
305
|
debug
|
291
306
|
)
|
292
307
|
|
308
|
+
if partial_response.agent
|
309
|
+
agent_tool_name = message['tool_calls'].dig(0, 'function', 'name')
|
310
|
+
agent_tracker.add_tracking_agents_tool_name(agent_tool_name)
|
311
|
+
end
|
312
|
+
|
293
313
|
history.concat(partial_response.messages)
|
294
314
|
context_variables.merge!(partial_response.context_variables)
|
295
315
|
active_agent = partial_response.agent if partial_response.agent
|
@@ -4,21 +4,24 @@ module OpenAISwarm
|
|
4
4
|
def process_and_print_streaming_response(response)
|
5
5
|
content = []
|
6
6
|
last_sender = ""
|
7
|
-
response.each do |
|
8
|
-
|
7
|
+
response.each do |stream|
|
8
|
+
delta = stream['delta']
|
9
|
+
if delta
|
10
|
+
last_sender = delta['sender'] if delta.key?('sender')
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
if delta.key?("content") && !delta["content"].nil?
|
13
|
+
if content.empty? && !last_sender.empty?
|
14
|
+
puts
|
15
|
+
print "\033[94m#{last_sender}:\033[0m "
|
16
|
+
last_sender = ""
|
17
|
+
end
|
18
|
+
print delta["content"]
|
19
|
+
content << delta["content"]
|
15
20
|
end
|
16
|
-
print chunk["content"]
|
17
|
-
content << chunk["content"]
|
18
21
|
end
|
19
22
|
|
20
|
-
if
|
21
|
-
|
23
|
+
if stream.key?("tool_calls") && !stream["tool_calls"].nil?
|
24
|
+
stream["tool_calls"].each do |tool_call|
|
22
25
|
f = tool_call["function"]
|
23
26
|
name = f["name"]
|
24
27
|
next if name.nil?
|
@@ -26,11 +29,11 @@ module OpenAISwarm
|
|
26
29
|
end
|
27
30
|
end
|
28
31
|
|
29
|
-
if
|
32
|
+
if stream.key?("delim") && stream["delim"] == "end" && !content.empty?
|
30
33
|
puts
|
31
34
|
content = ""
|
32
35
|
end
|
33
|
-
return
|
36
|
+
return stream["response"] if stream.key?("response")
|
34
37
|
end
|
35
38
|
end
|
36
39
|
|
@@ -7,7 +7,17 @@ module OpenAISwarm
|
|
7
7
|
last_user_message = filtered_messages.reverse.find { |msg| msg['role'] == 'user' }
|
8
8
|
last_user_message ? [last_user_message] : history
|
9
9
|
end
|
10
|
+
|
11
|
+
def request_tools_excluded(tools, tool_names, prevent_agent_reentry = false)
|
12
|
+
return nil if tools.empty?
|
13
|
+
return tools if tool_names.empty? || !prevent_agent_reentry
|
14
|
+
|
15
|
+
symbolize_keys_to_string(tools).reject do |tool|
|
16
|
+
tool_names.include?("#{tool['function']['name']}")
|
17
|
+
end
|
18
|
+
end
|
10
19
|
end
|
20
|
+
|
11
21
|
def self.debug_print(debug, *args)
|
12
22
|
return unless debug
|
13
23
|
timestamp = Time.now.strftime("%Y-%m-%d %H:%M:%S")
|
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.
|
4
|
+
version: 0.4.0
|
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-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby-openai
|