PtcRunner.LLM behaviour (PtcRunner v0.14.0)

Copy Markdown View Source

Provider-neutral LLM adapter boundary used by trusted Kernel provider builders.

callback/2 binds a configured full model identifier into a one-argument provider callback. Kernel policy, retries, prompt construction, and protocol recovery live in shipped Lisp libraries rather than this transport adapter.

Summary

Types

Provider-reported token usage. :total_cost is absent when pricing is unavailable; a present zero is a measured zero-cost response.

Callbacks

Make an LLM call.

Preload adapter-owned model metadata into a shared, process-independent store (e.g. a :persistent_term/ETS catalog) so the first per-request provider worker does not pay a large one-time load inside its bounded heap.

Prepares an adapter-owned request target and reports its catalog status.

Names the OTP application this adapter needs running to serve model.

Attests that the exact adapter target is safe to publish in canonical traces.

Stream an LLM response.

Functions

Returns the configured LLM adapter module.

Make a direct LLM call using the configured adapter.

Creates a normalized requester for a configured or prepared model.

Resolves a configured selector into an immutable adapter-owned request target.

Types

catalog_status()

@type catalog_status() :: :cataloged | :uncataloged | :unavailable

chunk()

@type chunk() :: %{delta: String.t()} | %{done: true, tokens: tokens()}

message()

@type message() :: %{role: :system | :user | :assistant | :tool, content: String.t()}

output_limit()

@type output_limit() :: %{
  name: :max_tokens,
  value: pos_integer(),
  bindings: [output_limit_binding()]
}

output_limit_binding()

@type output_limit_binding() ::
  :configured | :adapter_default | :model_output_limit | :remaining_context

response()

@type response() :: %{content: String.t(), tokens: tokens()}

tokens()

@type tokens() :: %{
  optional(:input) => non_neg_integer(),
  optional(:output) => non_neg_integer(),
  optional(:cache_creation) => non_neg_integer(),
  optional(:cache_read) => non_neg_integer(),
  optional(:total_cost) => float()
}

Provider-reported token usage. :total_cost is absent when pricing is unavailable; a present zero is a measured zero-cost response.

tool_call_response()

@type tool_call_response() :: %{
  :tool_calls => [map()],
  :content => String.t() | nil,
  :tokens => tokens(),
  optional(:finish_reason) => atom(),
  optional(:output_limit) => output_limit()
}

Callbacks

call(model, request)

@callback call(model :: term(), request :: map()) :: {:ok, map()} | {:error, term()}

Make an LLM call.

The request map contains:

  • :system - System prompt string
  • :messages - List of message maps
  • :schema - JSON Schema map (triggers structured output)
  • :tools - Tool definitions (triggers tool calling)
  • :cache - Boolean for prompt caching

Returns {:ok, response} or {:error, reason}.

ensure_ready()

(optional)
@callback ensure_ready() :: :ok

Preload adapter-owned model metadata into a shared, process-independent store (e.g. a :persistent_term/ETS catalog) so the first per-request provider worker does not pay a large one-time load inside its bounded heap.

Optional. Invoked during selected provider-application admission and again at capability-build time for direct embedding paths; implementations must be idempotent.

prepare_model(model)

(optional)
@callback prepare_model(model :: String.t()) ::
  {:ok, target :: term(), catalog_status()} | {:error, term()}

Prepares an adapter-owned request target and reports its catalog status.

Optional. Adapters without an external model catalog may omit this callback; PtcRunner then passes the original selector to requests and records the status as :unavailable.

provider_application(model)

(optional)
@callback provider_application(model :: String.t()) :: :req_llm | nil

Names the OTP application this adapter needs running to serve model.

Optional. An adapter backed by a dependency the core does not start returns that application's name so a host can report an unstarted provider application as a host misconfiguration rather than as a retryable transport failure. The answer is per model, because one adapter may route some models through a dependency and others straight over HTTP. Adapters with no such dependency omit the callback or return nil.

Constrained to :req_llm because that is the only backing application the installation catalog validates and the CLI's ownership selection understands. Admitting arbitrary applications means widening those two consumers as well, which is a deliberate change rather than a side effect of this callback.

public_model(model)

(optional)
@callback public_model(model :: String.t()) :: {:ok, String.t()} | :private

Attests that the exact adapter target is safe to publish in canonical traces.

Optional. Return {:ok, model} only when the complete value is public model identity. Return :private for local, endpoint-bearing, deployment-private, or otherwise sensitive targets. PtcRunner rejects altered, malformed, or oversized values and treats a missing or raising callback as private.

stream(model, request)

(optional)
@callback stream(model :: term(), request :: map()) ::
  {:ok, Enumerable.t()} | {:error, term()}

Stream an LLM response.

Returns {:ok, stream} where stream is an Enumerable of chunk maps:

  • %{delta: "text"} for content chunks
  • %{done: true, tokens: %{...}} for the final chunk

Functions

adapter!()

@spec adapter!() :: module()

Returns the configured LLM adapter module.

Resolution order:

  1. config :ptc_runner, :llm_adapter, MyAdapter
  2. PtcRunner.LLM.ReqLLMAdapter if req_llm is available
  3. Raises if no adapter found

call(model, request)

@spec call(String.t(), map()) :: {:ok, map()} | {:error, term()}

Make a direct LLM call using the configured adapter.

Examples

{:ok, response} = PtcRunner.LLM.call("amazon_bedrock:anthropic.claude-haiku-4-5-20251001-v1:0", %{
  system: "You are helpful.",
  messages: [%{role: :user, content: "Hello"}]
})

callback(model, opts \\ [])

@spec callback(
  String.t() | PtcRunner.LLM.PreparedModel.t(),
  keyword()
) :: {:ok, (map() -> {:ok, map()} | {:error, term()})} | {:error, term()}

Creates a normalized requester for a configured or prepared model.

A selector is prepared once while this function constructs the requester. A prepared value is bound directly. The optional :adapter setting selects a transport explicitly; other options are merged into every request.

Returns {:ok, requester} or an error before any request can run. An uncataloged selector emits one concise warning while the requester is built.

prepare(model, adapter \\ adapter!())

@spec prepare(String.t(), module()) ::
  {:ok, PtcRunner.LLM.PreparedModel.t()} | {:error, term()}

Resolves a configured selector into an immutable adapter-owned request target.

Preparation runs adapter warmup and model resolution once. It returns a typed error immediately when either operation cannot produce a valid prepared value.