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 +4 -4
- data/CHANGELOG.md +35 -13
- data/README.md +4 -3
- data/lib/generators/omni_agent/agent/agent_generator.rb +2 -2
- data/lib/omni_agent/agent.rb +19 -2
- data/lib/omni_agent/configuration.rb +4 -1
- data/lib/omni_agent/errors.rb +4 -0
- data/lib/omni_agent/providers/base.rb +23 -0
- data/lib/omni_agent/providers/openai.rb +8 -1
- data/lib/omni_agent/railtie.rb +31 -0
- data/lib/omni_agent/tool.rb +7 -6
- data/lib/omni_agent/version.rb +1 -1
- data/lib/omni_agent.rb +6 -0
- metadata +4 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1513c46e356438c23dfd193f25c390df240f2d1d812813114e2e582222de2965
|
|
4
|
+
data.tar.gz: a9b05b4445128aea795b3f7f5ea80cb6bd668bf2b674c15651011ce6780e0c2e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
|
7
|
+
## [0.1.5] - 2026-06-18
|
|
8
8
|
|
|
9
9
|
### Added
|
|
10
|
-
-
|
|
11
|
-
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
17
|
+
# [0.1.4] - 2026-06-16
|
|
23
18
|
|
|
24
19
|
### Fixed
|
|
25
|
-
-
|
|
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/
|
|
59
|
+
app/agents/
|
|
60
60
|
research_agent.rb
|
|
61
|
-
|
|
62
|
-
|
|
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(
|
|
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
|
data/lib/omni_agent/agent.rb
CHANGED
|
@@ -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 =
|
|
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]
|
|
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
|
data/lib/omni_agent/errors.rb
CHANGED
|
@@ -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
|
data/lib/omni_agent/tool.rb
CHANGED
|
@@ -40,14 +40,10 @@ module OmniAgent
|
|
|
40
40
|
}
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
-
def
|
|
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
|
data/lib/omni_agent/version.rb
CHANGED
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.
|
|
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:
|
|
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.
|
|
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: []
|