# `PtcRunner.SubAgent.Compiler`
[🔗](https://github.com/andreasronge/ptc_runner/blob/main/lib/ptc_runner/sub_agent/compiler.ex#L1)

Compilation logic for SubAgents.

This module provides the `compile/2` function that transforms a SubAgent into
a `CompiledAgent` by running it once with an LLM to derive the PTC-Lisp program.
The resulting CompiledAgent can then be executed many times without further LLM calls.

## SubAgentTools Support

Compiled agents can include SubAgentTools. The orchestrator's PTC-Lisp code is
deterministic (sequencing tool calls), while SubAgentTools execute their child
agents with an LLM at runtime.

When executing a compiled agent with SubAgentTools, pass the LLM at runtime:

    {:ok, compiled} = SubAgent.compile(orchestrator, llm: compile_llm)
    compiled.execute.(%{topic: "cats"}, llm: runtime_llm)

See `PtcRunner.SubAgent.compile/2` for the public API.

# `compile`

```elixir
@spec compile(
  PtcRunner.SubAgent.Definition.t(),
  keyword()
) :: {:ok, PtcRunner.SubAgent.CompiledAgent.t()} | {:error, PtcRunner.Step.t()}
```

Compiles a SubAgent into a reusable PTC-Lisp function.

The LLM is called once during compilation to derive the logic. The resulting
`CompiledAgent` can then be executed many times without further LLM calls,
making it efficient for processing many items with deterministic logic.

## Requirements

- `max_turns: 1` - Only single-shot agents can be compiled
- `output: :ptc_lisp` - Only PTC-Lisp output mode (not `:text`)

## Tool Support

- Pure Elixir tools - Supported, executed directly
- `LLMTool` - Supported (requires LLM at runtime)
- `SubAgentTool` - Supported if child agent has no `mission_timeout`

When SubAgentTools are present, the compiled agent requires an `llm` option
at execute time for the child agents.

## Options

- `llm` - Required. LLM callback used once during compilation. Can be a function or atom.
- `llm_registry` - Required if `llm` is an atom. Maps atoms to LLM callbacks.
- `sample` - Optional sample data to help LLM understand the input structure (default: %{})

## Returns

- `{:ok, CompiledAgent.t()}` - Successfully compiled agent
- `{:error, Step.t()}` - Compilation failed (agent execution failed)

## Examples

    iex> tools = %{"double" => fn %{"n" => n} -> n * 2 end}
    iex> agent = PtcRunner.SubAgent.new(
    ...>   prompt: "Double the input number {{n}}",
    ...>   signature: "(n :int) -> {result :int}",
    ...>   tools: tools,
    ...>   max_turns: 1
    ...> )
    iex> mock_llm = fn _ -> {:ok, ~S|(return {:result (tool/double {:n data/n})})|} end
    iex> {:ok, compiled} = PtcRunner.SubAgent.Compiler.compile(agent, llm: mock_llm, sample: %{n: 5})
    iex> compiled.signature
    "(n :int) -> {result :int}"
    iex> is_binary(compiled.source)
    true
    iex> is_function(compiled.execute, 2)
    true
    iex> result = compiled.execute.(%{n: 10}, [])
    iex> result.return["result"]
    20

Agents with LLMTool compile successfully (LLMTool requires LLM at runtime):

    iex> alias PtcRunner.SubAgent.LLMTool
    iex> tools = %{"classify" => LLMTool.new(prompt: "Classify {{x}}", signature: "(x :string) -> :string")}
    iex> agent = PtcRunner.SubAgent.new(prompt: "Process {{item}}", signature: "(item :string) -> {category :string}", tools: tools, max_turns: 1)
    iex> {:ok, compiled} = PtcRunner.SubAgent.Compiler.compile(agent, llm: fn _ -> {:ok, ~S|(return {:category "test"})|} end)
    iex> compiled.llm_required?
    true

---

*Consult [api-reference.md](api-reference.md) for complete listing*
