activeagent 1.8.1 → 1.9.1
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 +1058 -0
- data/README.md +9 -1
- data/lib/active_agent/base.rb +22 -1
- data/lib/active_agent/concerns/delegation.rb +4 -0
- data/lib/active_agent/concerns/input_requests.rb +178 -0
- data/lib/active_agent/concerns/parameterized.rb +7 -4
- data/lib/active_agent/concerns/provider.rb +1 -0
- data/lib/active_agent/concerns/tooling.rb +7 -2
- data/lib/active_agent/delegation/runner.rb +31 -0
- data/lib/active_agent/evals/catalog.rb +305 -0
- data/lib/active_agent/evals/ruby_llm.rb +411 -0
- data/lib/active_agent/evals/scenario.rb +10 -3
- data/lib/active_agent/evals/scenario_parser.rb +38 -10
- data/lib/active_agent/evals.rb +1 -0
- data/lib/active_agent/generation.rb +62 -2
- data/lib/active_agent/generation_job.rb +14 -3
- data/lib/active_agent/input_request/resume.rb +199 -0
- data/lib/active_agent/input_request.rb +279 -0
- data/lib/active_agent/providers/_base_provider.rb +153 -31
- data/lib/active_agent/providers/anthropic/transforms.rb +57 -28
- data/lib/active_agent/providers/anthropic_provider.rb +102 -20
- data/lib/active_agent/providers/common/responses/prompt.rb +21 -0
- data/lib/active_agent/providers/concerns/input_requests.rb +332 -0
- data/lib/active_agent/providers/concerns/mcp_serving.rb +267 -0
- data/lib/active_agent/providers/concerns/tool_choice_clearing.rb +18 -3
- data/lib/active_agent/providers/deep_seek_provider.rb +6 -0
- data/lib/active_agent/providers/deepseek/_types.rb +4 -0
- data/lib/active_agent/providers/deepseek/chat/_types.rb +54 -0
- data/lib/active_agent/providers/deepseek/chat/request.rb +121 -0
- data/lib/active_agent/providers/deepseek/options.rb +53 -0
- data/lib/active_agent/providers/deepseek_provider.rb +46 -0
- data/lib/active_agent/providers/log_subscriber.rb +13 -0
- data/lib/active_agent/providers/mcp_bridge.rb +691 -0
- data/lib/active_agent/providers/mcp_tool_cache.rb +230 -0
- data/lib/active_agent/providers/open_ai/_base.rb +1 -1
- data/lib/active_agent/providers/open_ai/chat_provider.rb +22 -3
- data/lib/active_agent/providers/open_ai/options.rb +1 -1
- data/lib/active_agent/providers/open_ai/responses_provider.rb +68 -15
- data/lib/active_agent/providers/open_router/options.rb +6 -2
- data/lib/active_agent/providers/ruby_llm/messages/_types.rb +3 -1
- data/lib/active_agent/providers/ruby_llm/options.rb +5 -0
- data/lib/active_agent/providers/ruby_llm_provider.rb +127 -29
- data/lib/active_agent/schema_tools.rb +12 -0
- data/lib/active_agent/telemetry/instrumentation.rb +10 -1
- data/lib/active_agent/version.rb +1 -1
- data/lib/active_agent.rb +2 -0
- data/lib/generators/active_agent/schema_tools/USAGE +11 -0
- data/lib/generators/active_agent/schema_tools/schema_tools_generator.rb +39 -4
- data/lib/generators/active_agent/schema_tools/templates/schema_tools.rb.tt +13 -0
- metadata +31 -2
data/README.md
CHANGED
|
@@ -57,6 +57,9 @@ bundle add openai
|
|
|
57
57
|
# Requesty (uses OpenAI-compatible API)
|
|
58
58
|
bundle add openai
|
|
59
59
|
|
|
60
|
+
# DeepSeek (uses OpenAI-compatible API)
|
|
61
|
+
bundle add openai
|
|
62
|
+
|
|
60
63
|
# RubyLLM (unified API for 15+ providers)
|
|
61
64
|
bundle add ruby_llm
|
|
62
65
|
```
|
|
@@ -140,6 +143,11 @@ development:
|
|
|
140
143
|
service: "Ollama"
|
|
141
144
|
model: "llama3.2"
|
|
142
145
|
|
|
146
|
+
deepseek:
|
|
147
|
+
service: "DeepSeek"
|
|
148
|
+
api_key: <%= Rails.application.credentials.dig(:deepseek, :api_key) %>
|
|
149
|
+
model: "deepseek-flash"
|
|
150
|
+
|
|
143
151
|
ruby_llm:
|
|
144
152
|
service: "RubyLLM"
|
|
145
153
|
```
|
|
@@ -179,7 +187,7 @@ a free low-volume trial.
|
|
|
179
187
|
## Features
|
|
180
188
|
|
|
181
189
|
- **Agent-Oriented Programming**: Build AI applications using familiar Rails patterns
|
|
182
|
-
- **Multiple Provider Support**: Works with OpenAI, Anthropic, Ollama, RubyLLM, and more
|
|
190
|
+
- **Multiple Provider Support**: Works with OpenAI, Anthropic, Ollama, DeepSeek, RubyLLM, and more
|
|
183
191
|
- **Action-Based Design**: Define agent capabilities through actions
|
|
184
192
|
- **View Templates**: Use ERB templates for prompts (text, JSON, HTML)
|
|
185
193
|
- **Streaming Support**: Real-time response streaming with ActionCable
|
data/lib/active_agent/base.rb
CHANGED
|
@@ -7,6 +7,7 @@ require "active_support/core_ext/string/inflections"
|
|
|
7
7
|
require "active_agent/concerns/authorization"
|
|
8
8
|
require "active_agent/concerns/callbacks"
|
|
9
9
|
require "active_agent/concerns/delegation"
|
|
10
|
+
require "active_agent/concerns/input_requests"
|
|
10
11
|
require "active_agent/concerns/observers"
|
|
11
12
|
require "active_agent/concerns/parameterized"
|
|
12
13
|
require "active_agent/concerns/preview"
|
|
@@ -47,6 +48,7 @@ module ActiveAgent
|
|
|
47
48
|
include Callbacks
|
|
48
49
|
# After Rescue: the refusal handler is registered with rescue_from.
|
|
49
50
|
include Delegation
|
|
51
|
+
include InputRequests
|
|
50
52
|
include Parameterized
|
|
51
53
|
include Provider
|
|
52
54
|
include Queueing
|
|
@@ -261,7 +263,9 @@ module ActiveAgent
|
|
|
261
263
|
run_callbacks(:prompting) do
|
|
262
264
|
parameters = prepare_prompt_parameters
|
|
263
265
|
|
|
264
|
-
prompt_provider_klass.new(**parameters).prompt
|
|
266
|
+
prompt_provider_klass.new(**parameters).prompt.tap do |response|
|
|
267
|
+
run_input_request_callbacks(response)
|
|
268
|
+
end
|
|
265
269
|
end
|
|
266
270
|
end
|
|
267
271
|
end
|
|
@@ -332,6 +336,9 @@ module ActiveAgent
|
|
|
332
336
|
exception_handler:,
|
|
333
337
|
stream_broadcaster:,
|
|
334
338
|
tools_function:,
|
|
339
|
+
action_name: generation_action_name,
|
|
340
|
+
input_request_resume: @_input_request_resume,
|
|
341
|
+
announce_input_requests: !input_requests_silenced?
|
|
335
342
|
)
|
|
336
343
|
|
|
337
344
|
# Apply Templates
|
|
@@ -389,6 +396,7 @@ module ActiveAgent
|
|
|
389
396
|
end
|
|
390
397
|
|
|
391
398
|
if response_format[:type].to_sym == :json_schema
|
|
399
|
+
response_format = nest_flat_json_schema(response_format)
|
|
392
400
|
response_format[:json_schema] = prompt_view_schema(response_format[:json_schema])
|
|
393
401
|
response_format[:json_schema].deep_transform_keys! { _1.to_s.camelize(:lower) }
|
|
394
402
|
end
|
|
@@ -396,6 +404,19 @@ module ActiveAgent
|
|
|
396
404
|
response_format
|
|
397
405
|
end
|
|
398
406
|
|
|
407
|
+
# Moves a json_schema given flat, with its name, schema and strict flag
|
|
408
|
+
# beside +type+, under +json_schema+, so it is not mistaken for a
|
|
409
|
+
# json_schema that names a view. Without a top-level schema, or with a
|
|
410
|
+
# +json_schema+ already, the format is returned as it came.
|
|
411
|
+
#
|
|
412
|
+
# @api private
|
|
413
|
+
def nest_flat_json_schema(response_format)
|
|
414
|
+
return response_format if response_format.key?(:json_schema) || response_format[:schema].nil?
|
|
415
|
+
|
|
416
|
+
flat_keys = %i[name schema strict]
|
|
417
|
+
response_format.except(*flat_keys).merge(json_schema: response_format.slice(*flat_keys))
|
|
418
|
+
end
|
|
419
|
+
|
|
399
420
|
# @api private
|
|
400
421
|
def instrument_payload(key)
|
|
401
422
|
{
|
|
@@ -92,6 +92,10 @@ module ActiveAgent
|
|
|
92
92
|
# schema and the policy is +:raise+.
|
|
93
93
|
class InvalidResultError < StandardError; end
|
|
94
94
|
|
|
95
|
+
# Raised when a sub-agent pauses to ask the user for input and the budget
|
|
96
|
+
# policy is +:raise+.
|
|
97
|
+
class InputRequiredError < StandardError; end
|
|
98
|
+
|
|
95
99
|
included do
|
|
96
100
|
# Contracts this agent exposes to callers, keyed by action.
|
|
97
101
|
class_attribute :delegation_contracts, instance_accessor: false, default: {}.freeze
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_agent/input_request"
|
|
4
|
+
|
|
5
|
+
module ActiveAgent
|
|
6
|
+
# Lets a generation pause for the user and continue with the answers.
|
|
7
|
+
#
|
|
8
|
+
# A tool asks by returning an {InputRequest}. The generation then ends
|
|
9
|
+
# paused, and {Generation#resume_now} continues it from the response's
|
|
10
|
+
# checkpoint. See {InputRequest} for the kinds of request and what an
|
|
11
|
+
# answer does.
|
|
12
|
+
#
|
|
13
|
+
# @example Asking for confirmation before a side effect
|
|
14
|
+
# class SupportAgent < ApplicationAgent
|
|
15
|
+
# on_input_request :notify_reviewer
|
|
16
|
+
#
|
|
17
|
+
# def issue_refund(order_id:, amount:)
|
|
18
|
+
# return ActiveAgent::InputRequest.confirm("Refund #{amount} on order #{order_id}?") unless input_answer
|
|
19
|
+
#
|
|
20
|
+
# Refund.create!(order_id:, amount:)
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# private
|
|
24
|
+
#
|
|
25
|
+
# def notify_reviewer(response)
|
|
26
|
+
# ReviewMailer.pending(response.input_requests.map(&:prompt)).deliver_later
|
|
27
|
+
# end
|
|
28
|
+
# end
|
|
29
|
+
module InputRequests
|
|
30
|
+
extend ActiveSupport::Concern
|
|
31
|
+
|
|
32
|
+
included do
|
|
33
|
+
define_callbacks :input_request
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class_methods do
|
|
37
|
+
# Registers callbacks run when a generation pauses for input, before the
|
|
38
|
+
# paused response is returned. A callback that takes an argument receives
|
|
39
|
+
# the paused response.
|
|
40
|
+
#
|
|
41
|
+
# @param names [Array<Symbol>] methods to call
|
|
42
|
+
# @param block [Proc] optional block to call
|
|
43
|
+
# @return [void]
|
|
44
|
+
def on_input_request(*names, &block)
|
|
45
|
+
_insert_callbacks(names, block) do |callback, options|
|
|
46
|
+
set_callback(:input_request, :before, ->(agent) { agent.send(:run_input_request_callback, callback) }, **options)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# The first callback a class sets makes ActiveSupport alias the chain's
|
|
50
|
+
# runner into that class as a public method, which would list it among
|
|
51
|
+
# the agent's actions and in its release digest.
|
|
52
|
+
private :_run_input_request_callbacks if public_method_defined?(:_run_input_request_callbacks, false)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Stops a pause of this generation from being announced with
|
|
57
|
+
# `input_requested.active_agent` or passed to on_input_request callbacks.
|
|
58
|
+
#
|
|
59
|
+
# @return [void]
|
|
60
|
+
# @api private
|
|
61
|
+
def silence_input_requests!
|
|
62
|
+
@_input_requests_silenced = true
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Whether this generation continues one that paused for input.
|
|
66
|
+
#
|
|
67
|
+
# @return [Boolean]
|
|
68
|
+
def resuming?
|
|
69
|
+
!@_input_request_resume.nil?
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Returns the answer for the tool call being run, when the call is being
|
|
73
|
+
# dispatched again on resume; nil otherwise.
|
|
74
|
+
#
|
|
75
|
+
# @return [Object, nil]
|
|
76
|
+
def input_answer
|
|
77
|
+
InputRequest.answer_for(InputRequest.current_tool_call_id)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Continues a generation that paused for input. The action has already
|
|
81
|
+
# run, so tools, instructions and options are current; the conversation
|
|
82
|
+
# is replaced with the checkpoint's, not merged with what the action
|
|
83
|
+
# added.
|
|
84
|
+
#
|
|
85
|
+
# Answers to `:secret` requests are scrubbed from every tool result and
|
|
86
|
+
# tool error of this generation, so they reach neither the model nor
|
|
87
|
+
# telemetry.
|
|
88
|
+
#
|
|
89
|
+
# @param checkpoint [Hash] the paused response's checkpoint
|
|
90
|
+
# @param answers [Hash] an answer per paused tool call id
|
|
91
|
+
# @return [ActiveAgent::Providers::Common::PromptResponse]
|
|
92
|
+
# @raise [InputRequest::ResumeError] before any tool runs or any request
|
|
93
|
+
# is sent, when the answers or the checkpoint do not fit
|
|
94
|
+
def resume_prompt(checkpoint:, answers:)
|
|
95
|
+
resume = InputRequest::Resume.new(checkpoint:, answers:)
|
|
96
|
+
resume.assert_action!(generation_action_name)
|
|
97
|
+
|
|
98
|
+
input_request_secrets.concat(resume.secret_answers)
|
|
99
|
+
prompt_options[:messages] = resume.messages
|
|
100
|
+
@_input_request_resume = resume
|
|
101
|
+
|
|
102
|
+
process_prompt
|
|
103
|
+
ensure
|
|
104
|
+
@_input_request_resume = nil
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
|
|
109
|
+
# Records the action the generation runs. A tool call runs through
|
|
110
|
+
# `process` as well, and replaces `action_name` with the tool's name.
|
|
111
|
+
def process(method_name, ...)
|
|
112
|
+
@_generation_action_name = method_name.to_s unless tool_call?
|
|
113
|
+
super
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# @return [String, nil] the action this agent's generation runs
|
|
117
|
+
def generation_action_name
|
|
118
|
+
@_generation_action_name
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# @return [Boolean] whether a pause is kept from the host
|
|
122
|
+
def input_requests_silenced?
|
|
123
|
+
@_input_requests_silenced == true
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# @return [Array<String>] the secret answers this generation has received
|
|
127
|
+
def input_request_secrets
|
|
128
|
+
@_input_request_secrets ||= []
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# @param value [Object]
|
|
132
|
+
# @return [Object] `value` without any secret answer in it
|
|
133
|
+
def scrub_input_request_secrets(value)
|
|
134
|
+
InputRequest.scrub(value, input_request_secrets)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# @param error [Exception]
|
|
138
|
+
# @raise [Exception] `error`, or a replacement without the secret answers
|
|
139
|
+
# (see InputRequest.raise_scrubbed)
|
|
140
|
+
def raise_without_input_request_secrets(error)
|
|
141
|
+
InputRequest.raise_scrubbed(error, input_request_secrets)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Calls one on_input_request callback, with the paused response when it
|
|
145
|
+
# takes an argument.
|
|
146
|
+
#
|
|
147
|
+
# @param callback [Symbol, Proc]
|
|
148
|
+
# @return [void]
|
|
149
|
+
def run_input_request_callback(callback)
|
|
150
|
+
if callback.is_a?(Proc)
|
|
151
|
+
callback.arity.zero? ? instance_exec(&callback) : instance_exec(@_input_request_response, &callback)
|
|
152
|
+
else
|
|
153
|
+
method(callback).arity.zero? ? send(callback) : send(callback, @_input_request_response)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Runs the on_input_request callbacks for a paused response.
|
|
158
|
+
#
|
|
159
|
+
# @param response [ActiveAgent::Providers::Common::PromptResponse]
|
|
160
|
+
# @return [void]
|
|
161
|
+
def run_input_request_callbacks(response)
|
|
162
|
+
return if input_requests_silenced?
|
|
163
|
+
return unless response.respond_to?(:awaiting_input?) && response.awaiting_input?
|
|
164
|
+
|
|
165
|
+
# `only:` and `except:` compare against action_name, which the tool
|
|
166
|
+
# calls before the pause replaced with their own names.
|
|
167
|
+
tool_action_name, self.action_name = action_name, generation_action_name
|
|
168
|
+
@_input_request_response = response
|
|
169
|
+
|
|
170
|
+
begin
|
|
171
|
+
run_callbacks(:input_request)
|
|
172
|
+
ensure
|
|
173
|
+
self.action_name = tool_action_name
|
|
174
|
+
@_input_request_response = nil
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
@@ -208,8 +208,9 @@ module ActiveAgent
|
|
|
208
208
|
#
|
|
209
209
|
# @param generation_method [Symbol, String] the generation method to call
|
|
210
210
|
# @param job_options [Hash] options to pass to the job (e.g., queue, priority)
|
|
211
|
+
# @param job_arguments [Hash] further keyword arguments for the job
|
|
211
212
|
# @return [Object] the enqueued job instance
|
|
212
|
-
def enqueue_generation(generation_method, job_options = {})
|
|
213
|
+
def enqueue_generation(generation_method, job_options = {}, **job_arguments)
|
|
213
214
|
if processed?
|
|
214
215
|
super
|
|
215
216
|
else
|
|
@@ -218,7 +219,7 @@ module ActiveAgent
|
|
|
218
219
|
# another machine authorizes as the same caller.
|
|
219
220
|
agent_class.generation_job.set(job_options).perform_later(
|
|
220
221
|
agent_class.name, action_name.to_s, generation_method.to_s,
|
|
221
|
-
params: @params, args: args, kwargs: kwargs, actor: @actor
|
|
222
|
+
params: @params, args: args, kwargs: kwargs, actor: @actor, **job_arguments
|
|
222
223
|
)
|
|
223
224
|
end
|
|
224
225
|
end
|
|
@@ -305,8 +306,9 @@ module ActiveAgent
|
|
|
305
306
|
#
|
|
306
307
|
# @param generation_method [Symbol, String] the generation method to call
|
|
307
308
|
# @param job_options [Hash] options to pass to the job
|
|
309
|
+
# @param job_arguments [Hash] further keyword arguments for the job
|
|
308
310
|
# @return [Object] the enqueued job instance
|
|
309
|
-
def enqueue_generation(generation_method, job_options = {})
|
|
311
|
+
def enqueue_generation(generation_method, job_options = {}, **job_arguments)
|
|
310
312
|
if processed?
|
|
311
313
|
super
|
|
312
314
|
else
|
|
@@ -320,7 +322,8 @@ module ActiveAgent
|
|
|
320
322
|
kwargs: kwargs,
|
|
321
323
|
direct_generation_type: @generation_type,
|
|
322
324
|
direct_args: @direct_args,
|
|
323
|
-
direct_options: @direct_options
|
|
325
|
+
direct_options: @direct_options,
|
|
326
|
+
**job_arguments
|
|
324
327
|
)
|
|
325
328
|
end
|
|
326
329
|
end
|
|
@@ -11,14 +11,19 @@ module ActiveAgent
|
|
|
11
11
|
# Returns a proc that handles tool/function calls from providers.
|
|
12
12
|
#
|
|
13
13
|
# The proc routes tool calls to the appropriate action method using
|
|
14
|
-
# the {#process} method.
|
|
14
|
+
# the {#process} method. Secret answers the generation received (see
|
|
15
|
+
# InputRequests) are scrubbed from the result and from any error, before
|
|
16
|
+
# either reaches the provider or telemetry.
|
|
15
17
|
#
|
|
16
18
|
# @return [Proc] callback proc that accepts (action_name, *args, **kwargs)
|
|
17
19
|
def tools_function
|
|
18
20
|
proc do |action_name, *args, **kwargs|
|
|
19
21
|
# Marked as a tool call so a refusal is reported to the model as a
|
|
20
22
|
# result rather than raised through the run (see Authorization).
|
|
21
|
-
with_tool_call { process(action_name, *args, **kwargs) }
|
|
23
|
+
result = with_tool_call { process(action_name, *args, **kwargs) }
|
|
24
|
+
scrub_input_request_secrets(result)
|
|
25
|
+
rescue StandardError => error
|
|
26
|
+
raise_without_input_request_secrets(error)
|
|
22
27
|
end
|
|
23
28
|
end
|
|
24
29
|
end
|
|
@@ -32,6 +32,8 @@ module ActiveAgent
|
|
|
32
32
|
# @return [Object] the sub-agent's result, or a structured error the
|
|
33
33
|
# calling model can act on
|
|
34
34
|
# @raise [BudgetExceededError] when the budget policy is +:raise+
|
|
35
|
+
# @raise [InputRequiredError] when the sub-agent pauses for input and
|
|
36
|
+
# the budget policy is +:raise+
|
|
35
37
|
# @raise [InvalidResultError] when the returns policy is +:raise+
|
|
36
38
|
def call(**arguments)
|
|
37
39
|
if (violation = budget_violation)
|
|
@@ -123,6 +125,9 @@ module ActiveAgent
|
|
|
123
125
|
definition.backend.apply(agent)
|
|
124
126
|
apply_returns_format(agent)
|
|
125
127
|
inherit_trace_id(agent)
|
|
128
|
+
# A pause becomes this call's result (see #input_required), and its
|
|
129
|
+
# checkpoint is dropped, so nothing outside the call may act on it.
|
|
130
|
+
agent.silence_input_requests!
|
|
126
131
|
|
|
127
132
|
agent.process_prompt
|
|
128
133
|
end
|
|
@@ -193,6 +198,8 @@ module ActiveAgent
|
|
|
193
198
|
# @param payload [Hash] instrumentation payload
|
|
194
199
|
# @return [Object]
|
|
195
200
|
def result(response, payload)
|
|
201
|
+
return input_required(response, payload) if response.try(:awaiting_input?)
|
|
202
|
+
|
|
196
203
|
message = response.message
|
|
197
204
|
return nil if message.nil?
|
|
198
205
|
|
|
@@ -251,6 +258,30 @@ module ActiveAgent
|
|
|
251
258
|
}
|
|
252
259
|
end
|
|
253
260
|
|
|
261
|
+
# A delegated agent cannot pause for the user: nothing holds its
|
|
262
|
+
# checkpoint once the tool call returns. Its questions go back to the
|
|
263
|
+
# calling model instead.
|
|
264
|
+
#
|
|
265
|
+
# @param response [ActiveAgent::Providers::Common::PromptResponse] a paused response
|
|
266
|
+
# @param payload [Hash] instrumentation payload
|
|
267
|
+
# @return [Hash]
|
|
268
|
+
# @raise [InputRequiredError] when the budget policy is +:raise+
|
|
269
|
+
def input_required(response, payload)
|
|
270
|
+
payload[:status] = :input_required
|
|
271
|
+
|
|
272
|
+
if budget.policy == :raise
|
|
273
|
+
raise InputRequiredError, "#{definition.agent_class}##{definition.action} stopped to ask the user for input " \
|
|
274
|
+
"(#{response.input_requests.map(&:tool_name).join(", ")}), which a delegated agent cannot do"
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
{
|
|
278
|
+
error: "input_required",
|
|
279
|
+
questions: response.input_requests.map(&:prompt),
|
|
280
|
+
message: "The #{definition.tool_name} delegation stopped to ask the user for input, which a delegated agent cannot do. " \
|
|
281
|
+
"Answer with the information you already have, or ask the user yourself."
|
|
282
|
+
}
|
|
283
|
+
end
|
|
284
|
+
|
|
254
285
|
# @param missing [Array<Symbol>]
|
|
255
286
|
# @param content [String]
|
|
256
287
|
# @return [Hash]
|