activeagent 0.6.0rc1 → 0.6.0rc2

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: 01b5ef5633bece3cdce9d9dba58e499b48c57d80c3a461ceda58adbff738e36a
4
- data.tar.gz: 9cc9065b10e9bbf0f8fdeba420fe7b9518b7e97f2d32e3bf11af55babd697009
3
+ metadata.gz: a9496edf8536e904969a006a14d65bd69c3257f4db22d80e859a977646a02513
4
+ data.tar.gz: 0b35eb065fe146ee027d72789826ea3af793e57140d8aefead79d73586548e6e
5
5
  SHA512:
6
- metadata.gz: 6849b7f94803261d8049ed092aaa8176003b85d5353202aa1a96160860bb07dd98fc9171308c5ed4ea3c9644b6aa68cf09c9c5c827ec7a45300eb14b44e9f1e0
7
- data.tar.gz: db67a92dd9f9344fd50234caae1641bb70d57ca61200bbea98a177a175c791de886ee3ac42bc807e0e824d95c94adb6e7765908fec3f50cfc4f73aeb66e21c4f
6
+ metadata.gz: 65ce7639ce9106b99e827600b7582776413cdeb69b7de28cf7ed80e0e496ebc1be6ea613ab9f67e944bb7837344458d3709a2065eb2725fc9b7263c08146f08d
7
+ data.tar.gz: c702c8618b701979dee4871ab200e7fd60d5c09fc2670926a9f77e1c0975ab14e2a5e9b30df2eccce9276440f5e6baf396d16320aabff67a1fbbd332a88b5379
@@ -104,7 +104,6 @@ module ActiveAgent
104
104
  # Define how the agent should generate content
105
105
  def generate_with(provider, **options)
106
106
  self.generation_provider = provider
107
-
108
107
  if options.has_key?(:instructions) || (self.options || {}).empty?
109
108
  # Either instructions explicitly provided, or no inherited options exist
110
109
  self.options = (self.options || {}).merge(options)
@@ -245,11 +244,13 @@ module ActiveAgent
245
244
  current_context = context.clone
246
245
  # Merge action params with original params to preserve context
247
246
  original_params = current_context.params || {}
247
+
248
248
  if action.params.is_a?(Hash)
249
249
  self.params = original_params.merge(action.params)
250
250
  else
251
251
  self.params = original_params
252
252
  end
253
+
253
254
  process(action.name)
254
255
  context.message.role = :tool
255
256
  context.message.action_id = action.id
@@ -373,6 +374,10 @@ module ActiveAgent
373
374
  if headers[:message].present? && headers[:message].is_a?(ActiveAgent::ActionPrompt::Message)
374
375
  headers[:body] = headers[:message].content
375
376
  headers[:role] = headers[:message].role
377
+ elsif headers[:message].present? && headers[:message].is_a?(Array)
378
+ # Handle array of multipart content like [{type: "text", text: "..."}, {type: "file", file: {...}}]
379
+ headers[:body] = headers[:message]
380
+ headers[:role] = :user
376
381
  elsif headers[:message].present? && headers[:message].is_a?(String)
377
382
  headers[:body] = headers[:message]
378
383
  headers[:role] = :user
@@ -394,7 +399,6 @@ module ActiveAgent
394
399
  ActiveAgent::ActionPrompt::Message.new(content: headers[:body], content_type: "input_text")
395
400
  ]
396
401
  end
397
-
398
402
  headers
399
403
  end
400
404
 
@@ -421,7 +425,7 @@ module ActiveAgent
421
425
  # Extract runtime options from prompt_options (exclude instructions as it has special template logic)
422
426
  runtime_options = prompt_options.slice(
423
427
  :model, :temperature, :max_tokens, :stream, :top_p, :frequency_penalty,
424
- :presence_penalty, :response_format, :seed, :stop, :tools_choice, :data_collection
428
+ :presence_penalty, :response_format, :seed, :stop, :tools_choice, :data_collection, :plugins
425
429
  )
426
430
  # Handle explicit options parameter
427
431
  explicit_options = prompt_options[:options] || {}
@@ -27,7 +27,7 @@ module ActiveAgent
27
27
  @metadata = attributes[:metadata] || {}
28
28
  @charset = attributes[:charset] || "UTF-8"
29
29
  @content = attributes[:content] || ""
30
- @content_type = attributes[:content_type] || "text/plain"
30
+ @content_type = detect_content_type(attributes)
31
31
  @role = attributes[:role] || :user
32
32
  @raw_actions = attributes[:raw_actions]
33
33
  @requested_actions = attributes[:requested_actions] || []
@@ -85,6 +85,22 @@ module ActiveAgent
85
85
 
86
86
  private
87
87
 
88
+ def detect_content_type(attributes)
89
+ # If content_type is explicitly provided, use it
90
+ return attributes[:content_type] if attributes[:content_type]
91
+
92
+ # If content is an array with multipart/mixed content, set appropriate type
93
+ if attributes[:content].is_a?(Array)
94
+ # Check if it contains multimodal content (text, image_url, file, etc.)
95
+ has_multimodal = attributes[:content].any? do |item|
96
+ item.is_a?(Hash) && (item[:type] || item["type"])
97
+ end
98
+ has_multimodal ? "multipart/mixed" : "array"
99
+ else
100
+ "text/plain"
101
+ end
102
+ end
103
+
88
104
  def validate_role
89
105
  unless VALID_ROLES.include?(role.to_s)
90
106
  raise ArgumentError, "Invalid role: #{role}. Valid roles are: #{VALID_ROLES.join(", ")}"
@@ -34,7 +34,7 @@ module ActiveAgent
34
34
  end
35
35
 
36
36
  def multimodal?
37
- @multimodal ||= @message&.content.is_a?(Array) || @messages.any? { |m| m.content.is_a?(Array) }
37
+ @multimodal ||= @message&.content.is_a?(Array) || @messages.any? { |m| m&.content.is_a?(Array) }
38
38
  end
39
39
 
40
40
  def messages=(messages)
@@ -10,6 +10,7 @@ module ActiveAgent
10
10
  include ParameterBuilder
11
11
 
12
12
  class GenerationProviderError < StandardError; end
13
+
13
14
  attr_reader :client, :config, :prompt, :response, :access_token, :model_name
14
15
 
15
16
  def initialize(config)
@@ -46,7 +46,6 @@ module ActiveAgent
46
46
 
47
47
  def handle_generation_error(error)
48
48
  error_message = format_error_message(error)
49
-
50
49
  # Create new error with original backtrace preserved
51
50
  new_error = ActiveAgent::GenerationProvider::Base::GenerationProviderError.new(error_message)
52
51
  new_error.set_backtrace(error.backtrace) if error.respond_to?(:backtrace)
@@ -61,7 +60,9 @@ module ActiveAgent
61
60
  end
62
61
 
63
62
  def format_error_message(error)
64
- message = if error.respond_to?(:message)
63
+ message = if error.respond_to?(:response)
64
+ error.response[:body]
65
+ elsif error.respond_to?(:message)
65
66
  error.message
66
67
  elsif error.respond_to?(:to_s)
67
68
  error.to_s
@@ -25,7 +25,13 @@ module ActiveAgent
25
25
  @access_token ||= config["api_key"] || config["access_token"] || OpenAI.configuration.access_token || ENV["OPENAI_ACCESS_TOKEN"]
26
26
  @organization_id = config["organization_id"] || OpenAI.configuration.organization_id || ENV["OPENAI_ORGANIZATION_ID"]
27
27
  @admin_token = config["admin_token"] || OpenAI.configuration.admin_token || ENV["OPENAI_ADMIN_TOKEN"]
28
- @client = OpenAI::Client.new(access_token: @access_token, uri_base: @host, organization_id: @organization_id)
28
+ @client = OpenAI::Client.new(
29
+ access_token: @access_token,
30
+ uri_base: @host,
31
+ organization_id: @organization_id,
32
+ admin_token: @admin_token,
33
+ log_errors: Rails.env.development?
34
+ )
29
35
 
30
36
  @model_name = config["model"] || "gpt-4o-mini"
31
37
  end
@@ -4,31 +4,6 @@ require_relative "open_ai_provider"
4
4
  module ActiveAgent
5
5
  module GenerationProvider
6
6
  class OpenRouterProvider < OpenAIProvider
7
- # Vision-capable models on OpenRouter
8
- VISION_MODELS = [
9
- "openai/gpt-4-vision-preview",
10
- "openai/gpt-4o",
11
- "openai/gpt-4o-mini",
12
- "anthropic/claude-3-5-sonnet",
13
- "anthropic/claude-3-opus",
14
- "anthropic/claude-3-sonnet",
15
- "anthropic/claude-3-haiku",
16
- "google/gemini-pro-1.5",
17
- "google/gemini-pro-vision"
18
- ].freeze
19
-
20
- # Models that support structured output
21
- STRUCTURED_OUTPUT_MODELS = [
22
- "openai/gpt-4o",
23
- "openai/gpt-4o-2024-08-06",
24
- "openai/gpt-4o-mini",
25
- "openai/gpt-4o-mini-2024-07-18",
26
- "openai/gpt-4-turbo",
27
- "openai/gpt-4-turbo-2024-04-09",
28
- "openai/gpt-3.5-turbo-0125",
29
- "openai/gpt-3.5-turbo-1106"
30
- ].freeze
31
-
32
7
  def initialize(config)
33
8
  @config = config
34
9
  @access_token = config["api_key"] || config["access_token"] ||
@@ -52,7 +27,7 @@ module ActiveAgent
52
27
  @client = OpenAI::Client.new(
53
28
  uri_base: "https://openrouter.ai/api/v1",
54
29
  access_token: @access_token,
55
- log_errors: true,
30
+ log_errors: Rails.env.development?,
56
31
  default_headers: openrouter_headers
57
32
  )
58
33
  end
@@ -69,15 +44,6 @@ module ActiveAgent
69
44
  handle_openrouter_error(e)
70
45
  end
71
46
 
72
- # Helper methods for checking model capabilities
73
- def supports_vision?(model = @model_name)
74
- VISION_MODELS.include?(model)
75
- end
76
-
77
- def supports_structured_output?(model = @model_name)
78
- STRUCTURED_OUTPUT_MODELS.include?(model)
79
- end
80
-
81
47
  protected
82
48
 
83
49
  def build_provider_parameters
@@ -88,6 +54,32 @@ module ActiveAgent
88
54
  add_openrouter_params(params)
89
55
  end
90
56
 
57
+ def format_content_item(item)
58
+ # Handle OpenRouter-specific content formats
59
+ if item.is_a?(Hash)
60
+ case item[:type] || item["type"]
61
+ when "file"
62
+ # Convert file type to image_url for OpenRouter PDF support
63
+ file_data = item.dig(:file, :file_data) || item.dig("file", "file_data")
64
+ if file_data
65
+ {
66
+ type: "image_url",
67
+ image_url: {
68
+ url: file_data
69
+ }
70
+ }
71
+ else
72
+ item
73
+ end
74
+ else
75
+ # Use default formatting for other types
76
+ super
77
+ end
78
+ else
79
+ super
80
+ end
81
+ end
82
+
91
83
  private
92
84
 
93
85
  def default_app_name
@@ -158,6 +150,10 @@ module ActiveAgent
158
150
  parameters[:provider] = build_provider_preferences
159
151
  end
160
152
 
153
+ # Add plugins (e.g., for PDF processing)
154
+
155
+ parameters[:plugins] = prompt.options[:plugins] if prompt.options[:plugins].present?
156
+ parameters[:models] = prompt.options[:fallback_models] if prompt.options[:enable_fallbacks] && prompt.options[:fallback_models].present?
161
157
  parameters
162
158
  end
163
159
 
@@ -208,7 +204,6 @@ module ActiveAgent
208
204
  parameters[:stream] = provider_stream if prompt.options[:stream] || config["stream"]
209
205
 
210
206
  response = @client.chat(parameters: parameters)
211
-
212
207
  # Log if fallback was used
213
208
  if response.respond_to?(:headers) && response.headers["x-model"] != @model_name
214
209
  Rails.logger.info "[OpenRouter] Fallback model used: #{response.headers['x-model']}" if defined?(Rails)
@@ -229,7 +224,6 @@ module ActiveAgent
229
224
  message = handle_message(message_json) if message_json
230
225
 
231
226
  update_context(prompt: prompt, message: message, response: response) if message
232
-
233
227
  # Create response with OpenRouter metadata
234
228
  @response = ActiveAgent::GenerationProvider::Response.new(
235
229
  prompt: prompt,
@@ -315,7 +309,7 @@ module ActiveAgent
315
309
  handle_timeout_error(error)
316
310
  else
317
311
  # Fall back to parent error handling
318
- super(error) if defined?(super)
312
+ raise GenerationProviderError, error, error.backtrace
319
313
  end
320
314
  end
321
315
 
@@ -46,7 +46,7 @@ module ActiveAgent
46
46
  options = {}
47
47
 
48
48
  # Common options that map directly
49
- [ :stream, :top_p, :frequency_penalty, :presence_penalty, :seed, :stop, :user ].each do |key|
49
+ [ :stream, :top_p, :frequency_penalty, :presence_penalty, :seed, :stop, :user, :plugins ].each do |key|
50
50
  options[key] = @prompt.options[key] if @prompt.options.key?(key)
51
51
  end
52
52
 
@@ -68,7 +68,13 @@ module ActiveAgent
68
68
 
69
69
  initializer "active_agent.compile_config_methods" do
70
70
  ActiveSupport.on_load(:active_agent) do
71
- config.compile_methods! if config.respond_to?(:compile_methods!)
71
+ config.compile_methods! if config.class.respond_to?(:compile_methods!)
72
+ end
73
+ end
74
+
75
+ initializer "active_agent.inflections" do
76
+ ActiveSupport::Inflector.inflections do |inflect|
77
+ inflect.acronym "AI"
72
78
  end
73
79
  end
74
80
 
@@ -1,3 +1,3 @@
1
1
  module ActiveAgent
2
- VERSION = "0.6.0rc1"
2
+ VERSION = "0.6.0rc2"
3
3
  end
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.0rc1
4
+ version: 0.6.0rc2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Bowen
@@ -18,7 +18,7 @@ dependencies:
18
18
  version: '7.2'
19
19
  - - "<="
20
20
  - !ruby/object:Gem::Version
21
- version: 8.0.2.1
21
+ version: '9.0'
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -28,7 +28,7 @@ dependencies:
28
28
  version: '7.2'
29
29
  - - "<="
30
30
  - !ruby/object:Gem::Version
31
- version: 8.0.2.1
31
+ version: '9.0'
32
32
  - !ruby/object:Gem::Dependency
33
33
  name: actionview
34
34
  requirement: !ruby/object:Gem::Requirement
@@ -38,7 +38,7 @@ dependencies:
38
38
  version: '7.2'
39
39
  - - "<="
40
40
  - !ruby/object:Gem::Version
41
- version: 8.0.2.1
41
+ version: '9.0'
42
42
  type: :runtime
43
43
  prerelease: false
44
44
  version_requirements: !ruby/object:Gem::Requirement
@@ -48,7 +48,7 @@ dependencies:
48
48
  version: '7.2'
49
49
  - - "<="
50
50
  - !ruby/object:Gem::Version
51
- version: 8.0.2.1
51
+ version: '9.0'
52
52
  - !ruby/object:Gem::Dependency
53
53
  name: activesupport
54
54
  requirement: !ruby/object:Gem::Requirement
@@ -58,7 +58,7 @@ dependencies:
58
58
  version: '7.2'
59
59
  - - "<="
60
60
  - !ruby/object:Gem::Version
61
- version: 8.0.2.1
61
+ version: '9.0'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
@@ -68,7 +68,7 @@ dependencies:
68
68
  version: '7.2'
69
69
  - - "<="
70
70
  - !ruby/object:Gem::Version
71
- version: 8.0.2.1
71
+ version: '9.0'
72
72
  - !ruby/object:Gem::Dependency
73
73
  name: activemodel
74
74
  requirement: !ruby/object:Gem::Requirement
@@ -78,7 +78,7 @@ dependencies:
78
78
  version: '7.2'
79
79
  - - "<="
80
80
  - !ruby/object:Gem::Version
81
- version: 8.0.2.1
81
+ version: '9.0'
82
82
  type: :runtime
83
83
  prerelease: false
84
84
  version_requirements: !ruby/object:Gem::Requirement
@@ -88,7 +88,7 @@ dependencies:
88
88
  version: '7.2'
89
89
  - - "<="
90
90
  - !ruby/object:Gem::Version
91
- version: 8.0.2.1
91
+ version: '9.0'
92
92
  - !ruby/object:Gem::Dependency
93
93
  name: activejob
94
94
  requirement: !ruby/object:Gem::Requirement
@@ -98,7 +98,7 @@ dependencies:
98
98
  version: '7.2'
99
99
  - - "<="
100
100
  - !ruby/object:Gem::Version
101
- version: 8.0.2.1
101
+ version: '9.0'
102
102
  type: :runtime
103
103
  prerelease: false
104
104
  version_requirements: !ruby/object:Gem::Requirement
@@ -108,35 +108,147 @@ dependencies:
108
108
  version: '7.2'
109
109
  - - "<="
110
110
  - !ruby/object:Gem::Version
111
- version: 8.0.2.1
111
+ version: '9.0'
112
112
  - !ruby/object:Gem::Dependency
113
113
  name: jbuilder
114
114
  requirement: !ruby/object:Gem::Requirement
115
115
  requirements:
116
116
  - - "~>"
117
117
  - !ruby/object:Gem::Version
118
- version: 2.14.1
118
+ version: '2.14'
119
119
  type: :development
120
120
  prerelease: false
121
121
  version_requirements: !ruby/object:Gem::Requirement
122
122
  requirements:
123
123
  - - "~>"
124
124
  - !ruby/object:Gem::Version
125
- version: 2.14.1
125
+ version: '2.14'
126
126
  - !ruby/object:Gem::Dependency
127
127
  name: rails
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ - !ruby/object:Gem::Dependency
141
+ name: ruby-openai
142
+ requirement: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - "~>"
145
+ - !ruby/object:Gem::Version
146
+ version: 8.2.0
147
+ type: :development
148
+ prerelease: false
149
+ version_requirements: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - "~>"
152
+ - !ruby/object:Gem::Version
153
+ version: 8.2.0
154
+ - !ruby/object:Gem::Dependency
155
+ name: ruby-anthropic
128
156
  requirement: !ruby/object:Gem::Requirement
129
157
  requirements:
130
158
  - - "~>"
131
159
  - !ruby/object:Gem::Version
132
- version: 8.0.2.1
160
+ version: 0.4.2
133
161
  type: :development
134
162
  prerelease: false
135
163
  version_requirements: !ruby/object:Gem::Requirement
136
164
  requirements:
137
165
  - - "~>"
138
166
  - !ruby/object:Gem::Version
139
- version: 8.0.2.1
167
+ version: 0.4.2
168
+ - !ruby/object:Gem::Dependency
169
+ name: standard
170
+ requirement: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - ">="
173
+ - !ruby/object:Gem::Version
174
+ version: '0'
175
+ type: :development
176
+ prerelease: false
177
+ version_requirements: !ruby/object:Gem::Requirement
178
+ requirements:
179
+ - - ">="
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ - !ruby/object:Gem::Dependency
183
+ name: rubocop-rails-omakase
184
+ requirement: !ruby/object:Gem::Requirement
185
+ requirements:
186
+ - - ">="
187
+ - !ruby/object:Gem::Version
188
+ version: '0'
189
+ type: :development
190
+ prerelease: false
191
+ version_requirements: !ruby/object:Gem::Requirement
192
+ requirements:
193
+ - - ">="
194
+ - !ruby/object:Gem::Version
195
+ version: '0'
196
+ - !ruby/object:Gem::Dependency
197
+ name: puma
198
+ requirement: !ruby/object:Gem::Requirement
199
+ requirements:
200
+ - - ">="
201
+ - !ruby/object:Gem::Version
202
+ version: '0'
203
+ type: :development
204
+ prerelease: false
205
+ version_requirements: !ruby/object:Gem::Requirement
206
+ requirements:
207
+ - - ">="
208
+ - !ruby/object:Gem::Version
209
+ version: '0'
210
+ - !ruby/object:Gem::Dependency
211
+ name: sqlite3
212
+ requirement: !ruby/object:Gem::Requirement
213
+ requirements:
214
+ - - ">="
215
+ - !ruby/object:Gem::Version
216
+ version: '0'
217
+ type: :development
218
+ prerelease: false
219
+ version_requirements: !ruby/object:Gem::Requirement
220
+ requirements:
221
+ - - ">="
222
+ - !ruby/object:Gem::Version
223
+ version: '0'
224
+ - !ruby/object:Gem::Dependency
225
+ name: vcr
226
+ requirement: !ruby/object:Gem::Requirement
227
+ requirements:
228
+ - - ">="
229
+ - !ruby/object:Gem::Version
230
+ version: '0'
231
+ type: :development
232
+ prerelease: false
233
+ version_requirements: !ruby/object:Gem::Requirement
234
+ requirements:
235
+ - - ">="
236
+ - !ruby/object:Gem::Version
237
+ version: '0'
238
+ - !ruby/object:Gem::Dependency
239
+ name: webmock
240
+ requirement: !ruby/object:Gem::Requirement
241
+ requirements:
242
+ - - ">="
243
+ - !ruby/object:Gem::Version
244
+ version: '0'
245
+ type: :development
246
+ prerelease: false
247
+ version_requirements: !ruby/object:Gem::Requirement
248
+ requirements:
249
+ - - ">="
250
+ - !ruby/object:Gem::Version
251
+ version: '0'
140
252
  description: The only agent-oriented AI framework designed for Rails, where Agents
141
253
  are Controllers. Build AI features with less complexity using the MVC conventions
142
254
  you love.