PtcRunner.SubAgent.Loop.PtcToolCall (PtcRunner v0.12.0)

Copy Markdown View Source

Native tool-call transport handler for ptc_transport: :tool_call agents.

Owns the loop branch that consumes assistant turns shaped as native tool_calls (the lisp_eval invocation) plus the direct final-answer path. App tools are never exposed as provider-native tools — they remain callable only from inside a PTC-Lisp program via (tool/name ...). The system prompt continues to render the full app-tool inventory.

See Plans/ptc-lisp-tool-call-transport.md for the full design.

Naming

In this module, "tool call" without qualifier refers to a native tool call (the lisp_eval invocation on the provider wire). PTC-Lisp (tool/...) invocations continue to be called "app tool calls" and surface as lisp_step.tool_calls.

Public surface

Summary

Functions

Handle an assistant turn under ptc_transport: :tool_call.

Build the request tools list for an agent.

The canonical description string for the lisp_eval tool in v1 PTC :tool_call mode.

The reserved native tool name ("lisp_eval").

Build the OpenAI-format tool schema for lisp_eval.

Functions

handle_response(response, agent, state)

@spec handle_response(
  map(),
  PtcRunner.SubAgent.Definition.t(),
  PtcRunner.SubAgent.Loop.State.t()
) ::
  {:continue, PtcRunner.SubAgent.Loop.State.t(), PtcRunner.Turn.t()}
  | {:stop, {:ok | :error, PtcRunner.Step.t()}, PtcRunner.Turn.t() | nil,
     map() | nil}

Handle an assistant turn under ptc_transport: :tool_call.

Returns the same signal as content-mode response handling:

  • {:continue, new_state, turn} for non-terminating turns.
  • {:stop, {:ok | :error, step}, turn, turn_tokens} to terminate.

Branches:

  • No native tool calls: treat direct content as the final answer (signature handling matrix). Markdown-fenced clojure as content triggers targeted feedback (R16).
  • Exactly one lisp_eval call: execute, append paired role: :tool message, continue or terminate on (return)/(fail).
  • One unknown tool call: paired unknown_tool error, continue.
  • More than one native tool call: paired multiple_tool_calls error per tool_call_id, continue (R12, R13).

request_tools(arg1)

@spec request_tools(PtcRunner.SubAgent.Definition.t()) :: [map()] | nil

Build the request tools list for an agent.

In :tool_call mode, returns exactly one entry — the lisp_eval schema — regardless of how many app tools the agent declares. App tools stay in the system prompt's Tool Inventory and are callable only from inside the sandboxed program.

In :content mode, returns nil so the request omits the tools field (matching today's behavior where PTC-Lisp app tools are not exposed as native provider tools).

tool_description()

@spec tool_description() :: String.t()

The canonical description string for the lisp_eval tool in v1 PTC :tool_call mode.

Delegates to PtcRunner.PtcToolProtocol.tool_description/1 with the :in_process_with_app_tools profile. Tests assert stable substrings against this value; do not paraphrase the guidance elsewhere.

tool_name()

@spec tool_name() :: String.t()

The reserved native tool name ("lisp_eval").

tool_schema()

@spec tool_schema() :: map()

Build the OpenAI-format tool schema for lisp_eval.

Returns a single map. The intended use in :tool_call mode is to put exactly this one entry in the LLM request's tools field — app tools are never included.

Examples

iex> schema = PtcRunner.SubAgent.Loop.PtcToolCall.tool_schema()
iex> schema["type"]
"function"
iex> schema["function"]["name"]
"lisp_eval"
iex> schema["function"]["parameters"]["required"]
["program"]