activeagent 1.0.2 → 1.0.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/lib/active_agent/concerns/view.rb +1 -1
- data/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent.rb +1 -1
- data/lib/active_agent/providers/anthropic/request.rb +3 -1
- data/lib/active_agent/providers/anthropic/transforms.rb +88 -0
- data/lib/active_agent/providers/bedrock_provider.rb +2 -2
- data/lib/active_agent/providers/mock/request.rb +1 -4
- data/lib/active_agent/providers/open_ai/chat_provider.rb +3 -3
- data/lib/active_agent/providers/requesty/_types.rb +16 -0
- data/lib/active_agent/providers/requesty/options.rb +39 -0
- data/lib/active_agent/providers/requesty_provider.rb +63 -0
- data/lib/active_agent/version.rb +1 -1
- metadata +23 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 90c3dbcc6a09014256fc36ac0bf88de83aa6a85d95c688be1eb289dde397c633
|
|
4
|
+
data.tar.gz: 6bdf588db4ab2809efaddde5ee7b06fd58ad59e1b5b3479e6c653828d90a4ad9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a70e79c1a1b0f683ac3322476b9f2cdaa70deaf3f8e035d2d5307e8fb6561ea5cc5b2589edfa1b2e3a68e28a2db0806d591d1f6726cbd6ab39ed512a072388db
|
|
7
|
+
data.tar.gz: a94dd4a891041044b02117ddc2bcf448cbf1c31c764fbaf8e3cd88b0b81cddf636bb90cac2ddfcb4706ec91b46da2490fdb24fd1e967d9aa200e95ca655a8caf
|
|
@@ -142,7 +142,7 @@ module ActiveAgent
|
|
|
142
142
|
|
|
143
143
|
return if !strict && !lookup_context.exists?(template_name, _prefixes, false, [], **options)
|
|
144
144
|
|
|
145
|
-
template = lookup_context.
|
|
145
|
+
template = lookup_context.find(template_name, _prefixes, false, [], **options)
|
|
146
146
|
|
|
147
147
|
render_to_string(template: template.virtual_path, locals:, layout: false).chomp.presence
|
|
148
148
|
end
|
|
@@ -71,7 +71,7 @@ module ActiveAgent
|
|
|
71
71
|
].freeze
|
|
72
72
|
|
|
73
73
|
# Available providers
|
|
74
|
-
PROVIDERS = %w[openai anthropic ollama openrouter].freeze
|
|
74
|
+
PROVIDERS = %w[openai anthropic ollama openrouter requesty].freeze
|
|
75
75
|
|
|
76
76
|
# Returns the configuration as a hash for versioning
|
|
77
77
|
def configuration_snapshot
|
|
@@ -71,7 +71,9 @@ module ActiveAgent
|
|
|
71
71
|
# @raise [ArgumentError] when gem model validation fails
|
|
72
72
|
def initialize(**params)
|
|
73
73
|
# Step 1: Extract custom fields that gem doesn't support
|
|
74
|
-
|
|
74
|
+
# Read response_format without deleting - normalize_params will delete and convert it
|
|
75
|
+
# to output_config for json_schema, or drop it for other types.
|
|
76
|
+
@response_format = params[:response_format]
|
|
75
77
|
@stream = params.delete(:stream)
|
|
76
78
|
anthropic_beta = params.delete(:anthropic_beta)
|
|
77
79
|
|
|
@@ -31,6 +31,12 @@ module ActiveAgent
|
|
|
31
31
|
params[:tools] = normalize_tools(params[:tools]) if params[:tools]
|
|
32
32
|
params[:tool_choice] = normalize_tool_choice(params[:tool_choice]) if params[:tool_choice]
|
|
33
33
|
|
|
34
|
+
# Normalize json_schema response_format → output_config (Anthropic's native structured output field)
|
|
35
|
+
if params[:response_format]
|
|
36
|
+
output_config = normalize_response_format(params.delete(:response_format))
|
|
37
|
+
params[:output_config] = output_config if output_config
|
|
38
|
+
end
|
|
39
|
+
|
|
34
40
|
# Handle mcps parameter (common format) -> transforms to mcp_servers (provider format)
|
|
35
41
|
if params[:mcps]
|
|
36
42
|
params[:mcp_servers] = normalize_mcp_servers(params.delete(:mcps))
|
|
@@ -159,6 +165,87 @@ module ActiveAgent
|
|
|
159
165
|
end
|
|
160
166
|
end
|
|
161
167
|
|
|
168
|
+
# Normalizes response_format to Anthropic output_config structure.
|
|
169
|
+
#
|
|
170
|
+
# Supported (ActiveAgent common format):
|
|
171
|
+
# { type: "json_schema", json_schema: { name: "...", schema: {...}, strict: true } }
|
|
172
|
+
# → { format: { type: "json_schema", schema: {...} } }
|
|
173
|
+
#
|
|
174
|
+
# Notes:
|
|
175
|
+
# - Anthropic requires `additionalProperties: false` on all object schemas.
|
|
176
|
+
# This is auto-injected into any object schemas that don't have it set.
|
|
177
|
+
# - Anthropic does not use OpenAI's `name` or `strict` fields in output_config.format.
|
|
178
|
+
# - json_object is not handled here; it remains prompt-emulated.
|
|
179
|
+
# - text is not handled here; Anthropic returns plain text by default.
|
|
180
|
+
#
|
|
181
|
+
# @param format [Hash, Symbol, String] ActiveAgent common response_format
|
|
182
|
+
# @return [Hash] Anthropic output_config hash, or nil if not applicable
|
|
183
|
+
def normalize_response_format(format)
|
|
184
|
+
case format
|
|
185
|
+
when Hash
|
|
186
|
+
format_hash = format.deep_symbolize_keys
|
|
187
|
+
|
|
188
|
+
if format_hash[:type].to_s == "json_schema"
|
|
189
|
+
schema = format_hash[:json_schema]&.dig(:schema)
|
|
190
|
+
if schema
|
|
191
|
+
schema = inject_additional_properties(schema.deep_dup)
|
|
192
|
+
end
|
|
193
|
+
{
|
|
194
|
+
format: {
|
|
195
|
+
type: "json_schema",
|
|
196
|
+
schema: schema
|
|
197
|
+
}.compact
|
|
198
|
+
}
|
|
199
|
+
elsif format_hash[:type].to_s == "json_object"
|
|
200
|
+
# json_object is not handled here; it remains prompt-emulated.
|
|
201
|
+
nil
|
|
202
|
+
elsif format_hash[:type].to_s == "text"
|
|
203
|
+
# text is not handled here; it remains prompt-emulated.
|
|
204
|
+
nil
|
|
205
|
+
else
|
|
206
|
+
# Pass through (already properly structured or Anthropic native format)
|
|
207
|
+
format_hash
|
|
208
|
+
end
|
|
209
|
+
when Symbol, String
|
|
210
|
+
# Bare :json_schema without a schema cannot use native output_config.
|
|
211
|
+
# Anthropic requires a valid JSON Schema — return nil to fall back.
|
|
212
|
+
nil
|
|
213
|
+
else
|
|
214
|
+
format
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# Recursively injects `additionalProperties: false` into all object schemas.
|
|
219
|
+
#
|
|
220
|
+
# Anthropic's structured output API requires this field on all object types.
|
|
221
|
+
# Only inserts when not already explicitly set by the user.
|
|
222
|
+
#
|
|
223
|
+
# @param schema [Hash] JSON Schema hash
|
|
224
|
+
# @return [Hash] schema with additionalProperties injected
|
|
225
|
+
def inject_additional_properties(schema)
|
|
226
|
+
return schema unless schema.is_a?(Hash)
|
|
227
|
+
|
|
228
|
+
if schema[:type] == "object" && !schema.key?(:additionalProperties)
|
|
229
|
+
schema[:additionalProperties] = false
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Recurse into nested schemas
|
|
233
|
+
schema[:properties]&.each_value { |prop| inject_additional_properties(prop) }
|
|
234
|
+
schema[:items]&.then { |items| inject_additional_properties(items) }
|
|
235
|
+
schema[:anyOf]&.each { |s| inject_additional_properties(s) }
|
|
236
|
+
schema[:oneOf]&.each { |s| inject_additional_properties(s) }
|
|
237
|
+
schema[:allOf]&.each { |s| inject_additional_properties(s) }
|
|
238
|
+
|
|
239
|
+
if schema[:definitions]
|
|
240
|
+
schema[:definitions].each_value { |defn| inject_additional_properties(defn) }
|
|
241
|
+
end
|
|
242
|
+
if schema[:"$defs"]
|
|
243
|
+
schema[:"$defs"].each_value { |defn| inject_additional_properties(defn) }
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
schema
|
|
247
|
+
end
|
|
248
|
+
|
|
162
249
|
# Merges consecutive same-role messages into single messages with multiple content blocks.
|
|
163
250
|
#
|
|
164
251
|
# Required by Anthropic API - consecutive messages with the same role must be combined.
|
|
@@ -437,6 +524,7 @@ module ActiveAgent
|
|
|
437
524
|
msg.delete(:model)
|
|
438
525
|
msg.delete(:stop_reason)
|
|
439
526
|
msg.delete(:stop_sequence)
|
|
527
|
+
msg.delete(:stop_details)
|
|
440
528
|
msg.delete(:type)
|
|
441
529
|
msg.delete(:usage)
|
|
442
530
|
end
|
|
@@ -15,11 +15,11 @@ module ActiveAgent
|
|
|
15
15
|
# bedrock:
|
|
16
16
|
# service: "Bedrock"
|
|
17
17
|
# aws_region: "eu-west-2"
|
|
18
|
-
# model: "eu.anthropic.claude-sonnet-
|
|
18
|
+
# model: "eu.anthropic.claude-sonnet-5-v1:0"
|
|
19
19
|
#
|
|
20
20
|
# @example Agent usage
|
|
21
21
|
# class SummaryAgent < ApplicationAgent
|
|
22
|
-
# generate_with :bedrock, model: "eu.anthropic.claude-sonnet-
|
|
22
|
+
# generate_with :bedrock, model: "eu.anthropic.claude-sonnet-5-v1:0"
|
|
23
23
|
#
|
|
24
24
|
# def summarize
|
|
25
25
|
# prompt(message: params[:message])
|
|
@@ -22,16 +22,13 @@ module ActiveAgent
|
|
|
22
22
|
attribute :stream, :boolean, default: false
|
|
23
23
|
attribute :tools # Array of tool definitions
|
|
24
24
|
attribute :tool_choice # Tool choice configuration
|
|
25
|
+
attribute :response_format
|
|
25
26
|
|
|
26
27
|
# Common Format Compatibility
|
|
27
28
|
def message=(value)
|
|
28
29
|
self.messages ||= []
|
|
29
30
|
self.messages << value
|
|
30
31
|
end
|
|
31
|
-
|
|
32
|
-
def response_format
|
|
33
|
-
{ type: "text" }
|
|
34
|
-
end
|
|
35
32
|
end
|
|
36
33
|
end
|
|
37
34
|
end
|
|
@@ -104,7 +104,7 @@ module ActiveAgent
|
|
|
104
104
|
|
|
105
105
|
# If we have a delta, we need to update a message in the stack
|
|
106
106
|
message = find_or_create_message(api_message.index)
|
|
107
|
-
|
|
107
|
+
message_merge_delta(message, api_message.delta.deep_to_h)
|
|
108
108
|
|
|
109
109
|
# Stream back content changes as they come in
|
|
110
110
|
if api_message.delta.content
|
|
@@ -140,7 +140,7 @@ module ActiveAgent
|
|
|
140
140
|
def process_function_calls(api_function_calls)
|
|
141
141
|
api_function_calls.each do |api_function_call|
|
|
142
142
|
content = instrument("tool_call.active_agent", tool_name: api_function_call.dig(:function, :name)) do
|
|
143
|
-
case api_function_call[:type]
|
|
143
|
+
case api_function_call[:type].to_s
|
|
144
144
|
when "function"
|
|
145
145
|
process_tool_call_function(api_function_call[:function])
|
|
146
146
|
else
|
|
@@ -245,7 +245,7 @@ module ActiveAgent
|
|
|
245
245
|
end
|
|
246
246
|
end
|
|
247
247
|
when String
|
|
248
|
-
hash[key] += value
|
|
248
|
+
hash[key] += value.to_s
|
|
249
249
|
else
|
|
250
250
|
hash[key] = value
|
|
251
251
|
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../open_ai/chat/_types"
|
|
4
|
+
require_relative "options"
|
|
5
|
+
|
|
6
|
+
module ActiveAgent
|
|
7
|
+
module Providers
|
|
8
|
+
module Requesty
|
|
9
|
+
# ActiveModel type for casting and serializing Requesty requests.
|
|
10
|
+
#
|
|
11
|
+
# Requesty is OpenAI-compatible, so requests use the same shape as the
|
|
12
|
+
# OpenAI Chat API. This delegates entirely to OpenAI::Chat::RequestType.
|
|
13
|
+
RequestType = ActiveAgent::Providers::OpenAI::Chat::RequestType
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../open_ai/options"
|
|
4
|
+
|
|
5
|
+
module ActiveAgent
|
|
6
|
+
module Providers
|
|
7
|
+
module Requesty
|
|
8
|
+
# Configuration options for the Requesty provider.
|
|
9
|
+
#
|
|
10
|
+
# Extends OpenAI::Options, overriding the base URL to point at Requesty's
|
|
11
|
+
# OpenAI-compatible gateway and resolving the API key from REQUESTY_API_KEY.
|
|
12
|
+
# Requesty does not use organization or project identifiers.
|
|
13
|
+
#
|
|
14
|
+
# @example Basic configuration
|
|
15
|
+
# options = Options.new(api_key: ENV["REQUESTY_API_KEY"])
|
|
16
|
+
#
|
|
17
|
+
# @see https://docs.requesty.ai
|
|
18
|
+
# @see https://app.requesty.ai/api-keys Requesty API Keys
|
|
19
|
+
class Options < ActiveAgent::Providers::OpenAI::Options
|
|
20
|
+
# @!attribute base_url
|
|
21
|
+
# @return [String] API endpoint (default: "https://router.requesty.ai/v1")
|
|
22
|
+
attribute :base_url, :string, as: "https://router.requesty.ai/v1"
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def resolve_api_key(kwargs)
|
|
27
|
+
kwargs[:api_key] ||
|
|
28
|
+
kwargs[:access_token] ||
|
|
29
|
+
ENV["REQUESTY_API_KEY"] ||
|
|
30
|
+
ENV["REQUESTY_ACCESS_TOKEN"]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Not used as part of Requesty
|
|
34
|
+
def resolve_organization_id(kwargs) = nil
|
|
35
|
+
def resolve_project_id(kwargs) = nil
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require_relative "_base_provider"
|
|
2
|
+
|
|
3
|
+
require_gem!(:openai, __FILE__)
|
|
4
|
+
|
|
5
|
+
require_relative "open_ai_provider"
|
|
6
|
+
require_relative "requesty/_types"
|
|
7
|
+
|
|
8
|
+
module ActiveAgent
|
|
9
|
+
module Providers
|
|
10
|
+
# Provides access to Requesty's OpenAI-compatible LLM gateway.
|
|
11
|
+
#
|
|
12
|
+
# Extends the OpenAI provider to work with Requesty's OpenAI-compatible API,
|
|
13
|
+
# enabling access to multiple AI models through a single interface using the
|
|
14
|
+
# +provider/model+ naming convention (e.g. +openai/gpt-4o-mini+).
|
|
15
|
+
#
|
|
16
|
+
# Requesty is a plain OpenAI-compatible gateway: requests, responses and
|
|
17
|
+
# transforms are identical to the OpenAI Chat API, so this provider reuses
|
|
18
|
+
# OpenAI::Chat::RequestType and OpenAI::Chat::Transforms directly. The only
|
|
19
|
+
# Requesty-specific configuration is the base URL and API key, which live in
|
|
20
|
+
# Requesty::Options.
|
|
21
|
+
#
|
|
22
|
+
# @example Configuration in active_agent.yml
|
|
23
|
+
# requesty:
|
|
24
|
+
# service: "Requesty"
|
|
25
|
+
# api_key: <%= ENV["REQUESTY_API_KEY"] %>
|
|
26
|
+
# model: "openai/gpt-4o-mini"
|
|
27
|
+
#
|
|
28
|
+
# @see OpenAI::ChatProvider
|
|
29
|
+
# @see https://docs.requesty.ai
|
|
30
|
+
class RequestyProvider < OpenAI::ChatProvider
|
|
31
|
+
# @return [String]
|
|
32
|
+
def self.service_name
|
|
33
|
+
"Requesty"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @return [Class]
|
|
37
|
+
def self.options_klass
|
|
38
|
+
Requesty::Options
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @return [ActiveModel::Type::Value]
|
|
42
|
+
def self.prompt_request_type
|
|
43
|
+
OpenAI::Chat::RequestType.new
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# @return [ActiveModel::Type::Value]
|
|
47
|
+
def self.embed_request_type
|
|
48
|
+
OpenAI::Embedding::RequestType.new
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
protected
|
|
52
|
+
|
|
53
|
+
# @see BaseProvider#api_response_normalize
|
|
54
|
+
# @param api_response [OpenAI::Models::ChatCompletion]
|
|
55
|
+
# @return [Hash] normalized response hash
|
|
56
|
+
def api_response_normalize(api_response)
|
|
57
|
+
return api_response unless api_response
|
|
58
|
+
|
|
59
|
+
OpenAI::Chat::Transforms.gem_to_hash(api_response)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
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: 1.0.
|
|
4
|
+
version: 1.0.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Justin Bowen
|
|
@@ -269,16 +269,30 @@ dependencies:
|
|
|
269
269
|
requirements:
|
|
270
270
|
- - ">="
|
|
271
271
|
- !ruby/object:Gem::Version
|
|
272
|
-
version: '
|
|
272
|
+
version: '6.4'
|
|
273
273
|
type: :development
|
|
274
274
|
prerelease: false
|
|
275
275
|
version_requirements: !ruby/object:Gem::Requirement
|
|
276
276
|
requirements:
|
|
277
277
|
- - ">="
|
|
278
278
|
- !ruby/object:Gem::Version
|
|
279
|
-
version: '
|
|
279
|
+
version: '6.4'
|
|
280
280
|
- !ruby/object:Gem::Dependency
|
|
281
281
|
name: webmock
|
|
282
|
+
requirement: !ruby/object:Gem::Requirement
|
|
283
|
+
requirements:
|
|
284
|
+
- - ">="
|
|
285
|
+
- !ruby/object:Gem::Version
|
|
286
|
+
version: '3.26'
|
|
287
|
+
type: :development
|
|
288
|
+
prerelease: false
|
|
289
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
290
|
+
requirements:
|
|
291
|
+
- - ">="
|
|
292
|
+
- !ruby/object:Gem::Version
|
|
293
|
+
version: '3.26'
|
|
294
|
+
- !ruby/object:Gem::Dependency
|
|
295
|
+
name: cgi
|
|
282
296
|
requirement: !ruby/object:Gem::Requirement
|
|
283
297
|
requirements:
|
|
284
298
|
- - ">="
|
|
@@ -297,14 +311,14 @@ dependencies:
|
|
|
297
311
|
requirements:
|
|
298
312
|
- - ">="
|
|
299
313
|
- !ruby/object:Gem::Version
|
|
300
|
-
version: '
|
|
314
|
+
version: '3.2'
|
|
301
315
|
type: :development
|
|
302
316
|
prerelease: false
|
|
303
317
|
version_requirements: !ruby/object:Gem::Requirement
|
|
304
318
|
requirements:
|
|
305
319
|
- - ">="
|
|
306
320
|
- !ruby/object:Gem::Version
|
|
307
|
-
version: '
|
|
321
|
+
version: '3.2'
|
|
308
322
|
- !ruby/object:Gem::Dependency
|
|
309
323
|
name: pry
|
|
310
324
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -497,6 +511,9 @@ files:
|
|
|
497
511
|
- lib/active_agent/providers/open_router_provider.rb
|
|
498
512
|
- lib/active_agent/providers/openai_provider.rb
|
|
499
513
|
- lib/active_agent/providers/openrouter_provider.rb
|
|
514
|
+
- lib/active_agent/providers/requesty/_types.rb
|
|
515
|
+
- lib/active_agent/providers/requesty/options.rb
|
|
516
|
+
- lib/active_agent/providers/requesty_provider.rb
|
|
500
517
|
- lib/active_agent/providers/ruby_llm/_types.rb
|
|
501
518
|
- lib/active_agent/providers/ruby_llm/embedding_request.rb
|
|
502
519
|
- lib/active_agent/providers/ruby_llm/messages/_types.rb
|
|
@@ -576,7 +593,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
576
593
|
- !ruby/object:Gem::Version
|
|
577
594
|
version: '0'
|
|
578
595
|
requirements: []
|
|
579
|
-
rubygems_version:
|
|
596
|
+
rubygems_version: 4.0.6
|
|
580
597
|
specification_version: 4
|
|
581
598
|
summary: Rails AI Agents Framework
|
|
582
599
|
test_files: []
|