Class: Phronomy::Agent::ToolInvocation Private

Inherits:
Object
  • Object
show all
Defined in:
lib/phronomy/agent/tool_invocation.rb

Overview

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

Mutable state for one concrete ToolCall.

The object owns Tool-domain state. FSMSession owns transitions, while ToolInvocationSessionBuilder converts Task completion into explicit Tool-internal events.

Defined Under Namespace

Classes: AuthorizationOutcome, ExecutionOutcome

Constant Summary collapse

PREFLIGHT_SETTLED_STATES =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

%i[
  authorized awaiting_approval rejected failed cancelled completed
].freeze
TERMINAL_STATES =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

%i[completed rejected failed cancelled].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(parent_agent_invocation_id:, agent:, tool:, tool_call:, config:, approval_policy: nil, approval_context: {}, id: SecureRandom.uuid) ⇒ ToolInvocation

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of ToolInvocation.



73
74
75
76
77
78
79
80
81
82
83
84
85
86
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
# File 'lib/phronomy/agent/tool_invocation.rb', line 73

def initialize(
  parent_agent_invocation_id:,
  agent:,
  tool:,
  tool_call:,
  config:,
  approval_policy: nil,
  approval_context: {},
  id: SecureRandom.uuid
)
  @id = id.to_s
  @parent_agent_invocation_id =
    parent_agent_invocation_id.to_s
  @agent = agent
  @tool = tool
  @tool_name = tool_call.name.to_s
  @tool_call_id =
    tool_call.respond_to?(:id) ? tool_call.id : nil
  raw_arguments =
    if tool_call.respond_to?(:arguments)
      tool_call.arguments || {}
    else
      {}
    end
  @raw_arguments = immutable_copy(raw_arguments)
  @config = config
  @approval_policy = approval_policy
  @approval_context =
    immutable_copy(approval_context || {})
  @origin =
    if tool&.respond_to?(:tool_origin)
      tool.tool_origin.to_sym
    else
      :local
    end
  @metadata =
    immutable_copy(
      tool&.respond_to?(:approval_metadata) ?
        tool. :
        {}
    )

  @arguments = nil
  @facts = {}.freeze
  @final_decision = nil
  @authorization_reason = nil
  @result = nil
  @error = nil
  @approval_consumed = false
  @status = :created
  @session_id = nil
  @phase = nil
end

Instance Attribute Details

#agentObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



34
35
36
# File 'lib/phronomy/agent/tool_invocation.rb', line 34

def agent
  @agent
end

#approval_contextObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



34
35
36
# File 'lib/phronomy/agent/tool_invocation.rb', line 34

def approval_context
  @approval_context
end

#approval_policyObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



34
35
36
# File 'lib/phronomy/agent/tool_invocation.rb', line 34

def approval_policy
  @approval_policy
end

#argumentsObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



34
35
36
# File 'lib/phronomy/agent/tool_invocation.rb', line 34

def arguments
  @arguments
end

#authorization_reasonObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



34
35
36
# File 'lib/phronomy/agent/tool_invocation.rb', line 34

def authorization_reason
  @authorization_reason
end

#configObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



34
35
36
# File 'lib/phronomy/agent/tool_invocation.rb', line 34

def config
  @config
end

#errorObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



34
35
36
# File 'lib/phronomy/agent/tool_invocation.rb', line 34

def error
  @error
end

#factsObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



34
35
36
# File 'lib/phronomy/agent/tool_invocation.rb', line 34

def facts
  @facts
end

#final_decisionObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



34
35
36
# File 'lib/phronomy/agent/tool_invocation.rb', line 34

def final_decision
  @final_decision
end

#idObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



34
35
36
# File 'lib/phronomy/agent/tool_invocation.rb', line 34

def id
  @id
end

#metadataObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



34
35
36
# File 'lib/phronomy/agent/tool_invocation.rb', line 34

def 
  @metadata
end

#originObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



34
35
36
# File 'lib/phronomy/agent/tool_invocation.rb', line 34

def origin
  @origin
end

#parent_agent_invocation_idObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



34
35
36
# File 'lib/phronomy/agent/tool_invocation.rb', line 34

def parent_agent_invocation_id
  @parent_agent_invocation_id
end

#phaseObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



34
35
36
# File 'lib/phronomy/agent/tool_invocation.rb', line 34

def phase
  @phase
end

#raw_argumentsObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



34
35
36
# File 'lib/phronomy/agent/tool_invocation.rb', line 34

def raw_arguments
  @raw_arguments
end

#resultObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



34
35
36
# File 'lib/phronomy/agent/tool_invocation.rb', line 34

def result
  @result
end

#session_idObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



34
35
36
# File 'lib/phronomy/agent/tool_invocation.rb', line 34

def session_id
  @session_id
end

#statusObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



34
35
36
# File 'lib/phronomy/agent/tool_invocation.rb', line 34

def status
  @status
end

#toolObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



34
35
36
# File 'lib/phronomy/agent/tool_invocation.rb', line 34

def tool
  @tool
end

#tool_call_idObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



34
35
36
# File 'lib/phronomy/agent/tool_invocation.rb', line 34

def tool_call_id
  @tool_call_id
end

#tool_nameObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



34
35
36
# File 'lib/phronomy/agent/tool_invocation.rb', line 34

def tool_name
  @tool_name
end

Class Method Details

.missing(parent_agent_invocation_id:, agent:, tool_call:, config: {}) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/phronomy/agent/tool_invocation.rb', line 56

def self.missing(
  parent_agent_invocation_id:,
  agent:,
  tool_call:,
  config: {}
)
  new(
    parent_agent_invocation_id: parent_agent_invocation_id,
    agent: agent,
    tool: nil,
    tool_call: tool_call,
    config: config
  ).tap do |invocation|
    invocation.send(:complete_missing_tool!)
  end
end

Instance Method Details

#apply_fsm_action_result(outcome) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Deprecated internal compatibility hook. FSMSession no longer calls this method; asynchronous results enter through explicit events.



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/phronomy/agent/tool_invocation.rb', line 162

def apply_fsm_action_result(outcome)
  event_type =
    case outcome
    when AuthorizationOutcome
      :authorization_completed
    when ExecutionOutcome
      :execution_completed
    else
      return self
    end
  handle_fsm_event(
    Phronomy::Event.new(
      type: event_type,
      target_id: @id,
      payload: outcome
    )
  )
  self
end

#authorization_task(runtime: Phronomy::Runtime.instance) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/phronomy/agent/tool_invocation.rb', line 215

def authorization_task(runtime: Phronomy::Runtime.instance)
  pool = runtime.pool(
    :authorization,
    size: Phronomy.configuration.authorization_pool_size,
    queue_size: Phronomy.configuration.authorization_queue_size
  )
  timeout = @config.fetch(
    :authorization_timeout,
    Phronomy.configuration.authorization_timeout
  )
  cancellation_token = @config[:cancellation_token]
  pending = pool.submit(
    timeout: timeout,
    cancellation_token: cancellation_token,
    on_full: :raise
  ) do
    evaluate_authorization
  end

  task = Phronomy::Task.deferred(
    name: "tool-authorization:#{@tool_name}"
  )
  pending.on_complete do |outcome, error|
    resolved =
      if error
        authorization_failure_outcome(error)
      else
        outcome
      end
    task.backend.unblock(resolved, nil)
    task.transition!(:completed, value: resolved)
  end
  task
rescue => error
  task = Phronomy::Task.deferred(
    name: "tool-authorization:#{@tool_name}"
  )
  outcome = authorization_failure_outcome(error)
  task.backend.unblock(outcome, nil)
  task.transition!(:completed, value: outcome)
  task
end

#authorized?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


360
361
362
# File 'lib/phronomy/agent/tool_invocation.rb', line 360

def authorized?
  @status == :authorized
end

#awaiting_approval?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


356
357
358
# File 'lib/phronomy/agent/tool_invocation.rb', line 356

def awaiting_approval?
  @status == :awaiting_approval
end

#cancelled?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


348
349
350
# File 'lib/phronomy/agent/tool_invocation.rb', line 348

def cancelled?
  @status == :cancelled
end

#dispatchable?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


376
377
378
379
380
# File 'lib/phronomy/agent/tool_invocation.rb', line 376

def dispatchable?
  return false unless @status == :queued

  @final_decision == :allow || @approval_consumed
end

#display_argumentsObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



388
389
390
# File 'lib/phronomy/agent/tool_invocation.rb', line 388

def display_arguments
  redact_for_display(@arguments || @raw_arguments)
end

#display_factsObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



392
393
394
395
# File 'lib/phronomy/agent/tool_invocation.rb', line 392

def display_facts
  sensitive_values = sensitive_argument_values
  redact_value(@facts, sensitive_values)
end

#execution_completed?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


364
365
366
# File 'lib/phronomy/agent/tool_invocation.rb', line 364

def execution_completed?
  @status == :completed
end

#execution_task(runtime: Phronomy::Runtime.instance) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
# File 'lib/phronomy/agent/tool_invocation.rb', line 258

def execution_task(runtime: Phronomy::Runtime.instance)
  pending =
    Phronomy::Agent::ToolExecutor.call_invocation_async(
      tool_invocation: self,
      cancellation_token: @config[:cancellation_token],
      config: @config,
      runtime: runtime
    )
  task = Phronomy::Task.deferred(
    name: "tool-execution:#{@tool_name}"
  )
  pending.on_complete do |result, error|
    outcome =
      if error
        ExecutionOutcome.new(
          error: error,
          cancelled: error.is_a?(
            Phronomy::CancellationError
          )
        )
      else
        ExecutionOutcome.new(result: result)
      end
    task.backend.unblock(outcome, nil)
    task.transition!(:completed, value: outcome)
  end
  task
rescue => error
  task = Phronomy::Task.deferred(
    name: "tool-execution:#{@tool_name}"
  )
  outcome = ExecutionOutcome.new(
    error: error,
    cancelled: error.is_a?(Phronomy::CancellationError)
  )
  task.backend.unblock(outcome, nil)
  task.transition!(:completed, value: outcome)
  task
end

#failed?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


344
345
346
# File 'lib/phronomy/agent/tool_invocation.rb', line 344

def failed?
  @status == :failed
end

#handle_fsm_event(event) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Applies Tool-internal asynchronous completion events on the EventLoop thread. The return value tells FSMSession that the event was consumed by the context before its declared transition is evaluated.



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/phronomy/agent/tool_invocation.rb', line 135

def handle_fsm_event(event)
  case event.type
  when :authorization_completed
    outcome = event.payload
    if outcome.is_a?(Exception)
      outcome =
        AuthorizationOutcome.new(error: outcome)
    end
    apply_authorization_outcome(outcome)
    true
  when :execution_completed
    outcome = event.payload
    if outcome.is_a?(Exception)
      outcome = ExecutionOutcome.new(
        error: outcome,
        cancelled: outcome.is_a?(Phronomy::CancellationError)
      )
    end
    apply_execution_outcome(outcome)
    true
  else
    false
  end
end

#mark_authorized!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



303
304
305
306
307
# File 'lib/phronomy/agent/tool_invocation.rb', line 303

def mark_authorized!
  @approval_consumed = true if @status == :awaiting_approval
  @status = :authorized
  self
end

#mark_awaiting_approval!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



298
299
300
301
# File 'lib/phronomy/agent/tool_invocation.rb', line 298

def mark_awaiting_approval!
  @status = :awaiting_approval
  self
end

#mark_cancelled!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



325
326
327
328
# File 'lib/phronomy/agent/tool_invocation.rb', line 325

def mark_cancelled!
  @status = :cancelled
  self
end

#mark_framework_failed!(error) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



330
331
332
333
334
# File 'lib/phronomy/agent/tool_invocation.rb', line 330

def mark_framework_failed!(error)
  @error = error
  @status = :failed
  self
end

#mark_queued!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



309
310
311
312
# File 'lib/phronomy/agent/tool_invocation.rb', line 309

def mark_queued!
  @status = :queued
  self
end

#mark_rejected!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



319
320
321
322
323
# File 'lib/phronomy/agent/tool_invocation.rb', line 319

def mark_rejected!
  @final_decision = :reject
  @status = :rejected
  self
end

#mark_running!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



314
315
316
317
# File 'lib/phronomy/agent/tool_invocation.rb', line 314

def mark_running!
  @status = :running
  self
end

#preflight_settled?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


368
369
370
# File 'lib/phronomy/agent/tool_invocation.rb', line 368

def preflight_settled?
  PREFLIGHT_SETTLED_STATES.include?(@status)
end

#rejected?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


352
353
354
# File 'lib/phronomy/agent/tool_invocation.rb', line 352

def rejected?
  @status == :rejected
end

#set_graph_metadata(thread_id: nil, phase: nil) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



127
128
129
130
# File 'lib/phronomy/agent/tool_invocation.rb', line 127

def (thread_id: nil, phase: nil)
  @session_id = thread_id if thread_id
  @phase = phase
end

#terminal?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


372
373
374
# File 'lib/phronomy/agent/tool_invocation.rb', line 372

def terminal?
  TERMINAL_STATES.include?(@status)
end

#tool_schemaObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



382
383
384
385
386
# File 'lib/phronomy/agent/tool_invocation.rb', line 382

def tool_schema
  @tool&.respond_to?(:params_schema) ?
    @tool.params_schema :
    {}
end

#validate!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/phronomy/agent/tool_invocation.rb', line 182

def validate!
  return self if terminal?

  validated, schema_error =
    if @tool.respond_to?(:validate_and_coerce, true)
      @tool.send(:validate_and_coerce, @raw_arguments)
    else
      [@raw_arguments, nil]
    end

  if schema_error
    if @tool.class.respond_to?(:on_schema_error) &&
        @tool.class.on_schema_error == :raise
      @error = Phronomy::ToolError.new(
        "#{@tool.class.name} schema error: #{schema_error}"
      )
      @status = :failed
    else
      @result = "Schema validation failed: #{schema_error}"
      @status = :completed
    end
    return self
  end

  @arguments = immutable_copy(validated || {})
  @status = :valid
  self
rescue => error
  @error = error
  @status = :failed
  self
end

#validation_completed?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


340
341
342
# File 'lib/phronomy/agent/tool_invocation.rb', line 340

def validation_completed?
  @status == :completed
end

#validation_passed?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


336
337
338
# File 'lib/phronomy/agent/tool_invocation.rb', line 336

def validation_passed?
  @status == :valid
end