Class: Ask::Agent::ToolCallRepair

Inherits:
Object
  • Object
show all
Defined in:
lib/ask/agent/tool_call_repair.rb

Overview

Repairs malformed tool calls before they execute.

When a model emits a tool call with unparseable arguments or an unknown tool name, execution currently fails and the model burns a turn seeing the error. Repair intercepts those calls before execution, asks the model to re-emit them corrected (one internal LLM round-trip), and executes the corrected versions — remapped to the original call ids so tool results stay consistent with the conversation history.

Enable with Session.new(tool_call_repair: true) for the built-in repair prompt, or pass a callable for full control:

Session.new(tool_call_repair: ->(chat, calls, tools) {
{ calls.keys.first => ToolCallInfo.new(
    id: calls.keys.first, name: "bash", arguments: "{}"
  ) }
})

The callable receives the chat, the malformed calls hash (id => ToolCallInfo), and the available tools; it returns a hash of corrections keyed by the ORIGINAL call ids. Calls without a correction are dropped — the model saw them in the prompt and declined to fix them.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(callable = nil) ⇒ ToolCallRepair

Returns a new instance of ToolCallRepair.

Parameters:

  • callable (Proc, nil) (defaults to: nil)

    custom repair function; nil uses the built-in repair prompt



53
54
55
# File 'lib/ask/agent/tool_call_repair.rb', line 53

def initialize(callable = nil)
  @callable = callable
end

Class Method Details

.repair_info(tool_call, tools) ⇒ String?

Returns why the call is repairable, or nil when it is well-formed and executable.

Parameters:

Returns:

  • (String, nil)

    why the call is repairable, or nil when it is well-formed and executable



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/ask/agent/tool_call_repair.rb', line 35

def self.repair_info(tool_call, tools)
  unless tools.any? { |t| t.respond_to?(:name) && t.name == tool_call.name }
    return "unknown tool '#{tool_call.name}'"
  end

  args = tool_call.arguments
  return nil if args.is_a?(Hash)

  parsed = JSON.parse(args.to_s)
  return nil if parsed.is_a?(Hash)

  "arguments must be a JSON object, got #{parsed.class}"
rescue JSON::ParserError => e
  "arguments are not valid JSON: #{e.message}"
end

Instance Method Details

#call(chat:, calls:, tools:) ⇒ Hash{String => ToolCallInfo}

Ask the model to correct the malformed calls.

Parameters:

  • chat (Ask::Agent::Chat)

    the session chat (history is restored after the internal round-trip)

  • calls (Hash{String => ToolCallInfo})

    malformed calls by id

  • tools (Array<Object>)

Returns:

  • (Hash{String => ToolCallInfo})

    corrections keyed by the original call ids



65
66
67
68
69
70
71
# File 'lib/ask/agent/tool_call_repair.rb', line 65

def call(chat:, calls:, tools:)
  if @callable
    normalize(@callable.call(chat, calls, tools))
  else
    built_in(chat, calls, tools)
  end
end