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 +4 -4
- data/CHANGELOG.md +13 -1
- data/lib/omni_agent/agent.rb +131 -15
- data/lib/omni_agent/providers/base.rb +54 -1
- data/lib/omni_agent/providers/mock.rb +40 -0
- data/lib/omni_agent/providers/openai.rb +42 -37
- data/lib/omni_agent/providers/response.rb +14 -3
- data/lib/omni_agent/providers.rb +3 -2
- data/lib/omni_agent/tool/schema_builder.rb +20 -6
- data/lib/omni_agent/tool.rb +25 -5
- data/lib/omni_agent/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '0094c9758c242364af55eeffd4e87143f78b1db9ffabb54c06aaa23234030382'
|
|
4
|
+
data.tar.gz: 866bc34809e4c1ecbf21679a2d4dab00a0e1383ae3ac3bb8f6f94348d7ae7b6b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
data/lib/omni_agent/agent.rb
CHANGED
|
@@ -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
|
-
|
|
170
|
+
sync_context_from_instance_variables(context)
|
|
171
|
+
return response
|
|
168
172
|
end
|
|
169
173
|
|
|
170
|
-
messages <<
|
|
171
|
-
|
|
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
|
|
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 = {
|
|
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
|
|
2
|
+
require "json"
|
|
3
3
|
|
|
4
4
|
module OmniAgent
|
|
5
5
|
module Providers
|
|
6
6
|
class OpenAI < Base
|
|
7
7
|
begin
|
|
8
|
-
require
|
|
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
|
-
|
|
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(
|
|
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:
|
|
80
|
+
parameters: schema
|
|
55
81
|
}
|
|
56
82
|
}
|
|
57
83
|
end
|
|
58
84
|
|
|
59
|
-
def parse_response(raw_response)
|
|
60
|
-
|
|
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:
|
|
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
|
data/lib/omni_agent/providers.rb
CHANGED
|
@@ -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
|
|
24
|
-
property = { type: "array"
|
|
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
|
data/lib/omni_agent/tool.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
data/lib/omni_agent/version.rb
CHANGED
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.
|
|
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
|
+
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
|