omni_agent 0.1.2 → 0.1.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8c2b72de1f5d07816381e196c20dec88c00746471223afe27b077aea31952794
4
- data.tar.gz: b92ea50f19a39af83fd0d3962b03322e955927fa25c1757836cc5710be8a5be6
3
+ metadata.gz: '0094c9758c242364af55eeffd4e87143f78b1db9ffabb54c06aaa23234030382'
4
+ data.tar.gz: 866bc34809e4c1ecbf21679a2d4dab00a0e1383ae3ac3bb8f6f94348d7ae7b6b
5
5
  SHA512:
6
- metadata.gz: 22d39652f648b750b5aaf292689effe3a855048c7cb4055cdc3f4d21df54611fb16e80d92011a7f893132209013ea7ea222c6da91abe699793529285adc1613a
7
- data.tar.gz: d38d01d15b4b767dd6d704bf4f0f8f07d8c69ac52976f3e05d3e5ce66f74f316bfa28f35c30aabd1095b7ba99f8d7c722f6009f85b014fed8884b83df32e292d
6
+ metadata.gz: bf56557c378dce28538199dc6cf9795923acd7db6ea291dae542845f9f66d923a40ef78e0c1dfdff443e675425c431a58d0cb0d825edc69a171d6b473a0201a1
7
+ data.tar.gz: a4ee13697bbff34e8a3e0e1f196b282bc05d35ba680b8f19c2860c44527d3b61ed7210ed5a6d756dfcf7acea6bcb4219696d04528b7c3d57a479963f390fc1e4
data/CHANGELOG.md CHANGED
@@ -22,4 +22,16 @@ All notable changes to this project will be documented in this file.
22
22
  ## [0.1.2] - 2026-06-11
23
23
 
24
24
  ### Fixed
25
- - Fixed Zeitwerk issue with `OmniAgent::Errors`
25
+ - Fixed Zeitwerk issue with `OmniAgent::Errors`
26
+
27
+ ## [0.1.3] - 2026-06-12
28
+
29
+ ### Added
30
+ - Added `:mock` provider to be able to test `after_generation` and `before_generation` behaviour
31
+ - Added new DSL for tools `stops_generation` which signals to stop the chat flow once this tool is executed
32
+ - Added new instance methods for tools `stops_generation!` and `stops_generation?` that allow to stop the generation within the `execute` logic
33
+ - Added `raw_request` to the agent response, it includes the exact requests params sent to the provider
34
+ - Added `raw_response` to the agent response, it includes the exact response from the provider.
35
+ - Added `generated_messages` to the agent response, it contains all the messages generated by the agent and the user request.
36
+ - Added `history` as a context variable which are the messages to be sent to the provider.
37
+ - Added support for nested datatypes in the `array` schema for tools
@@ -147,14 +147,16 @@ module OmniAgent
147
147
 
148
148
  def run(input, context: {}, prompt_method: nil)
149
149
  context = @default_context.merge(context || {})
150
+ bind_context_instance_variables(context)
150
151
 
151
- messages = [
152
- { role: "system", content: nil },
153
- { role: "user", content: input }
154
- ]
152
+ messages = []
155
153
 
156
154
  run_before_generation_callbacks(input: input, context: context, messages: messages)
155
+ sync_context_from_instance_variables(context)
156
+
157
+ messages.replace(build_messages(input: input, history: context[:history]))
157
158
  messages[0][:content] = system_prompt(context: context, prompt_method: prompt_method)
159
+ initial_messages_count = messages.length - 1
158
160
 
159
161
  filtered_tools = tool_filter(tools: available_tools, agent_tags: self.class.tags)
160
162
 
@@ -163,24 +165,29 @@ module OmniAgent
163
165
 
164
166
  if response.content && !response.tool_calls?
165
167
  messages << { role: "assistant", content: response.content }
168
+ set_after_generation_state(response: response, messages: messages, initial_messages_count: initial_messages_count)
166
169
  run_after_generation_callbacks(input: input, context: context, messages: messages, response: response)
167
- return response.content
170
+ sync_context_from_instance_variables(context)
171
+ return response
168
172
  end
169
173
 
170
- messages << {
171
- role: "assistant",
172
- content: response.content,
173
- tool_calls: response.raw_response.dig("choices", 0, "message", "tool_calls")
174
- }
174
+ messages << build_assistant_tool_call_message(response)
175
+ should_stop_generation = false
175
176
 
176
177
  response.tool_calls.each do |tool_call|
177
178
  tool_name = tool_call[:name]
178
179
  tool_args = tool_call[:arguments]
179
180
  tool_id = tool_call[:id]
180
181
 
181
- tool_class = filtered_tools.find { |t| t.name.demodulize == tool_name }
182
+ tool_class = filtered_tools.find do |t|
183
+ class_name = t.name.to_s
184
+ simple_name = class_name.respond_to?(:demodulize) ? class_name.demodulize : class_name.split("::").last
185
+ simple_name == tool_name
186
+ end
182
187
 
183
188
  if tool_class
189
+ tool_instance = tool_class.new
190
+
184
191
  begin
185
192
  result = tool_class.invoke(tool_args)
186
193
 
@@ -198,6 +205,9 @@ module OmniAgent
198
205
  content: "Error executing tool: #{e.message}"
199
206
  }
200
207
  end
208
+
209
+ should_stop_generation ||= tool_class.respond_to?(:stops_generation?) && tool_class.stops_generation?
210
+ should_stop_generation ||= tool_instance.respond_to?(:stops_generation?) && tool_instance.stops_generation?
201
211
  else
202
212
  messages << {
203
213
  role: "tool",
@@ -207,6 +217,13 @@ module OmniAgent
207
217
  }
208
218
  end
209
219
  end
220
+
221
+ if should_stop_generation
222
+ set_after_generation_state(response: response, messages: messages, initial_messages_count: initial_messages_count)
223
+ run_after_generation_callbacks(input: input, context: context, messages: messages, response: response)
224
+ sync_context_from_instance_variables(context)
225
+ return response
226
+ end
210
227
  end
211
228
  end
212
229
 
@@ -226,6 +243,61 @@ module OmniAgent
226
243
  tools
227
244
  end
228
245
 
246
+ def build_messages(input:, history:)
247
+ normalized_history = normalize_history_messages(history)
248
+
249
+ [
250
+ { role: "system", content: nil },
251
+ *normalized_history,
252
+ { role: "user", content: input }
253
+ ]
254
+ end
255
+
256
+ def normalize_history_messages(history)
257
+ return [] if history.nil?
258
+ return [] unless history.is_a?(Array)
259
+
260
+ history.filter_map do |message|
261
+ next unless message.is_a?(Hash)
262
+
263
+ normalized = message.transform_keys(&:to_sym)
264
+ normalized.compact
265
+ end
266
+ end
267
+
268
+ def build_assistant_tool_call_message(response)
269
+ message = { role: "assistant" }
270
+ message[:content] = response.content unless response.content.nil?
271
+
272
+ raw_tool_calls = response.raw_response.is_a?(Hash) ? response.raw_response.dig("choices", 0, "message", "tool_calls") : nil
273
+ normalized_tool_calls = normalize_tool_calls_for_message(raw_tool_calls, response.tool_calls)
274
+ message[:tool_calls] = normalized_tool_calls unless normalized_tool_calls.empty?
275
+
276
+ message
277
+ end
278
+
279
+ def normalize_tool_calls_for_message(raw_tool_calls, parsed_tool_calls)
280
+ return raw_tool_calls if raw_tool_calls.is_a?(Array) && !raw_tool_calls.empty?
281
+
282
+ Array(parsed_tool_calls).map do |tool_call|
283
+ {
284
+ "id" => tool_call[:id],
285
+ "type" => "function",
286
+ "function" => {
287
+ "name" => tool_call[:name],
288
+ "arguments" => serialize_tool_arguments(tool_call[:arguments])
289
+ }
290
+ }
291
+ end
292
+ end
293
+
294
+ def serialize_tool_arguments(arguments)
295
+ return arguments if arguments.is_a?(String)
296
+ return JSON.generate(arguments) if defined?(JSON)
297
+
298
+ arguments.to_s
299
+ end
300
+
229
301
  def run_alias_entrypoint_logic(alias_name, fallback_input: nil)
230
302
  @message = nil
231
303
  result = public_send(alias_name)
@@ -249,13 +321,24 @@ module OmniAgent
249
321
  end
250
322
 
251
323
  def run_after_generation_callbacks(input:, context:, messages:, response:)
252
- payload = { input: input, context: context, messages: messages, response: response }
324
+ payload = {
325
+ input: input,
326
+ context: context,
327
+ messages: messages,
328
+ generated_messages: response.generated_messages,
329
+ response: response
330
+ }
253
331
 
254
332
  self.class.configured_after_generation_callbacks.each do |callback_name|
255
333
  invoke_generation_callback(callback_name, payload)
256
334
  end
257
335
  end
258
336
 
337
+ def set_after_generation_state(response:, messages:, initial_messages_count:)
338
+ generated_messages = messages.drop(initial_messages_count)
339
+ @response = response.with_generated_messages(generated_messages)
340
+ end
341
+
259
342
  def invoke_generation_callback(callback_name, payload)
260
343
  original_callback_name = "__omni_agent_original_#{callback_name}".to_sym
261
344
  callback_target = if respond_to?(original_callback_name, true)
@@ -265,12 +348,45 @@ module OmniAgent
265
348
  end
266
349
 
267
350
  callback_method = self.class.instance_method(callback_target)
351
+ bind_context_instance_variables(payload[:context])
268
352
 
269
353
  if callback_method.arity == 0
270
354
  __send__(callback_target)
271
355
  else
272
356
  __send__(callback_target, payload)
273
357
  end
358
+
359
+ sync_context_from_instance_variables(payload[:context])
360
+ end
361
+
362
+ def bind_context_instance_variables(context)
363
+ return unless context.is_a?(Hash)
364
+
365
+ @__omni_agent_context_bindings ||= {}
366
+
367
+ context.each do |key, value|
368
+ ivar_name = "@#{key}"
369
+ next unless ivar_name.match?(/\A@[a-zA-Z_]\w*\z/)
370
+
371
+ ivar = ivar_name.to_sym
372
+ instance_variable_set(ivar, value)
373
+ @__omni_agent_context_bindings[ivar] = key
374
+ end
375
+ end
376
+
377
+ def sync_context_from_instance_variables(context)
378
+ return unless context.is_a?(Hash)
379
+
380
+ if instance_variable_defined?(:@__omni_agent_context_bindings)
381
+ @__omni_agent_context_bindings.each do |ivar, key|
382
+ next unless instance_variable_defined?(ivar)
383
+ context[key] = instance_variable_get(ivar)
384
+ end
385
+ end
386
+
387
+ if instance_variable_defined?(:@history)
388
+ context[:history] = @history
389
+ end
274
390
  end
275
391
 
276
392
  def system_prompt(context:, prompt_method: nil)
@@ -292,7 +408,7 @@ module OmniAgent
292
408
  isolated_scope.instance_variable_set("@#{key}", value)
293
409
  end
294
410
 
295
- internal_vars = [:@provider, :@chat_options] # Blacklists internal instance variables
411
+ internal_vars = [ :@provider, :@chat_options ] # Blacklists internal instance variables
296
412
 
297
413
  (instance_variables - internal_vars).each do |ivar|
298
414
  isolated_scope.instance_variable_set(ivar, instance_variable_get(ivar))
@@ -301,7 +417,7 @@ module OmniAgent
301
417
  base_prompt = render_prompt_template(base_file_path, isolated_scope)
302
418
  method_prompt = render_prompt_template(method_file_path, isolated_scope)
303
419
 
304
- prompts = [base_prompt, method_prompt].compact.reject(&:empty?)
420
+ prompts = [ base_prompt, method_prompt ].compact.reject(&:empty?)
305
421
  return prompts.join("\n\n") if prompts.any?
306
422
 
307
423
  "You are a helpful assistant with access to local tools."
@@ -325,4 +441,4 @@ module OmniAgent
325
441
  .downcase
326
442
  end
327
443
  end
328
- end
444
+ end
@@ -13,6 +13,32 @@ module OmniAgent
13
13
  raise NotImplementedError, "Providers must implement #chat"
14
14
  end
15
15
 
16
+ def validate_messages!(messages, allowed_roles:)
17
+ unless messages.is_a?(Array)
18
+ raise OmniAgent::Error, "messages must be an array"
19
+ end
20
+
21
+ normalized_roles = Array(allowed_roles).map(&:to_s)
22
+
23
+ messages.each_with_index do |message, index|
24
+ unless message.is_a?(Hash)
25
+ raise OmniAgent::Error, "message at index #{index} must be a hash"
26
+ end
27
+
28
+ role = message[:role] || message["role"]
29
+ if role.nil?
30
+ raise OmniAgent::Error, "message at index #{index} is missing role"
31
+ end
32
+
33
+ role_name = role.to_s
34
+ unless normalized_roles.include?(role_name)
35
+ raise OmniAgent::Error, "invalid message role '#{role_name}' at index #{index}"
36
+ end
37
+
38
+ validate_message_payload!(message, index, role_name)
39
+ end
40
+ end
41
+
16
42
  protected
17
43
 
18
44
  def default_api_key
@@ -22,6 +48,33 @@ module OmniAgent
22
48
  def default_model
23
49
  raise NotImplementedError, "Providers must define a default model"
24
50
  end
51
+
52
+ def validate_message_payload!(message, index, role_name)
53
+ has_content = message.key?(:content) || message.key?("content")
54
+ content_value = message[:content] || message["content"]
55
+
56
+ case role_name
57
+ when "assistant"
58
+ has_tool_calls = message.key?(:tool_calls) || message.key?("tool_calls")
59
+ return if has_content || has_tool_calls
60
+
61
+ raise OmniAgent::Error, "assistant message at index #{index} must include content or tool_calls"
62
+ when "tool"
63
+ tool_call_id = message[:tool_call_id] || message["tool_call_id"]
64
+ tool_name = message[:name] || message["name"]
65
+ unless tool_call_id && tool_name && has_content
66
+ raise OmniAgent::Error, "tool message at index #{index} must include tool_call_id, name, and content"
67
+ end
68
+ else
69
+ unless has_content
70
+ raise OmniAgent::Error, "#{role_name} message at index #{index} must include content"
71
+ end
72
+ end
73
+
74
+ if has_content && content_value.nil?
75
+ raise OmniAgent::Error, "message content at index #{index} cannot be nil"
76
+ end
77
+ end
25
78
  end
26
79
  end
27
- end
80
+ end
@@ -0,0 +1,40 @@
1
+ module OmniAgent
2
+ module Providers
3
+ class Mock < Base
4
+ LOREM_IPSUM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
5
+
6
+ def chat(messages:, tools: [], **_options)
7
+ validate_messages!(messages, allowed_roles: %i[system user assistant tool])
8
+
9
+ OmniAgent::Providers::Response.new(
10
+ content: LOREM_IPSUM,
11
+ raw_request: {
12
+ model: model,
13
+ messages: messages,
14
+ tools: tools
15
+ },
16
+ raw_response: {
17
+ "choices" => [
18
+ {
19
+ "message" => {
20
+ "content" => LOREM_IPSUM
21
+ }
22
+ }
23
+ ]
24
+ },
25
+ tool_calls: []
26
+ )
27
+ end
28
+
29
+ protected
30
+
31
+ def default_api_key
32
+ nil
33
+ end
34
+
35
+ def default_model
36
+ "mock"
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,18 +1,40 @@
1
1
  # lib/omni_agent/providers/openai.rb
2
- require 'json'
2
+ require "json"
3
3
 
4
4
  module OmniAgent
5
5
  module Providers
6
6
  class OpenAI < Base
7
7
  begin
8
- require 'openai'
8
+ require "openai"
9
9
  rescue LoadError
10
- raise OmniAgent::MissingDependencyError,
10
+ raise OmniAgent::MissingDependencyError,
11
11
  "The 'openai' gem is required to use the OpenAI provider. " \
12
12
  "Please add `gem 'openai'` to your Gemfile."
13
13
  end
14
-
14
+
15
15
  def chat(messages:, tools: [], **options)
16
+ validate_messages!(messages, allowed_roles: %i[system user assistant tool])
17
+
18
+ messages = messages.map do |msg|
19
+ clean_msg = msg.reject { |_, v| v.nil? }
20
+
21
+ if !clean_msg.key?(:tool_calls) && !clean_msg[:content].is_a?(String)
22
+ clean_msg[:content] = ""
23
+ end
24
+
25
+ if clean_msg[:tool_calls].is_a?(Array)
26
+ clean_msg[:tool_calls].each do |tc|
27
+ if tc.dig("function", "arguments").is_a?(Hash) || tc.dig(:function, :arguments).is_a?(Hash)
28
+ func = tc["function"] || tc[:function]
29
+ args_key = func.key?("arguments") ? "arguments" : :arguments
30
+ func[args_key] = func[args_key].to_json
31
+ end
32
+ end
33
+ end
34
+
35
+ clean_msg
36
+ end
37
+
16
38
  openai_tools = tools.map { |tool| format_tool(tool) }
17
39
 
18
40
  payload = {
@@ -24,9 +46,9 @@ module OmniAgent
24
46
 
25
47
  response = client.chat.completions.create(**payload)
26
48
 
27
- parse_response(response)
28
- rescue => e
29
- puts "Error during OpenAI chat: #{e.message}"
49
+ parse_response(response, payload)
50
+ rescue => e
51
+ raise OmniAgent::Error, "Error during OpenAI chat: #{e.message}"
30
52
  end
31
53
 
32
54
  protected
@@ -36,7 +58,7 @@ module OmniAgent
36
58
  end
37
59
 
38
60
  def default_api_key
39
- ENV.fetch('OPENAI_ACCESS_TOKEN', nil)
61
+ ENV.fetch("OPENAI_ACCESS_TOKEN", nil)
40
62
  end
41
63
 
42
64
  def default_model
@@ -46,20 +68,25 @@ module OmniAgent
46
68
  private
47
69
 
48
70
  def format_tool(tool_class)
71
+ schema = tool_class.json_schema.dup
72
+
73
+ schema[:additionalProperties] = false
74
+
49
75
  {
50
76
  type: "function",
51
77
  function: {
52
78
  name: tool_class.name.split("::").last,
53
79
  description: tool_class.description,
54
- parameters: tool_class.json_schema
80
+ parameters: schema
55
81
  }
56
82
  }
57
83
  end
58
84
 
59
- def parse_response(raw_response)
60
- choices = raw_response.respond_to?(:choices) ? raw_response.choices : raw_response["choices"]
85
+ def parse_response(raw_response, raw_request)
86
+ provider_raw_response = raw_response.respond_to?(:to_h) ? raw_response.to_h : raw_response
87
+ choices = raw_response.respond_to?(:choices) ? raw_response.choices : provider_raw_response["choices"]
61
88
  first_choice = choices&.first || {}
62
-
89
+
63
90
  message = first_choice.respond_to?(:message) ? first_choice.message : (first_choice["message"] || {})
64
91
  content = message.respond_to?(:content) ? message.content : message["content"]
65
92
 
@@ -75,35 +102,13 @@ module OmniAgent
75
102
  }
76
103
  end
77
104
 
78
- compat_tool_calls = raw_tool_calls.map do |tc|
79
- fn = tc.respond_to?(:function) ? tc.function : tc["function"]
80
- {
81
- "id" => tc.respond_to?(:id) ? tc.id : tc["id"],
82
- "type" => "function",
83
- "function" => {
84
- "name" => fn.respond_to?(:name) ? fn.name : fn["name"],
85
- "arguments" => fn.respond_to?(:arguments) ? fn.arguments : fn["arguments"]
86
- }
87
- }
88
- end.compact
89
-
90
- compat_raw_response = {
91
- "choices" => [
92
- {
93
- "message" => {
94
- "content" => content,
95
- "tool_calls" => (compat_tool_calls.empty? ? nil : compat_tool_calls)
96
- }.compact
97
- }
98
- ]
99
- }
100
-
101
105
  OmniAgent::Providers::Response.new(
102
106
  content: content,
103
- raw_response: compat_raw_response,
107
+ raw_response: provider_raw_response,
108
+ raw_request: raw_request,
104
109
  tool_calls: tool_calls
105
110
  )
106
111
  end
107
112
  end
108
113
  end
109
- end
114
+ end
@@ -1,17 +1,28 @@
1
1
  module OmniAgent
2
2
  module Providers
3
3
  class Response
4
- attr_reader :content, :tool_calls, :raw_response
4
+ attr_reader :content, :tool_calls, :raw_response, :raw_request, :generated_messages
5
5
 
6
- def initialize(content:, tool_calls: [], raw_response: nil)
6
+ def initialize(content:, tool_calls: [], raw_response: nil, raw_request: nil, generated_messages: [])
7
7
  @content = content
8
8
  @tool_calls = tool_calls || []
9
9
  @raw_response = raw_response
10
+ @raw_request = raw_request
11
+ @generated_messages = generated_messages || []
10
12
  end
11
13
 
12
14
  def tool_calls?
13
15
  @tool_calls.any?
14
16
  end
17
+
18
+ def answer
19
+ @content
20
+ end
21
+
22
+ def with_generated_messages(messages)
23
+ @generated_messages = Array(messages)
24
+ self
25
+ end
15
26
  end
16
27
  end
17
- end
28
+ end
@@ -3,8 +3,9 @@ module OmniAgent
3
3
  module Providers
4
4
  def self.registry
5
5
  {
6
- openai: OmniAgent::Providers::OpenAI
6
+ openai: OmniAgent::Providers::OpenAI,
7
+ mock: OmniAgent::Providers::Mock
7
8
  }
8
9
  end
9
10
  end
10
- end
11
+ end
@@ -20,10 +20,24 @@ module OmniAgent
20
20
  add_property(name, type: "boolean", description: description, required: required)
21
21
  end
22
22
 
23
- def array(name, items_type:, description: nil, required: true)
24
- property = { type: "array", items: { type: items_type } }
23
+ def array(name, items_type: nil, description: nil, required: true, &block)
24
+ property = { type: "array" }
25
25
  property[:description] = description if description
26
-
26
+
27
+ if block_given?
28
+ nested_builder = SchemaBuilder.new
29
+ nested_builder.instance_eval(&block)
30
+
31
+ property[:items] = {
32
+ type: "object",
33
+ properties: nested_builder.properties,
34
+ required: nested_builder.required_fields,
35
+ additionalProperties: false
36
+ }
37
+ else
38
+ property[:items] = { type: items_type || "string" }
39
+ end
40
+
27
41
  @properties[name] = property
28
42
  @required_fields << name.to_s if required
29
43
  end
@@ -35,7 +49,7 @@ module OmniAgent
35
49
  if block_given?
36
50
  nested_builder = SchemaBuilder.new
37
51
  nested_builder.instance_eval(&block)
38
-
52
+
39
53
  property[:properties] = nested_builder.properties
40
54
  property[:required] = nested_builder.required_fields
41
55
  property[:additionalProperties] = false
@@ -52,10 +66,10 @@ module OmniAgent
52
66
  def add_property(name, type:, description:, required:)
53
67
  property = { type: type }
54
68
  property[:description] = description if description
55
-
69
+
56
70
  @properties[name] = property
57
71
  @required_fields << name.to_s if required
58
72
  end
59
73
  end
60
74
  end
61
- end
75
+ end
@@ -1,4 +1,3 @@
1
- # lib/omni_agents/tool.rb
2
1
  module OmniAgent
3
2
  class Tool
4
3
  class << self
@@ -26,7 +25,7 @@ module OmniAgent
26
25
  if block_given?
27
26
  builder = SchemaBuilder.new
28
27
  builder.instance_eval(&block)
29
-
28
+
30
29
  @properties = builder.properties
31
30
  @required = builder.required_fields
32
31
  end
@@ -37,13 +36,26 @@ module OmniAgent
37
36
  type: "object",
38
37
  properties: @properties || {},
39
38
  required: @required || [],
40
- additionalProperties: false
39
+ additionalProperties: false
41
40
  }
42
41
  end
43
42
 
44
43
  def invoke(arguments_hash)
45
44
  kwargs = arguments_hash.transform_keys(&:to_sym)
46
- new.execute(**kwargs)
45
+
46
+ valid_keys = (@properties || {}).keys.map(&:to_sym)
47
+
48
+ filtered_kwargs = kwargs.slice(*valid_keys)
49
+
50
+ new.execute(**filtered_kwargs)
51
+ end
52
+
53
+ def stops_generation(value = true)
54
+ @stops_generation = !!value
55
+ end
56
+
57
+ def stops_generation?
58
+ @stops_generation == true
47
59
  end
48
60
 
49
61
  private
@@ -61,8 +73,16 @@ module OmniAgent
61
73
  end
62
74
  end
63
75
 
76
+ def stop_generation!
77
+ @stops_generation_instance = true
78
+ end
79
+
80
+ def stops_generation?
81
+ @stops_generation_instance == true
82
+ end
83
+
64
84
  def execute(**args)
65
85
  raise NotImplementedError, "#{self.class.name} must implement #execute"
66
86
  end
67
87
  end
68
- end
88
+ end
@@ -1,3 +1,3 @@
1
1
  module OmniAgent
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.3"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: omni_agent
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - ACR1209
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-06-11 00:00:00.000000000 Z
11
+ date: 2026-06-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -67,6 +67,7 @@ files:
67
67
  - lib/omni_agent/errors.rb
68
68
  - lib/omni_agent/providers.rb
69
69
  - lib/omni_agent/providers/base.rb
70
+ - lib/omni_agent/providers/mock.rb
70
71
  - lib/omni_agent/providers/openai.rb
71
72
  - lib/omni_agent/providers/response.rb
72
73
  - lib/omni_agent/tasks/omni_agent_tasks.rake