Class: Phronomy::Tools::Agent
- Inherits:
-
Agent::Context::Capability::Base
- Object
- RubyLLM::Tool
- Agent::Context::Capability::Base
- Phronomy::Tools::Agent
- Defined in:
- lib/phronomy/tools/agent.rb
Overview
Wraps a Phronomy::Agent::Base subclass as a callable Tool.
Agent-backed Tools are logically asynchronous rather than offloaded synchronous operations. Their ToolInvocation starts the child Agent and then returns to EventLoop immediately; the Tool completion handle settles when the child Agent FSMSession finishes. An OffloadPool worker is therefore never consumed merely to wait for another Agent.
Class Method Summary collapse
Instance Method Summary collapse
-
#call_async(args, cancellation_token: nil, config: {}) ⇒ Object
Agent-backed Tools have an asynchronous implementation that does not use ToolExecutor/OffloadPool.
Methods inherited from Agent::Context::Capability::Base
approval_facts, #approval_metadata, #call, description, #execute, execution_mode, max_result_size, #name, on_error, on_schema_error, param, param_enums, param_schemas, parameters, #params_schema, params_schema_definition, provider_params, redact_params, requires_approval, #requires_approval, #requires_approval?, tool_name, #tool_origin
Class Method Details
.from_agent(agent_class, tool_name: nil, description: nil) ⇒ Object
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/phronomy/tools/agent.rb', line 18 def from_agent(agent_class, tool_name: nil, description: nil) raise ArgumentError, "agent_class must be a Class" unless agent_class.is_a?(Class) unless agent_class <= Phronomy::Agent::Base raise ArgumentError, "agent_class must inherit from Phronomy::Agent::Base" end # Fail at Tool definition time rather than on the first Tool call. agent_class.agent_definition klass = Class.new(self) effective_name = tool_name || derive_name(agent_class) effective_desc = description || "Delegates to #{agent_class.name || "an agent"}" klass.tool_name(effective_name) klass.description(effective_desc) # Preserve the synchronous Tool API for top-level callers. ToolInvocation # never uses this path for Agent-backed Tools; it calls #call_async. klass.define_method(:execute) do |input:, cancellation_token: nil| = {} if cancellation_token [:config] = {cancellation_token: cancellation_token} end result = Phronomy::Agent.run_once( definition: agent_class, input: input, ** ) result[:output].to_s end # Internal asynchronous execution protocol used by Agent#call_async. # The child Agent owns its own FSMSession/EventLoop lifecycle; this # method only returns its completion handle and performs a short map. klass.define_method(:execute_async) do |input:, cancellation_token: nil, config: {}| persistence = Phronomy::Persistence::InMemory.new agent = agent_class.create(persistence: persistence) task_config = (config || {}).dup if cancellation_token && !task_config[:cancellation_token] task_config[:cancellation_token] = cancellation_token end agent.invoke_async(input, config: task_config).map do |result| result[:output].to_s end end klass.send(:private, :execute_async) klass end |
Instance Method Details
#call_async(args, cancellation_token: nil, config: {}) ⇒ Object
Agent-backed Tools have an asynchronous implementation that does not use ToolExecutor/OffloadPool. Validation and Tool error policy still match Capability::Base#call.
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/phronomy/tools/agent.rb', line 87 def call_async( args, cancellation_token: nil, config: {} ) cancellation_token&.raise_if_cancelled! validated_args, schema_error = send(:validate_and_coerce, args) if schema_error return schema_error_task(schema_error) end source = execute_async( **(validated_args || {}), cancellation_token: cancellation_token, config: config || {} ) unless source.respond_to?(:on_complete) raise Phronomy::ToolError, "#{self.class.name} asynchronous execution must return a completion handle" end result_task = Phronomy::Task.deferred(name: "agent-tool-#{name}") source.on_complete do |result, error| if error settle_async_error(result_task, error) next end begin result_task.complete(send(:truncate_result_if_needed, result)) rescue => result_error settle_async_error(result_task, result_error) end end result_task rescue Phronomy::ToolError, Phronomy::CancellationError => error failed_task(error) rescue => error result_task = Phronomy::Task.deferred(name: "agent-tool-#{name}") settle_async_error(result_task, error) result_task end |