Telemetry, tracing, and usage metrics for SubAgent execution.
This module handles:
- Token accumulation across LLM calls
- Final usage statistics (duration, memory, turns, tokens)
- Trace entry construction with optional debug info
- Turn struct construction for execution history
- Trace filtering based on execution result
Summary
Functions
Accumulate tokens from an LLM call into state.
Apply trace filtering based on trace_mode and execution result.
Build final usage map with token counts from accumulated state.
Build a truncated preview of the result for telemetry metadata.
Build token measurements map for telemetry.
Build a Turn struct for the current execution cycle.
Build measurements for turn stop event with optional tokens.
Emit turn stop event immediately after a turn completes.
Estimate token count for a text string.
Extract program from the last turn in a result.
Functions
Accumulate tokens from an LLM call into state.
Parameters
state- Current loop statetokens- Token counts map with:input,:output,:cache_creation,:cache_readkeys, or nil
Returns
Updated state with accumulated token counts.
Apply trace filtering based on trace_mode and execution result.
Filter Modes
true- Always include tracefalse- Never include trace (returns nil):on_error- Include trace only when is_error is true
@spec build_final_usage(map(), non_neg_integer(), non_neg_integer(), integer()) :: map()
Build final usage map with token counts from accumulated state.
Parameters
state- Current loop state with accumulated metricsduration_ms- Total execution duration in millisecondsmemory_bytes- Memory used in bytesturn_offset- Offset for turn count (0 for completed turns, -1 for pre-turn failures)
Returns
Map with usage statistics including cache token metrics and compression stats when available.
Build a truncated preview of the result for telemetry metadata.
Truncates to 65536 characters.
Build token measurements map for telemetry.
Build a Turn struct for the current execution cycle.
Creates either a success or failure Turn based on the success? option.
Parameters
state- Current loop state (used for turn number and messages)raw_response- Full LLM response textprogram- PTC-Lisp program that was executed (or nil if parsing failed)result- Execution result or erroropts- Keyword options:success?- Whether this turn succeeded (default: true)prints- Captured println output (default: [])tool_calls- Tool invocations made during this turn (default: [])memory- Memory state after this turn (default: state.memory)type- Turn type::normal,:must_return, or:retry(default::normal)
Returns
A %Turn{} struct.
Build measurements for turn stop event with optional tokens.
@spec emit_turn_stop_immediate( PtcRunner.Turn.t() | nil, PtcRunner.SubAgent.t() | map(), map(), integer(), map() | nil ) :: :ok
Emit turn stop event immediately after a turn completes.
This is used by the iterative driver_loop to emit telemetry right after each turn, rather than batching events when the stack unwinds. Handles nil turn defensively for cases where LLM errors occur before a Turn struct is created.
Parameters
turn- The Turn struct for this turn, or nil if LLM error occurred before turn creationagent- The SubAgent structstate- Current loop stateturn_start- Monotonic timestamp when turn startedturn_tokens- Optional token counts from LLM call (overrides state.turn_tokens if provided)
@spec estimate_tokens(String.t() | nil) :: non_neg_integer()
Estimate token count for a text string.
Uses a simple approximation of ~4 characters per token, which is reasonably accurate for most LLM tokenizers (within ~10-20%).
Examples
iex> PtcRunner.SubAgent.Loop.Metrics.estimate_tokens("Hello world")
2
iex> PtcRunner.SubAgent.Loop.Metrics.estimate_tokens("")
0
iex> PtcRunner.SubAgent.Loop.Metrics.estimate_tokens(nil)
0
Extract program from the last turn in a result.
The result is {:ok, step} or {:error, step} for final results.
For continuation results (loop), this returns nil.
Note: step.turns is in chronological order (first turn first, last turn last).
Examples
iex> step = %PtcRunner.Step{turns: [%{program: "code"}]}
iex> PtcRunner.SubAgent.Loop.Metrics.extract_program_from_result({:ok, step})
"code"
iex> PtcRunner.SubAgent.Loop.Metrics.extract_program_from_result({:ok, %PtcRunner.Step{turns: []}})
nil
iex> PtcRunner.SubAgent.Loop.Metrics.extract_program_from_result({:error, :invalid})
nil