activeagent 0.6.0rc4 → 0.6.0
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/README.md +1 -1
- data/lib/active_agent/action_prompt/base.rb +5 -2
- data/lib/active_agent/callbacks.rb +9 -0
- data/lib/active_agent/generation.rb +12 -0
- data/lib/active_agent/generation_provider/anthropic_provider.rb +1 -1
- data/lib/active_agent/generation_provider/error_handling.rb +1 -1
- data/lib/active_agent/generation_provider/ollama_provider.rb +50 -1
- data/lib/active_agent/generation_provider/open_ai_provider.rb +88 -8
- data/lib/active_agent/version.rb +1 -1
- metadata +29 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 56c61e9a6a6173d572792952121413f72d49d9b9bcee932a350666d845236465
|
4
|
+
data.tar.gz: 86267a64dea6c8eef7bf890fe10fcb1ef1a2b2d09ddeab3f0a9d1d25bb6465fd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c173eac89c75335d474c533a41f3137ef2279c5aef1a90d60c93270383dd77c1e15b557c1e006e41c6f15f47f0fce12502c924fd939d58cac85978b97d749d2f
|
7
|
+
data.tar.gz: 6bd6d686a0a1a2613206f8b6d50d093a343e7913bd0dbdaac669abb95895a0306f114cc77e2cea01280981b5b5fff403d0edc6de1b1912cfc822ecc2935de961
|
data/README.md
CHANGED
@@ -2,9 +2,9 @@
|
|
2
2
|
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/user-attachments/assets/2bad263a-c09f-40b6-94ba-fff8e346d65d">
|
3
3
|
<img alt="activeagents_banner" src="https://github.com/user-attachments/assets/0ebbaa2f-c6bf-4d40-bb77-931015a14be3">
|
4
4
|
</picture>
|
5
|
-
*Build AI in Rails*
|
6
5
|
|
7
6
|
|
7
|
+
> *Build AI in Rails*
|
8
8
|
>
|
9
9
|
> *Now Agents are Controllers*
|
10
10
|
>
|
@@ -280,7 +280,7 @@ module ActiveAgent
|
|
280
280
|
def initialize # :nodoc:
|
281
281
|
super
|
282
282
|
@_prompt_was_called = false
|
283
|
-
@_context = ActiveAgent::ActionPrompt::Prompt.new(options: self.class.options || {}, agent_instance: self)
|
283
|
+
@_context = ActiveAgent::ActionPrompt::Prompt.new(options: self.class.options&.deep_dup || {}, agent_instance: self)
|
284
284
|
end
|
285
285
|
|
286
286
|
def process(method_name, *args) # :nodoc:
|
@@ -444,7 +444,10 @@ module ActiveAgent
|
|
444
444
|
:presence_penalty, :response_format, :seed, :stop, :tools_choice, :plugins,
|
445
445
|
|
446
446
|
# OpenRouter Provider Settings
|
447
|
-
:data_collection, :require_parameters, :only, :ignore, :quantizations, :sort, :max_price
|
447
|
+
:data_collection, :require_parameters, :only, :ignore, :quantizations, :sort, :max_price,
|
448
|
+
|
449
|
+
# Built-in Tools Support (OpenAI Responses API)
|
450
|
+
:tools
|
448
451
|
)
|
449
452
|
# Handle explicit options parameter
|
450
453
|
explicit_options = prompt_options[:options] || {}
|
@@ -7,6 +7,7 @@ module ActiveAgent
|
|
7
7
|
included do
|
8
8
|
include ActiveSupport::Callbacks
|
9
9
|
define_callbacks :generation, skip_after_callbacks_if_terminated: true
|
10
|
+
define_callbacks :embedding, skip_after_callbacks_if_terminated: true
|
10
11
|
end
|
11
12
|
|
12
13
|
module ClassMethods
|
@@ -18,6 +19,14 @@ module ActiveAgent
|
|
18
19
|
set_callback(:generation, callback, name, options)
|
19
20
|
end
|
20
21
|
end
|
22
|
+
|
23
|
+
# # Defines a callback that will get called right before/after/around the
|
24
|
+
# # embedding provider method.
|
25
|
+
define_method "#{callback}_embedding" do |*names, &blk|
|
26
|
+
_insert_callbacks(names, blk) do |name, options|
|
27
|
+
set_callback(:embedding, callback, name, options)
|
28
|
+
end
|
29
|
+
end
|
21
30
|
end
|
22
31
|
end
|
23
32
|
end
|
@@ -50,6 +50,18 @@ module ActiveAgent
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
+
def embed_now
|
54
|
+
processed_agent.handle_exceptions do
|
55
|
+
processed_agent.run_callbacks(:embedding) do
|
56
|
+
processed_agent.embed
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def embed_later(options = {})
|
62
|
+
enqueue_generation :embed_now, options
|
63
|
+
end
|
64
|
+
|
53
65
|
private
|
54
66
|
|
55
67
|
def processed_agent
|
@@ -4,7 +4,7 @@ begin
|
|
4
4
|
gem "ruby-anthropic", "~> 0.4.2"
|
5
5
|
require "anthropic"
|
6
6
|
rescue LoadError
|
7
|
-
raise LoadError, "The 'ruby-anthropic' gem is required for AnthropicProvider. Please add it to your Gemfile and run `bundle install`."
|
7
|
+
raise LoadError, "The 'ruby-anthropic ~> 0.4.2' gem is required for AnthropicProvider. Please add it to your Gemfile and run `bundle install`."
|
8
8
|
end
|
9
9
|
|
10
10
|
require "active_agent/action_prompt/action"
|
@@ -9,7 +9,56 @@ module ActiveAgent
|
|
9
9
|
@access_token ||= config["api_key"] || config["access_token"] || ENV["OLLAMA_API_KEY"] || ENV["OLLAMA_ACCESS_TOKEN"]
|
10
10
|
@model_name = config["model"]
|
11
11
|
@host = config["host"] || "http://localhost:11434"
|
12
|
-
@
|
12
|
+
@api_version = config["api_version"] || "v1"
|
13
|
+
@client = OpenAI::Client.new(uri_base: @host, access_token: @access_token, log_errors: Rails.env.development?, api_version: @api_version)
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def format_error_message(error)
|
19
|
+
# Check for various connection-related errors
|
20
|
+
connection_errors = [
|
21
|
+
Errno::ECONNREFUSED,
|
22
|
+
Errno::EADDRNOTAVAIL,
|
23
|
+
Errno::EHOSTUNREACH,
|
24
|
+
Net::OpenTimeout,
|
25
|
+
Net::ReadTimeout,
|
26
|
+
SocketError,
|
27
|
+
Faraday::ConnectionFailed
|
28
|
+
]
|
29
|
+
|
30
|
+
if connection_errors.any? { |klass| error.is_a?(klass) } ||
|
31
|
+
(error.message&.include?("Failed to open TCP connection") ||
|
32
|
+
error.message&.include?("Connection refused"))
|
33
|
+
"Unable to connect to Ollama at #{@host}. Please ensure Ollama is running on the configured host and port.\n" \
|
34
|
+
"You can start Ollama with: `ollama serve`\n" \
|
35
|
+
"Or update your configuration to point to the correct host."
|
36
|
+
else
|
37
|
+
super
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def embeddings_parameters(input: prompt.message.content, model: "nomic-embed-text")
|
42
|
+
{
|
43
|
+
model: @config["embedding_model"] || model,
|
44
|
+
input: input
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
def embeddings_response(response, request_params = nil)
|
49
|
+
# Ollama can return either format:
|
50
|
+
# 1. OpenAI-compatible: { "data": [{ "embedding": [...] }] }
|
51
|
+
# 2. Native Ollama: { "embedding": [...] }
|
52
|
+
embedding = response.dig("data", 0, "embedding") || response.dig("embedding")
|
53
|
+
|
54
|
+
message = ActiveAgent::ActionPrompt::Message.new(content: embedding, role: "assistant")
|
55
|
+
|
56
|
+
@response = ActiveAgent::GenerationProvider::Response.new(
|
57
|
+
prompt: prompt,
|
58
|
+
message: message,
|
59
|
+
raw_response: response,
|
60
|
+
raw_request: request_params
|
61
|
+
)
|
13
62
|
end
|
14
63
|
end
|
15
64
|
end
|
@@ -2,7 +2,7 @@ begin
|
|
2
2
|
gem "ruby-openai", ">= 8.1.0"
|
3
3
|
require "openai"
|
4
4
|
rescue LoadError
|
5
|
-
raise LoadError, "The 'ruby-openai' gem is required for OpenAIProvider. Please add it to your Gemfile and run `bundle install`."
|
5
|
+
raise LoadError, "The 'ruby-openai >= 8.1.0' gem is required for OpenAIProvider. Please add it to your Gemfile and run `bundle install`."
|
6
6
|
end
|
7
7
|
|
8
8
|
require "active_agent/action_prompt/action"
|
@@ -31,7 +31,7 @@ module ActiveAgent
|
|
31
31
|
organization_id: @organization_id,
|
32
32
|
admin_token: @admin_token,
|
33
33
|
log_errors: Rails.env.development?
|
34
|
-
|
34
|
+
)
|
35
35
|
|
36
36
|
@model_name = config["model"] || "gpt-4o-mini"
|
37
37
|
end
|
@@ -92,10 +92,39 @@ module ActiveAgent
|
|
92
92
|
|
93
93
|
private
|
94
94
|
|
95
|
-
#
|
96
|
-
|
97
|
-
|
98
|
-
|
95
|
+
# Override from ParameterBuilder to add web_search_options for Chat API
|
96
|
+
def build_provider_parameters
|
97
|
+
params = {}
|
98
|
+
|
99
|
+
# Check if we're using a model that supports web_search_options in Chat API
|
100
|
+
if chat_api_web_search_model? && @prompt.options[:web_search]
|
101
|
+
params[:web_search_options] = build_web_search_options(@prompt.options[:web_search])
|
102
|
+
end
|
103
|
+
|
104
|
+
params
|
105
|
+
end
|
106
|
+
|
107
|
+
def chat_api_web_search_model?
|
108
|
+
model = @prompt.options[:model] || @model_name
|
109
|
+
[ "gpt-4o-search-preview", "gpt-4o-mini-search-preview" ].include?(model)
|
110
|
+
end
|
111
|
+
|
112
|
+
def build_web_search_options(web_search_config)
|
113
|
+
options = {}
|
114
|
+
|
115
|
+
if web_search_config.is_a?(Hash)
|
116
|
+
options[:search_context_size] = web_search_config[:search_context_size] if web_search_config[:search_context_size]
|
117
|
+
|
118
|
+
if web_search_config[:user_location]
|
119
|
+
options[:user_location] = {
|
120
|
+
type: "approximate",
|
121
|
+
approximate: web_search_config[:user_location]
|
122
|
+
}
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
options
|
127
|
+
end
|
99
128
|
|
100
129
|
def chat_response(response, request_params = nil)
|
101
130
|
return @response if prompt.options[:stream]
|
@@ -123,7 +152,7 @@ module ActiveAgent
|
|
123
152
|
role: message_json["role"].intern,
|
124
153
|
action_requested: message_json["finish_reason"] == "tool_calls",
|
125
154
|
raw_actions: message_json["tool_calls"] || [],
|
126
|
-
content_type: prompt.output_schema.present? ? "application/json" : "text/plain"
|
155
|
+
content_type: prompt.output_schema.present? ? "application/json" : "text/plain"
|
127
156
|
)
|
128
157
|
|
129
158
|
@response = ActiveAgent::GenerationProvider::Response.new(
|
@@ -161,14 +190,65 @@ module ActiveAgent
|
|
161
190
|
end
|
162
191
|
|
163
192
|
def responses_parameters(model: @prompt.options[:model] || @model_name, messages: @prompt.messages, temperature: @prompt.options[:temperature] || @config["temperature"] || 0.7, tools: @prompt.actions, structured_output: @prompt.output_schema)
|
193
|
+
# Build tools array, combining action tools with built-in tools
|
194
|
+
tools_array = build_tools_for_responses(tools)
|
195
|
+
|
164
196
|
{
|
165
197
|
model: model,
|
166
198
|
input: ActiveAgent::GenerationProvider::ResponsesAdapter.new(@prompt).input,
|
167
|
-
tools:
|
199
|
+
tools: tools_array.presence,
|
168
200
|
text: structured_output
|
169
201
|
}.compact
|
170
202
|
end
|
171
203
|
|
204
|
+
def build_tools_for_responses(action_tools)
|
205
|
+
tools = []
|
206
|
+
|
207
|
+
# Start with action tools (user-defined functions) if any
|
208
|
+
tools.concat(action_tools) if action_tools.present?
|
209
|
+
|
210
|
+
# Add built-in tools if specified in options[:tools]
|
211
|
+
if @prompt.options[:tools].present?
|
212
|
+
built_in_tools = @prompt.options[:tools]
|
213
|
+
built_in_tools = [ built_in_tools ] unless built_in_tools.is_a?(Array)
|
214
|
+
|
215
|
+
built_in_tools.each do |tool|
|
216
|
+
next unless tool.is_a?(Hash)
|
217
|
+
|
218
|
+
case tool[:type]
|
219
|
+
when "web_search_preview", "web_search"
|
220
|
+
web_search_tool = { type: "web_search_preview" }
|
221
|
+
web_search_tool[:search_context_size] = tool[:search_context_size] if tool[:search_context_size]
|
222
|
+
web_search_tool[:user_location] = tool[:user_location] if tool[:user_location]
|
223
|
+
tools << web_search_tool
|
224
|
+
|
225
|
+
when "image_generation"
|
226
|
+
image_gen_tool = { type: "image_generation" }
|
227
|
+
image_gen_tool[:size] = tool[:size] if tool[:size]
|
228
|
+
image_gen_tool[:quality] = tool[:quality] if tool[:quality]
|
229
|
+
image_gen_tool[:format] = tool[:format] if tool[:format]
|
230
|
+
image_gen_tool[:compression] = tool[:compression] if tool[:compression]
|
231
|
+
image_gen_tool[:background] = tool[:background] if tool[:background]
|
232
|
+
image_gen_tool[:partial_images] = tool[:partial_images] if tool[:partial_images]
|
233
|
+
tools << image_gen_tool
|
234
|
+
|
235
|
+
when "mcp"
|
236
|
+
mcp_tool = { type: "mcp" }
|
237
|
+
mcp_tool[:server_label] = tool[:server_label] if tool[:server_label]
|
238
|
+
mcp_tool[:server_description] = tool[:server_description] if tool[:server_description]
|
239
|
+
mcp_tool[:server_url] = tool[:server_url] if tool[:server_url]
|
240
|
+
mcp_tool[:connector_id] = tool[:connector_id] if tool[:connector_id]
|
241
|
+
mcp_tool[:authorization] = tool[:authorization] if tool[:authorization]
|
242
|
+
mcp_tool[:require_approval] = tool[:require_approval] if tool[:require_approval]
|
243
|
+
mcp_tool[:allowed_tools] = tool[:allowed_tools] if tool[:allowed_tools]
|
244
|
+
tools << mcp_tool
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
tools
|
250
|
+
end
|
251
|
+
|
172
252
|
def embeddings_parameters(input: prompt.message.content, model: "text-embedding-3-large")
|
173
253
|
{
|
174
254
|
model: model,
|
data/lib/active_agent/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activeagent
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Bowen
|
@@ -249,6 +249,34 @@ dependencies:
|
|
249
249
|
- - ">="
|
250
250
|
- !ruby/object:Gem::Version
|
251
251
|
version: '0'
|
252
|
+
- !ruby/object:Gem::Dependency
|
253
|
+
name: cuprite
|
254
|
+
requirement: !ruby/object:Gem::Requirement
|
255
|
+
requirements:
|
256
|
+
- - "~>"
|
257
|
+
- !ruby/object:Gem::Version
|
258
|
+
version: '0.15'
|
259
|
+
type: :development
|
260
|
+
prerelease: false
|
261
|
+
version_requirements: !ruby/object:Gem::Requirement
|
262
|
+
requirements:
|
263
|
+
- - "~>"
|
264
|
+
- !ruby/object:Gem::Version
|
265
|
+
version: '0.15'
|
266
|
+
- !ruby/object:Gem::Dependency
|
267
|
+
name: capybara
|
268
|
+
requirement: !ruby/object:Gem::Requirement
|
269
|
+
requirements:
|
270
|
+
- - "~>"
|
271
|
+
- !ruby/object:Gem::Version
|
272
|
+
version: '3.40'
|
273
|
+
type: :development
|
274
|
+
prerelease: false
|
275
|
+
version_requirements: !ruby/object:Gem::Requirement
|
276
|
+
requirements:
|
277
|
+
- - "~>"
|
278
|
+
- !ruby/object:Gem::Version
|
279
|
+
version: '3.40'
|
252
280
|
description: The only agent-oriented AI framework designed for Rails, where Agents
|
253
281
|
are Controllers. Build AI features with less complexity using the MVC conventions
|
254
282
|
you love.
|