activeagent 0.6.0rc2 → 0.6.0rc4
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/action_prompt/base.rb +31 -12
- data/lib/active_agent/action_prompt/prompt.rb +16 -6
- data/lib/active_agent/callbacks.rb +0 -15
- data/lib/active_agent/generation_provider/anthropic_provider.rb +8 -4
- data/lib/active_agent/generation_provider/message_formatting.rb +2 -2
- data/lib/active_agent/generation_provider/open_ai_provider.rb +36 -12
- data/lib/active_agent/generation_provider/open_router_provider.rb +54 -6
- data/lib/active_agent/generation_provider/response.rb +38 -8
- data/lib/active_agent/streaming.rb +34 -0
- data/lib/active_agent/version.rb +1 -1
- data/lib/active_agent.rb +1 -0
- data/lib/generators/erb/install_generator.rb +0 -1
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 805b4d2cce3173788f3bb281e40973d8782891937b5220654b7dad53a9f701ec
|
4
|
+
data.tar.gz: 968f534f94d4e32541f1d87e21854add0f4bf8c9e851ba75f7599291a0b384b9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ec13c2dd2d8deb4fd4adb1a6f302de6a7bb5057fad1cd45f27fdecc6b8bea7b5496f3078e7a1f40168de9b4efaa8e4a2abf1cf1bd2865fa929912c58adce561c
|
7
|
+
data.tar.gz: a564772159ac558c8350d770977fc9b7b509c814435a748cac61af89bd87dc196614b45cc9f33db7bce8951568f659730bfe4cf95fd274f145ae00bb78510af6
|
@@ -2,6 +2,8 @@ require "active_agent/collector"
|
|
2
2
|
require "active_support/core_ext/string/inflections"
|
3
3
|
require "active_support/core_ext/hash/except"
|
4
4
|
require "active_support/core_ext/module/anonymous"
|
5
|
+
require "active_agent/action_prompt/message"
|
6
|
+
require "active_agent/action_prompt/action"
|
5
7
|
|
6
8
|
# require "active_agent/log_subscriber"
|
7
9
|
require "active_agent/rescuable"
|
@@ -10,6 +12,7 @@ module ActiveAgent
|
|
10
12
|
class Base < AbstractController::Base
|
11
13
|
include Callbacks
|
12
14
|
include GenerationProvider
|
15
|
+
include Streaming
|
13
16
|
include QueuedGeneration
|
14
17
|
include Rescuable
|
15
18
|
include Parameterized
|
@@ -217,7 +220,8 @@ module ActiveAgent
|
|
217
220
|
def handle_response(response)
|
218
221
|
return response unless response.message.requested_actions.present?
|
219
222
|
|
220
|
-
#
|
223
|
+
# The assistant message with tool_calls is already added by update_context in the provider
|
224
|
+
# Now perform the requested actions which will add tool response messages
|
221
225
|
perform_actions(requested_actions: response.message.requested_actions)
|
222
226
|
|
223
227
|
# Continue generation with updated context
|
@@ -241,9 +245,9 @@ module ActiveAgent
|
|
241
245
|
end
|
242
246
|
|
243
247
|
def perform_action(action)
|
244
|
-
|
245
|
-
|
246
|
-
original_params =
|
248
|
+
# Save the current messages to preserve conversation history
|
249
|
+
original_messages = context.messages.dup
|
250
|
+
original_params = context.params || {}
|
247
251
|
|
248
252
|
if action.params.is_a?(Hash)
|
249
253
|
self.params = original_params.merge(action.params)
|
@@ -251,14 +255,26 @@ module ActiveAgent
|
|
251
255
|
self.params = original_params
|
252
256
|
end
|
253
257
|
|
258
|
+
# Save the current prompt_was_called state and reset it so the action can render
|
259
|
+
original_prompt_was_called = @_prompt_was_called
|
260
|
+
@_prompt_was_called = false
|
261
|
+
|
262
|
+
# Process the action, which will render the view and populate context
|
254
263
|
process(action.name)
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
context.message.
|
259
|
-
|
260
|
-
|
261
|
-
|
264
|
+
|
265
|
+
# The action should have called prompt which populates context.message
|
266
|
+
# Create a tool message from the rendered response
|
267
|
+
tool_message = context.message.dup
|
268
|
+
tool_message.role = :tool
|
269
|
+
tool_message.action_id = action.id
|
270
|
+
tool_message.action_name = action.name
|
271
|
+
tool_message.generation_id = action.id
|
272
|
+
|
273
|
+
# Restore the messages with the new tool message
|
274
|
+
context.messages = original_messages + [ tool_message ]
|
275
|
+
|
276
|
+
# Restore the prompt_was_called state
|
277
|
+
@_prompt_was_called = original_prompt_was_called
|
262
278
|
end
|
263
279
|
|
264
280
|
def initialize # :nodoc:
|
@@ -425,7 +441,10 @@ module ActiveAgent
|
|
425
441
|
# Extract runtime options from prompt_options (exclude instructions as it has special template logic)
|
426
442
|
runtime_options = prompt_options.slice(
|
427
443
|
:model, :temperature, :max_tokens, :stream, :top_p, :frequency_penalty,
|
428
|
-
:presence_penalty, :response_format, :seed, :stop, :tools_choice, :
|
444
|
+
:presence_penalty, :response_format, :seed, :stop, :tools_choice, :plugins,
|
445
|
+
|
446
|
+
# OpenRouter Provider Settings
|
447
|
+
:data_collection, :require_parameters, :only, :ignore, :quantizations, :sort, :max_price
|
429
448
|
)
|
430
449
|
# Handle explicit options parameter
|
431
450
|
explicit_options = prompt_options[:options] || {}
|
@@ -30,7 +30,12 @@ module ActiveAgent
|
|
30
30
|
@action_name = attributes.fetch(:action_name, nil)
|
31
31
|
@mcp_servers = attributes.fetch(:mcp_servers, [])
|
32
32
|
set_message if attributes[:message].is_a?(String) || @body.is_a?(String) && @message&.content
|
33
|
-
|
33
|
+
# Ensure we have a system message with instructions at the start
|
34
|
+
if @messages.empty? || @messages[0].role != :system
|
35
|
+
@messages.unshift(instructions_message)
|
36
|
+
elsif @instructions.present?
|
37
|
+
@messages[0] = instructions_message
|
38
|
+
end
|
34
39
|
end
|
35
40
|
|
36
41
|
def multimodal?
|
@@ -39,17 +44,22 @@ module ActiveAgent
|
|
39
44
|
|
40
45
|
def messages=(messages)
|
41
46
|
@messages = messages
|
42
|
-
|
47
|
+
# Only add system message if we have instructions and don't already have a system message
|
48
|
+
if @instructions.present? && (@messages.empty? || @messages.first&.role != :system)
|
49
|
+
set_messages
|
50
|
+
end
|
43
51
|
end
|
44
52
|
|
45
53
|
def instructions=(instructions)
|
46
|
-
|
54
|
+
# Store the instructions even if blank (will use empty string)
|
55
|
+
@instructions = instructions || ""
|
47
56
|
|
48
|
-
|
57
|
+
# Update or add the system message
|
49
58
|
if @messages[0].present? && @messages[0].role == :system
|
50
59
|
@messages[0] = instructions_message
|
51
|
-
|
52
|
-
|
60
|
+
elsif @messages.empty? || @messages[0].role != :system
|
61
|
+
# Only add system message if we don't have one at the start
|
62
|
+
@messages.unshift(instructions_message)
|
53
63
|
end
|
54
64
|
end
|
55
65
|
|
@@ -7,7 +7,6 @@ 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 :stream, skip_after_callbacks_if_terminated: true
|
11
10
|
end
|
12
11
|
|
13
12
|
module ClassMethods
|
@@ -20,20 +19,6 @@ module ActiveAgent
|
|
20
19
|
end
|
21
20
|
end
|
22
21
|
end
|
23
|
-
|
24
|
-
# Defines a callback for handling streaming responses during generation
|
25
|
-
def on_stream(*names, &blk)
|
26
|
-
_insert_callbacks(names, blk) do |name, options|
|
27
|
-
set_callback(:stream, :before, name, options)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
# Helper method to run stream callbacks
|
33
|
-
def run_stream_callbacks(message, delta = nil, stop = false)
|
34
|
-
run_callbacks(:stream) do
|
35
|
-
yield(message, delta, stop) if block_given?
|
36
|
-
end
|
37
22
|
end
|
38
23
|
end
|
39
24
|
end
|
@@ -36,9 +36,12 @@ module ActiveAgent
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def chat_prompt(parameters: prompt_parameters)
|
39
|
-
|
39
|
+
if prompt.options[:stream] || config["stream"]
|
40
|
+
parameters[:stream] = provider_stream
|
41
|
+
@streaming_request_params = parameters
|
42
|
+
end
|
40
43
|
|
41
|
-
chat_response(@client.messages(parameters: parameters))
|
44
|
+
chat_response(@client.messages(parameters: parameters), parameters)
|
42
45
|
end
|
43
46
|
|
44
47
|
protected
|
@@ -120,7 +123,7 @@ module ActiveAgent
|
|
120
123
|
end
|
121
124
|
end
|
122
125
|
|
123
|
-
def chat_response(response)
|
126
|
+
def chat_response(response, request_params = nil)
|
124
127
|
return @response if prompt.options[:stream]
|
125
128
|
|
126
129
|
content = response["content"].first["text"]
|
@@ -137,7 +140,8 @@ module ActiveAgent
|
|
137
140
|
@response = ActiveAgent::GenerationProvider::Response.new(
|
138
141
|
prompt: prompt,
|
139
142
|
message: message,
|
140
|
-
raw_response: response
|
143
|
+
raw_response: response,
|
144
|
+
raw_request: request_params
|
141
145
|
)
|
142
146
|
end
|
143
147
|
|
@@ -94,12 +94,12 @@ module ActiveAgent
|
|
94
94
|
def format_single_tool_call(action)
|
95
95
|
# Default tool call format (OpenAI style)
|
96
96
|
{
|
97
|
+
id: action.id,
|
97
98
|
type: "function",
|
98
99
|
function: {
|
99
100
|
name: action.name,
|
100
101
|
arguments: action.params.is_a?(String) ? action.params : action.params.to_json
|
101
|
-
}
|
102
|
-
id: action.id
|
102
|
+
}
|
103
103
|
}
|
104
104
|
end
|
105
105
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
begin
|
2
|
-
gem "ruby-openai", "
|
2
|
+
gem "ruby-openai", ">= 8.1.0"
|
3
3
|
require "openai"
|
4
4
|
rescue LoadError
|
5
5
|
raise LoadError, "The 'ruby-openai' gem is required for OpenAIProvider. Please add it to your Gemfile and run `bundle install`."
|
@@ -69,7 +69,12 @@ module ActiveAgent
|
|
69
69
|
elsif chunk.dig("choices", 0, "delta", "tool_calls") && chunk.dig("choices", 0, "delta", "role")
|
70
70
|
message = handle_message(chunk.dig("choices", 0, "delta"))
|
71
71
|
prompt.messages << message
|
72
|
-
@response = ActiveAgent::GenerationProvider::Response.new(
|
72
|
+
@response = ActiveAgent::GenerationProvider::Response.new(
|
73
|
+
prompt:,
|
74
|
+
message:,
|
75
|
+
raw_response: chunk,
|
76
|
+
raw_request: @streaming_request_params
|
77
|
+
)
|
73
78
|
end
|
74
79
|
|
75
80
|
if chunk.dig("choices", 0, "finish_reason")
|
@@ -92,7 +97,7 @@ module ActiveAgent
|
|
92
97
|
# The format_tools method comes from ToolManagement module
|
93
98
|
# The provider_messages method comes from MessageFormatting module
|
94
99
|
|
95
|
-
def chat_response(response)
|
100
|
+
def chat_response(response, request_params = nil)
|
96
101
|
return @response if prompt.options[:stream]
|
97
102
|
message_json = response.dig("choices", 0, "message")
|
98
103
|
message_json["id"] = response.dig("id") if message_json["id"].blank?
|
@@ -100,10 +105,15 @@ module ActiveAgent
|
|
100
105
|
|
101
106
|
update_context(prompt: prompt, message: message, response: response)
|
102
107
|
|
103
|
-
@response = ActiveAgent::GenerationProvider::Response.new(
|
108
|
+
@response = ActiveAgent::GenerationProvider::Response.new(
|
109
|
+
prompt: prompt,
|
110
|
+
message: message,
|
111
|
+
raw_response: response,
|
112
|
+
raw_request: request_params
|
113
|
+
)
|
104
114
|
end
|
105
115
|
|
106
|
-
def responses_response(response)
|
116
|
+
def responses_response(response, request_params = nil)
|
107
117
|
message_json = response["output"].find { |output_item| output_item["type"] == "message" }
|
108
118
|
message_json["id"] = response.dig("id") if message_json["id"].blank?
|
109
119
|
|
@@ -116,7 +126,12 @@ module ActiveAgent
|
|
116
126
|
content_type: prompt.output_schema.present? ? "application/json" : "text/plain",
|
117
127
|
)
|
118
128
|
|
119
|
-
@response = ActiveAgent::GenerationProvider::Response.new(
|
129
|
+
@response = ActiveAgent::GenerationProvider::Response.new(
|
130
|
+
prompt: prompt,
|
131
|
+
message: message,
|
132
|
+
raw_response: response,
|
133
|
+
raw_request: request_params
|
134
|
+
)
|
120
135
|
end
|
121
136
|
|
122
137
|
def handle_message(message_json)
|
@@ -133,13 +148,16 @@ module ActiveAgent
|
|
133
148
|
# handle_actions is now provided by ToolManagement module
|
134
149
|
|
135
150
|
def chat_prompt(parameters: prompt_parameters)
|
136
|
-
|
137
|
-
|
151
|
+
if prompt.options[:stream] || config["stream"]
|
152
|
+
parameters[:stream] = provider_stream
|
153
|
+
@streaming_request_params = parameters
|
154
|
+
end
|
155
|
+
chat_response(@client.chat(parameters: parameters), parameters)
|
138
156
|
end
|
139
157
|
|
140
158
|
def responses_prompt(parameters: responses_parameters)
|
141
159
|
# parameters[:stream] = provider_stream if prompt.options[:stream] || config["stream"]
|
142
|
-
responses_response(@client.responses.create(parameters: parameters))
|
160
|
+
responses_response(@client.responses.create(parameters: parameters), parameters)
|
143
161
|
end
|
144
162
|
|
145
163
|
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)
|
@@ -158,14 +176,20 @@ module ActiveAgent
|
|
158
176
|
}
|
159
177
|
end
|
160
178
|
|
161
|
-
def embeddings_response(response)
|
179
|
+
def embeddings_response(response, request_params = nil)
|
162
180
|
message = ActiveAgent::ActionPrompt::Message.new(content: response.dig("data", 0, "embedding"), role: "assistant")
|
163
181
|
|
164
|
-
@response = ActiveAgent::GenerationProvider::Response.new(
|
182
|
+
@response = ActiveAgent::GenerationProvider::Response.new(
|
183
|
+
prompt: prompt,
|
184
|
+
message: message,
|
185
|
+
raw_response: response,
|
186
|
+
raw_request: request_params
|
187
|
+
)
|
165
188
|
end
|
166
189
|
|
167
190
|
def embeddings_prompt(parameters:)
|
168
|
-
|
191
|
+
params = embeddings_parameters
|
192
|
+
embeddings_response(@client.embeddings(parameters: params), params)
|
169
193
|
end
|
170
194
|
end
|
171
195
|
end
|
@@ -23,12 +23,22 @@ module ActiveAgent
|
|
23
23
|
# Data collection preference (allow, deny, or specific provider list)
|
24
24
|
@data_collection = config["data_collection"] || @provider_preferences["data_collection"] || "allow"
|
25
25
|
|
26
|
+
# Require parameters preference (defaults to false)
|
27
|
+
@require_parameters = config["require_parameters"] || @provider_preferences["require_parameters"] || false
|
28
|
+
|
29
|
+
# Additional OpenRouter provider routing options
|
30
|
+
@only_providers = config["only"] || @provider_preferences["only"]
|
31
|
+
@ignore_providers = config["ignore"] || @provider_preferences["ignore"]
|
32
|
+
@quantizations = config["quantizations"] || @provider_preferences["quantizations"]
|
33
|
+
@sort_preference = config["sort"] || @provider_preferences["sort"]
|
34
|
+
@max_price = config["max_price"] || @provider_preferences["max_price"]
|
35
|
+
|
26
36
|
# Initialize OpenAI client with OpenRouter base URL
|
27
37
|
@client = OpenAI::Client.new(
|
28
38
|
uri_base: "https://openrouter.ai/api/v1",
|
29
39
|
access_token: @access_token,
|
30
40
|
log_errors: Rails.env.development?,
|
31
|
-
|
41
|
+
extra_headers: openrouter_headers
|
32
42
|
)
|
33
43
|
end
|
34
44
|
|
@@ -144,9 +154,15 @@ module ActiveAgent
|
|
144
154
|
parameters[:transforms] = @transforms if @transforms.present?
|
145
155
|
|
146
156
|
# Add provider preferences (always include if we have data_collection or other settings)
|
147
|
-
# Check both configured and runtime data_collection values
|
157
|
+
# Check both configured and runtime data_collection/require_parameters values
|
148
158
|
runtime_data_collection = prompt&.options&.key?(:data_collection)
|
149
|
-
|
159
|
+
runtime_require_parameters = prompt&.options&.key?(:require_parameters)
|
160
|
+
runtime_provider_options = prompt&.options&.keys&.any? { |k| [ :only, :ignore, :quantizations, :sort, :max_price ].include?(k) }
|
161
|
+
|
162
|
+
if @provider_preferences.present? || @data_collection != "allow" || @require_parameters != false ||
|
163
|
+
@only_providers.present? || @ignore_providers.present? || @quantizations.present? ||
|
164
|
+
@sort_preference.present? || @max_price.present? ||
|
165
|
+
runtime_data_collection || runtime_require_parameters || runtime_provider_options
|
150
166
|
parameters[:provider] = build_provider_preferences
|
151
167
|
end
|
152
168
|
|
@@ -160,7 +176,12 @@ module ActiveAgent
|
|
160
176
|
def build_provider_preferences
|
161
177
|
prefs = {}
|
162
178
|
prefs[:order] = @provider_preferences["order"] if @provider_preferences["order"]
|
163
|
-
|
179
|
+
|
180
|
+
# Require parameters can be overridden at runtime
|
181
|
+
require_parameters = prompt.options[:require_parameters] if prompt&.options&.key?(:require_parameters)
|
182
|
+
require_parameters = @require_parameters if require_parameters.nil?
|
183
|
+
prefs[:require_parameters] = require_parameters if require_parameters != false
|
184
|
+
|
164
185
|
prefs[:allow_fallbacks] = @enable_fallbacks
|
165
186
|
|
166
187
|
# Data collection can be:
|
@@ -172,6 +193,27 @@ module ActiveAgent
|
|
172
193
|
data_collection ||= @data_collection
|
173
194
|
prefs[:data_collection] = data_collection
|
174
195
|
|
196
|
+
# Additional OpenRouter provider routing options - check runtime overrides first
|
197
|
+
only_providers = prompt.options[:only] if prompt&.options&.key?(:only)
|
198
|
+
only_providers ||= @only_providers
|
199
|
+
prefs[:only] = only_providers if only_providers.present?
|
200
|
+
|
201
|
+
ignore_providers = prompt.options[:ignore] if prompt&.options&.key?(:ignore)
|
202
|
+
ignore_providers ||= @ignore_providers
|
203
|
+
prefs[:ignore] = ignore_providers if ignore_providers.present?
|
204
|
+
|
205
|
+
quantizations = prompt.options[:quantizations] if prompt&.options&.key?(:quantizations)
|
206
|
+
quantizations ||= @quantizations
|
207
|
+
prefs[:quantizations] = quantizations if quantizations.present?
|
208
|
+
|
209
|
+
sort_preference = prompt.options[:sort] if prompt&.options&.key?(:sort)
|
210
|
+
sort_preference ||= @sort_preference
|
211
|
+
prefs[:sort] = sort_preference if sort_preference.present?
|
212
|
+
|
213
|
+
max_price = prompt.options[:max_price] if prompt&.options&.key?(:max_price)
|
214
|
+
max_price ||= @max_price
|
215
|
+
prefs[:max_price] = max_price if max_price.present?
|
216
|
+
|
175
217
|
prefs.compact
|
176
218
|
end
|
177
219
|
|
@@ -186,9 +228,15 @@ module ActiveAgent
|
|
186
228
|
params[:transforms] = @transforms if @transforms.present?
|
187
229
|
|
188
230
|
# Add provider configuration (always include if we have data_collection or other settings)
|
189
|
-
# Check both configured and runtime data_collection values
|
231
|
+
# Check both configured and runtime data_collection/require_parameters values
|
190
232
|
runtime_data_collection = prompt&.options&.key?(:data_collection)
|
191
|
-
|
233
|
+
runtime_require_parameters = prompt&.options&.key?(:require_parameters)
|
234
|
+
runtime_provider_options = prompt&.options&.keys&.any? { |k| [ :only, :ignore, :quantizations, :sort, :max_price ].include?(k) }
|
235
|
+
|
236
|
+
if @provider_preferences.present? || @data_collection != "allow" || @require_parameters != false ||
|
237
|
+
@only_providers.present? || @ignore_providers.present? || @quantizations.present? ||
|
238
|
+
@sort_preference.present? || @max_price.present? ||
|
239
|
+
runtime_data_collection || runtime_require_parameters || runtime_provider_options
|
192
240
|
params[:provider] = build_provider_preferences
|
193
241
|
end
|
194
242
|
|
@@ -3,13 +3,14 @@
|
|
3
3
|
module ActiveAgent
|
4
4
|
module GenerationProvider
|
5
5
|
class Response
|
6
|
-
attr_reader :message, :prompt, :raw_response
|
6
|
+
attr_reader :message, :prompt, :raw_response, :raw_request
|
7
7
|
attr_accessor :metadata
|
8
8
|
|
9
|
-
def initialize(prompt:, message: nil, raw_response: nil, metadata: nil)
|
9
|
+
def initialize(prompt:, message: nil, raw_response: nil, raw_request: nil, metadata: nil)
|
10
10
|
@prompt = prompt
|
11
11
|
@message = message || prompt.message
|
12
12
|
@raw_response = raw_response
|
13
|
+
@raw_request = sanitize_request(raw_request)
|
13
14
|
@metadata = metadata || {}
|
14
15
|
end
|
15
16
|
|
@@ -17,14 +18,9 @@ module ActiveAgent
|
|
17
18
|
def usage
|
18
19
|
return nil unless @raw_response
|
19
20
|
|
20
|
-
#
|
21
|
+
# Most providers store usage in the same format
|
21
22
|
if @raw_response.is_a?(Hash) && @raw_response["usage"]
|
22
23
|
@raw_response["usage"]
|
23
|
-
# Anthropic format
|
24
|
-
elsif @raw_response.is_a?(Hash) && @raw_response["usage"]
|
25
|
-
@raw_response["usage"]
|
26
|
-
else
|
27
|
-
nil
|
28
24
|
end
|
29
25
|
end
|
30
26
|
|
@@ -40,6 +36,40 @@ module ActiveAgent
|
|
40
36
|
def total_tokens
|
41
37
|
usage&.dig("total_tokens")
|
42
38
|
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def sanitize_request(request)
|
43
|
+
return nil if request.nil?
|
44
|
+
return request unless request.is_a?(Hash)
|
45
|
+
|
46
|
+
# Deep clone the request to avoid modifying the original
|
47
|
+
sanitized = request.deep_dup
|
48
|
+
|
49
|
+
# Sanitize any string values in the request
|
50
|
+
sanitize_hash_values(sanitized)
|
51
|
+
end
|
52
|
+
|
53
|
+
def sanitize_hash_values(hash)
|
54
|
+
hash.each do |key, value|
|
55
|
+
case value
|
56
|
+
when String
|
57
|
+
# Use ActiveAgent's sanitize_credentials to replace sensitive data
|
58
|
+
hash[key] = ActiveAgent.sanitize_credentials(value)
|
59
|
+
when Hash
|
60
|
+
sanitize_hash_values(value)
|
61
|
+
when Array
|
62
|
+
value.each_with_index do |item, index|
|
63
|
+
if item.is_a?(String)
|
64
|
+
value[index] = ActiveAgent.sanitize_credentials(item)
|
65
|
+
elsif item.is_a?(Hash)
|
66
|
+
sanitize_hash_values(item)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
hash
|
72
|
+
end
|
43
73
|
end
|
44
74
|
end
|
45
75
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveAgent
|
4
|
+
module Streaming
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
class StreamChunk < Data.define(:delta, :stop)
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_accessor :stream_chunk
|
11
|
+
|
12
|
+
included do
|
13
|
+
include ActiveSupport::Callbacks
|
14
|
+
define_callbacks :stream, skip_after_callbacks_if_terminated: true
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
# Defines a callback for handling streaming responses during generation
|
19
|
+
def on_stream(*names, &blk)
|
20
|
+
_insert_callbacks(names, blk) do |name, options|
|
21
|
+
set_callback(:stream, :before, name, options)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Helper method to run stream callbacks
|
27
|
+
def run_stream_callbacks(message, delta = nil, stop = false)
|
28
|
+
@stream_chunk = StreamChunk.new(delta, stop)
|
29
|
+
run_callbacks(:stream) do
|
30
|
+
yield(message, delta, stop) if block_given?
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/active_agent/version.rb
CHANGED
data/lib/active_agent.rb
CHANGED
@@ -9,7 +9,6 @@ module Erb # :nodoc:
|
|
9
9
|
def create_agent_layouts
|
10
10
|
if behavior == :invoke
|
11
11
|
formats.each do |format|
|
12
|
-
puts format
|
13
12
|
layout_path = File.join("app/views/layouts", filename_with_extensions("agent", format))
|
14
13
|
template filename_with_extensions(:layout, format), layout_path unless File.exist?(layout_path)
|
15
14
|
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.
|
4
|
+
version: 0.6.0rc4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Bowen
|
@@ -141,16 +141,16 @@ dependencies:
|
|
141
141
|
name: ruby-openai
|
142
142
|
requirement: !ruby/object:Gem::Requirement
|
143
143
|
requirements:
|
144
|
-
- - "
|
144
|
+
- - ">="
|
145
145
|
- !ruby/object:Gem::Version
|
146
|
-
version: 8.
|
146
|
+
version: 8.1.0
|
147
147
|
type: :development
|
148
148
|
prerelease: false
|
149
149
|
version_requirements: !ruby/object:Gem::Requirement
|
150
150
|
requirements:
|
151
|
-
- - "
|
151
|
+
- - ">="
|
152
152
|
- !ruby/object:Gem::Version
|
153
|
-
version: 8.
|
153
|
+
version: 8.1.0
|
154
154
|
- !ruby/object:Gem::Dependency
|
155
155
|
name: ruby-anthropic
|
156
156
|
requirement: !ruby/object:Gem::Requirement
|
@@ -298,6 +298,7 @@ files:
|
|
298
298
|
- lib/active_agent/rescuable.rb
|
299
299
|
- lib/active_agent/sanitizers.rb
|
300
300
|
- lib/active_agent/service.rb
|
301
|
+
- lib/active_agent/streaming.rb
|
301
302
|
- lib/active_agent/test_case.rb
|
302
303
|
- lib/active_agent/version.rb
|
303
304
|
- lib/activeagent.rb
|