ruby-openai-swarm 0.3.0.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 98aa27742c5ae980d9c1c8b9457beb2e61a4ff3a1548790d4205cd0f68cef039
4
- data.tar.gz: 37d7a5280f47c2a454a2aba4a5f64587787f1e829fc07b6c27accc90f63f91f8
3
+ metadata.gz: 49bcf71e27c830e373b9732691667aa4ff8657dc944a8dc113fb4b13e807ccd6
4
+ data.tar.gz: 38cd26d3670e7a49652b8b52677a2a7bf722c9f5635bfef55bea296d0502c4d1
5
5
  SHA512:
6
- metadata.gz: 0d671ef9fa72e895bfd3b3e62561fccfc3894a6f19605dd43e68da14b0c9f958d22559da8d9eecd981277a7db035ed39c17535f261cf40849f16a95ac6884516
7
- data.tar.gz: 96c6229510613dbb8b858200916bb9ab22583b7918d604adae0b1bc85bb5b89cfae28a6c833b8c16b2acc1758d1d89643c61f240c896ff6d1d3153536801b46d
6
+ metadata.gz: 0f4b422f084d400ef6eea393f20a650fd02fa8b96370e4781c4228113650763c7eb84c6c1aac21daceb8251cfd83d69ec69b536dff16c3bd7be3e8b0f22a353a
7
+ data.tar.gz: f24c83ea576a7d305069eda9da308f0d39b257065897377b228c785049c41254d3889d4e82b96d40755151b801b95d3a6f8ae5550d73fb871c902c9b21755793
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ruby-openai-swarm (0.3.0.1)
4
+ ruby-openai-swarm (0.4.0)
5
5
  ruby-openai (~> 7.3)
6
6
 
7
7
  GEM
@@ -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, stream: true, debug: env_debug)
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
- # OpenAISwarm::Repl.run_demo_loop(agent, stream: true, debug: env_debug)
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 = OpenAISwarm::Util.clean_message_tools(messages, agent.noisy_tool_calls)
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.empty? ? nil : 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 |chunk|
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 |chunk|
8
- last_sender = chunk['sender'] if chunk.key?('sender')
7
+ response.each do |stream|
8
+ delta = stream['delta']
9
+ if delta
10
+ last_sender = delta['sender'] if delta.key?('sender')
9
11
 
10
- if chunk.key?("content") && !chunk["content"].nil?
11
- if content.empty? && !last_sender.empty?
12
- puts
13
- print "\033[94m#{last_sender}:\033[0m "
14
- last_sender = ""
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 chunk.key?("tool_calls") && !chunk["tool_calls"].nil?
21
- chunk["tool_calls"].each do |tool_call|
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 chunk.key?("delim") && chunk["delim"] == "end" && !content.empty?
32
+ if stream.key?("delim") && stream["delim"] == "end" && !content.empty?
30
33
  puts
31
34
  content = ""
32
35
  end
33
- return chunk["response"] if chunk.key?("response")
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")
@@ -1,3 +1,3 @@
1
1
  module OpenAISwarm
2
- VERSION = "0.3.0.1"
2
+ VERSION = "0.4.0"
3
3
  end
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.3.0.1
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-18 00:00:00.000000000 Z
11
+ date: 2024-12-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-openai