Rho.ToolRunner (rho v0.1.0)

Copy Markdown View Source

Runs one tool call in its own task (under a Task.Supervisor, not linked to the loop). The before_tool_call gate runs first, inside the task, so a gate that waits (a biometric approval) never blocks the loop; once the gate allows, the task tells the loop ({:tool_running, pid}), which starts the tool's timeout clock from then.

The task's result is {:ok, text} or {:error, text}. A raise, exit or throw in the task (the gate's or the tool's) is formatted there (Rho.Untrusted.format/4) and ends the task with that text as its exit reason; the loop turns it, or any other abnormal end of the task (a kill, a linked process's exit), into an error result without running anything of the reason (Rho.Untrusted.exit_text/2). Before a tool runs, the registry's sources are told its task (Rho.ToolRegistry.watch/2), so a source of agent-written tools can count a crash or timeout (the loop kills it) against them.

A Dyn tool's answer (Rho.Untrusted.untrusted?/1) is made plain data in the task (Rho.Untrusted.plain/1: structs become maps, funs text) before it is encoded or leaves the task, so neither the loop nor its subscribers ever hold a value whose protocols run Dyn code.

A tool may answer with pictures for the model as well as text: {:ok, {:images, [{mime_type, bytes}], text}} (photos), or {:ok, {:image, mime_type, bytes, text}} for one (a screenshot).

And with data for the host's screens beside what the model reads (pi's tool result details): {:ok, {:details, output, details}}, where output is any of the above, or {:error, {:details, reason, details}} for a failure. The model and the session get output (or reason); details goes to the loop's subscribers, with tool_execution_end (Rho.Events), so a screen can draw the answer (a passage, a table) without parsing the text, and into the session only as far as the host's :keep_details (Rho.Loop) keeps it.

Summary

Functions

The default gate: everything is allowed.

The error text for a tool task that ended abnormally (its :DOWN reason), in the loop's process: nothing of the reason runs (Rho.Untrusted.exit_text/2); a crash the task formatted itself reads as that text.

Starts call (%{"arguments" => ...}) of module as a task under task_sup, not linked: gate first, then the tool, with ctx as the context Rho.Tool.run/2 gets. Once the gate allows, the task sends loop {:tool_running, pid}; it returns to_result/1 of the tool's answer. This is how Rho.Loop runs every call.

The error text for a call killed at module's timeout (Rho.Tool.timeout_ms/1).

A tool's return as the loop takes it: {:ok, text | images} or {:error, text}, or {:details, result, details} for a tool that answered with details (see the moduledoc).

Types

gate()

@type gate() :: (call :: map(), ctx :: map() -> :allow | {:block, String.t()})

images()

@type images() :: {:images, [{String.t(), binary()}], String.t()}

result()

@type result() :: {:ok, String.t() | images()} | {:error, String.t()}

Functions

allow_all(call, ctx)

@spec allow_all(map(), map()) :: :allow

The default gate: everything is allowed.

crash_text(reason)

@spec crash_text(term()) :: String.t()

The error text for a tool task that ended abnormally (its :DOWN reason), in the loop's process: nothing of the reason runs (Rho.Untrusted.exit_text/2); a crash the task formatted itself reads as that text.

start(task_sup, loop, module, call, gate, ctx)

@spec start(Supervisor.supervisor(), pid(), module(), map(), gate(), map()) ::
  Task.t()

Starts call (%{"arguments" => ...}) of module as a task under task_sup, not linked: gate first, then the tool, with ctx as the context Rho.Tool.run/2 gets. Once the gate allows, the task sends loop {:tool_running, pid}; it returns to_result/1 of the tool's answer. This is how Rho.Loop runs every call.

timeout_text(module)

@spec timeout_text(module()) :: String.t()

The error text for a call killed at module's timeout (Rho.Tool.timeout_ms/1).

to_result(other)

@spec to_result(term()) :: result() | {:details, result(), term()}

A tool's return as the loop takes it: {:ok, text | images} or {:error, text}, or {:details, result, details} for a tool that answered with details (see the moduledoc).