omni_agent 0.1.3 → 0.1.5

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: '0094c9758c242364af55eeffd4e87143f78b1db9ffabb54c06aaa23234030382'
4
- data.tar.gz: 866bc34809e4c1ecbf21679a2d4dab00a0e1383ae3ac3bb8f6f94348d7ae7b6b
3
+ metadata.gz: 1513c46e356438c23dfd193f25c390df240f2d1d812813114e2e582222de2965
4
+ data.tar.gz: a9b05b4445128aea795b3f7f5ea80cb6bd668bf2b674c15651011ce6780e0c2e
5
5
  SHA512:
6
- metadata.gz: bf56557c378dce28538199dc6cf9795923acd7db6ea291dae542845f9f66d923a40ef78e0c1dfdff443e675425c431a58d0cb0d825edc69a171d6b473a0201a1
7
- data.tar.gz: a4ee13697bbff34e8a3e0e1f196b282bc05d35ba680b8f19c2860c44527d3b61ed7210ed5a6d756dfcf7acea6bcb4219696d04528b7c3d57a479963f390fc1e4
6
+ metadata.gz: 4ee226683fb316a82a18457b50bbe783712fe4ad9115befa4ab7c9421b31747acf2b3f609360c126a599ac8dded16a9cc4dda670e115f3bed3711147776956a3
7
+ data.tar.gz: 6ab849bcae377ccc919d6daf13b0556d9d6ed07b1cd16d645fe66fb026df4dd941d4f9a06b396ea044bb4ad0db886457c25ce1ca4a4c4d4de6306dc31edf47f2
data/CHANGELOG.md CHANGED
@@ -4,25 +4,27 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
- ## [0.1.0] - 2026-06-09
7
+ ## [0.1.5] - 2026-06-18
8
8
 
9
9
  ### Added
10
- - Initial release of OmniAgent as a Rails engine for building application-native AI agents.
11
- - Agent runtime with provider abstraction, tool-calling loop, callbacks, and prompt rendering.
12
- - OpenAI provider support via the `openai` gem.
13
- - Tool DSL with JSON schema generation.
14
- - Rails generators and tasks for agent scaffolding.
15
- - RSpec coverage for agent runtime, providers, tools, and integration flows.
10
+ - Added max tool iterations limit on the agent loop, with corresponding error raised when exceeded.
11
+ - Added retry logic to the OpenAI provider, configurable via `Configuration`.
16
12
 
17
- ## [0.1.1] - 2026-06-09
18
-
19
- ### Changed
20
- - Lowered Rails version needed.
13
+ ### Fixed
14
+ - Improved provider resolution error handling for unknown providers.
15
+ - Forced eager load of error aliases to avoid Zeitwerk autoloading issues.
21
16
 
22
- ## [0.1.2] - 2026-06-11
17
+ # [0.1.4] - 2026-06-16
23
18
 
24
19
  ### Fixed
25
- - Fixed Zeitwerk issue with `OmniAgent::Errors`
20
+ - Change invoke method to an instance method instead of a class method, to actually catch invocations of the `stop_generation!` method.
21
+
22
+ ### Changed
23
+ - Remove agent file from directory to prevent needing to change Zeitwerk initializer as Railtie wasn't allowing to change behaviour without it.
24
+
25
+ ### Added
26
+ - Add Railtie to project to handle concerns.
27
+ - Added docusaurus, the actual docs will be done later.
26
28
 
27
29
  ## [0.1.3] - 2026-06-12
28
30
 
@@ -35,3 +37,23 @@ All notable changes to this project will be documented in this file.
35
37
  - Added `generated_messages` to the agent response, it contains all the messages generated by the agent and the user request.
36
38
  - Added `history` as a context variable which are the messages to be sent to the provider.
37
39
  - Added support for nested datatypes in the `array` schema for tools
40
+
41
+ ## [0.1.2] - 2026-06-11
42
+
43
+ ### Fixed
44
+ - Fixed Zeitwerk issue with `OmniAgent::Errors`
45
+
46
+ ## [0.1.1] - 2026-06-09
47
+
48
+ ### Changed
49
+ - Lowered Rails version needed.
50
+
51
+ ## [0.1.0] - 2026-06-09
52
+
53
+ ### Added
54
+ - Initial release of OmniAgent as a Rails engine for building application-native AI agents.
55
+ - Agent runtime with provider abstraction, tool-calling loop, callbacks, and prompt rendering.
56
+ - OpenAI provider support via the `openai` gem.
57
+ - Tool DSL with JSON schema generation.
58
+ - Rails generators and tasks for agent scaffolding.
59
+ - RSpec coverage for agent runtime, providers, tools, and integration flows.
data/README.md CHANGED
@@ -56,10 +56,11 @@ OPENAI_ACCESS_TOKEN=your_api_key_here
56
56
  4. Implement your agent prompt and optional tools under:
57
57
 
58
58
  ```text
59
- app/agents/research_agent/
59
+ app/agents/
60
60
  research_agent.rb
61
- prompt.md.erb
62
- tools/
61
+ research_agent/
62
+ prompt.md.erb
63
+ tools/
63
64
  ```
64
65
 
65
66
  ## Agent Example
@@ -46,7 +46,7 @@ module OmniAgent
46
46
  end
47
47
 
48
48
  def agent_file_path
49
- File.join(agent_directory, "#{file_name}.rb")
49
+ File.join(destination_root, "app", "agents", "#{file_name}.rb")
50
50
  end
51
51
 
52
52
  def prompt_file_path
@@ -89,7 +89,7 @@ module OmniAgent
89
89
 
90
90
  def requested_tool_names
91
91
  names = Array(options[:with_tools]).map(&:to_s).map(&:strip).reject(&:empty?)
92
- names.empty? ? ["ExampleTool"] : names
92
+ names.empty? ? [ "ExampleTool" ] : names
93
93
  end
94
94
 
95
95
  def generated_model_expression
@@ -159,8 +159,17 @@ module OmniAgent
159
159
  initial_messages_count = messages.length - 1
160
160
 
161
161
  filtered_tools = tool_filter(tools: available_tools, agent_tags: self.class.tags)
162
+ max_iterations = OmniAgent.configuration.max_tool_iterations
163
+ iterations = 0
162
164
 
163
165
  loop do
166
+ iterations += 1
167
+ if iterations > max_iterations
168
+ raise OmniAgent::MaxToolIterationsError,
169
+ "Exceeded max_tool_iterations (#{max_iterations}) without a final response. " \
170
+ "Increase OmniAgent.configuration.max_tool_iterations if more tool calls are expected."
171
+ end
172
+
164
173
  response = provider.chat(messages: messages, tools: filtered_tools, **@chat_options)
165
174
 
166
175
  if response.content && !response.tool_calls?
@@ -189,7 +198,7 @@ module OmniAgent
189
198
  tool_instance = tool_class.new
190
199
 
191
200
  begin
192
- result = tool_class.invoke(tool_args)
201
+ result = tool_instance.invoke(tool_args)
193
202
 
194
203
  messages << {
195
204
  role: "tool",
@@ -309,7 +318,15 @@ module OmniAgent
309
318
  end
310
319
 
311
320
  def resolve_provider(name, model)
312
- OmniAgent::Providers.registry[name.to_sym].new(model: model)
321
+ provider_class = OmniAgent::Providers.registry[name.to_sym]
322
+
323
+ unless provider_class
324
+ known = OmniAgent::Providers.registry.keys.join(", ")
325
+ raise OmniAgent::UnknownProviderError,
326
+ "Unknown provider #{name.inspect}. Known providers: #{known}"
327
+ end
328
+
329
+ provider_class.new(model: model)
313
330
  end
314
331
 
315
332
  def run_before_generation_callbacks(input:, context:, messages:)
@@ -1,10 +1,13 @@
1
1
  module OmniAgent
2
2
  class Configuration
3
- attr_accessor :default_provider, :default_model
3
+ attr_accessor :default_provider, :default_model, :max_retries, :retry_base_delay, :max_tool_iterations
4
4
 
5
5
  def initialize
6
6
  @default_provider = :openai
7
7
  @default_model = "gpt-4o-mini"
8
+ @max_retries = 3
9
+ @retry_base_delay = 0.5
10
+ @max_tool_iterations = 10
8
11
  end
9
12
  end
10
13
  end
@@ -2,8 +2,12 @@ module OmniAgent
2
2
  module Errors
3
3
  class Error < StandardError; end
4
4
  class MissingDependencyError < Error; end
5
+ class UnknownProviderError < Error; end
6
+ class MaxToolIterationsError < Error; end
5
7
  end
6
8
 
7
9
  Error = Errors::Error
8
10
  MissingDependencyError = Errors::MissingDependencyError
11
+ UnknownProviderError = Errors::UnknownProviderError
12
+ MaxToolIterationsError = Errors::MaxToolIterationsError
9
13
  end
@@ -49,6 +49,29 @@ module OmniAgent
49
49
  raise NotImplementedError, "Providers must define a default model"
50
50
  end
51
51
 
52
+ # Override in subclasses to flag exceptions worth retrying (rate limits, timeouts, 5xx).
53
+ def retryable_error?(_error)
54
+ false
55
+ end
56
+
57
+ def with_retries
58
+ max_retries = OmniAgent.configuration.max_retries
59
+ base_delay = OmniAgent.configuration.retry_base_delay
60
+ attempt = 0
61
+
62
+ begin
63
+ yield
64
+ rescue => e
65
+ raise unless retryable_error?(e)
66
+
67
+ attempt += 1
68
+ raise if attempt > max_retries
69
+
70
+ sleep(base_delay * (2**(attempt - 1)))
71
+ retry
72
+ end
73
+ end
74
+
52
75
  def validate_message_payload!(message, index, role_name)
53
76
  has_content = message.key?(:content) || message.key?("content")
54
77
  content_value = message[:content] || message["content"]
@@ -44,7 +44,7 @@ module OmniAgent
44
44
  payload[:tools] = openai_tools if openai_tools.any?
45
45
  payload.merge!(options) if options.any?
46
46
 
47
- response = client.chat.completions.create(**payload)
47
+ response = with_retries { client.chat.completions.create(**payload) }
48
48
 
49
49
  parse_response(response, payload)
50
50
  rescue => e
@@ -53,6 +53,13 @@ module OmniAgent
53
53
 
54
54
  protected
55
55
 
56
+ def retryable_error?(error)
57
+ defined?(::OpenAI::Errors) &&
58
+ (error.is_a?(::OpenAI::Errors::RateLimitError) ||
59
+ error.is_a?(::OpenAI::Errors::InternalServerError) ||
60
+ error.is_a?(::OpenAI::Errors::APIConnectionError))
61
+ end
62
+
56
63
  def client
57
64
  @client ||= ::OpenAI::Client.new(api_key: @api_key)
58
65
  end
@@ -0,0 +1,31 @@
1
+ # lib/omni_agent/railtie.rb
2
+ module OmniAgent
3
+ class Railtie < ::Rails::Railtie
4
+ initializer "omni_agent.remove_agents_from_zeitwerk", before: :setup_main_autoloader do
5
+ agents_path = Rails.root.join("app", "agents")
6
+ if agents_path.exist?
7
+ Rails.autoloaders.main.ignore(agents_path)
8
+ end
9
+ end
10
+
11
+ config.to_prepare do
12
+ agents_dir = Rails.root.join("app", "agents")
13
+ next unless agents_dir.exist?
14
+
15
+ Dir.glob(agents_dir.join("concerns/**/*.rb")).each { |f| require_dependency f }
16
+
17
+ Dir.glob(agents_dir.join("*")).each do |file|
18
+ next unless File.directory?(file)
19
+ next if File.basename(file) == "concerns"
20
+
21
+ main_agent_file = File.join(file, "#{File.basename(file)}.rb")
22
+ require_dependency main_agent_file if File.file?(main_agent_file)
23
+
24
+ Dir.glob(File.join(file, "**/*.rb")).each do |sub_file|
25
+ next if sub_file == main_agent_file
26
+ require_dependency sub_file
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -40,14 +40,10 @@ module OmniAgent
40
40
  }
41
41
  end
42
42
 
43
- def invoke(arguments_hash)
43
+ def parse_arguments(arguments_hash)
44
44
  kwargs = arguments_hash.transform_keys(&:to_sym)
45
-
46
45
  valid_keys = (@properties || {}).keys.map(&:to_sym)
47
-
48
- filtered_kwargs = kwargs.slice(*valid_keys)
49
-
50
- new.execute(**filtered_kwargs)
46
+ kwargs.slice(*valid_keys)
51
47
  end
52
48
 
53
49
  def stops_generation(value = true)
@@ -73,6 +69,11 @@ module OmniAgent
73
69
  end
74
70
  end
75
71
 
72
+ def invoke(arguments_hash)
73
+ filtered_kwargs = self.class.parse_arguments(arguments_hash)
74
+ execute(**filtered_kwargs)
75
+ end
76
+
76
77
  def stop_generation!
77
78
  @stops_generation_instance = true
78
79
  end
@@ -1,3 +1,3 @@
1
1
  module OmniAgent
2
- VERSION = "0.1.3"
2
+ VERSION = "0.1.5"
3
3
  end
data/lib/omni_agent.rb CHANGED
@@ -7,6 +7,12 @@ loader.inflector.inflect("openai" => "OpenAI")
7
7
  loader.ignore(File.expand_path("generators", __dir__))
8
8
  loader.setup
9
9
 
10
+ # errors.rb defines top-level aliases (OmniAgent::MissingDependencyError, etc.)
11
+ # that Zeitwerk cannot autoload, since they don't map to a file path of their
12
+ # own. Force it to load eagerly so the aliases exist regardless of which
13
+ # constant is referenced first.
14
+ require "omni_agent/errors"
15
+
10
16
 
11
17
  module OmniAgent
12
18
  class << self
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: omni_agent
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - ACR1209
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2026-06-13 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: rails
@@ -70,6 +69,7 @@ files:
70
69
  - lib/omni_agent/providers/mock.rb
71
70
  - lib/omni_agent/providers/openai.rb
72
71
  - lib/omni_agent/providers/response.rb
72
+ - lib/omni_agent/railtie.rb
73
73
  - lib/omni_agent/tasks/omni_agent_tasks.rake
74
74
  - lib/omni_agent/tool.rb
75
75
  - lib/omni_agent/tool/schema_builder.rb
@@ -81,7 +81,6 @@ metadata:
81
81
  homepage_uri: https://github.com/ACR1209/omni_agent
82
82
  source_code_uri: https://github.com/ACR1209/omni_agent
83
83
  changelog_uri: https://github.com/ACR1209/omni_agent/blob/main/CHANGELOG.md
84
- post_install_message:
85
84
  rdoc_options: []
86
85
  require_paths:
87
86
  - lib
@@ -96,8 +95,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
96
95
  - !ruby/object:Gem::Version
97
96
  version: '0'
98
97
  requirements: []
99
- rubygems_version: 3.5.11
100
- signing_key:
98
+ rubygems_version: 3.6.9
101
99
  specification_version: 4
102
100
  summary: Rails engine for building AI agents with tools.
103
101
  test_files: []