Packages
Secure BEAM sandbox runtime for LLM code mode and MCP aggregation. Run concurrent LLM/tool clients safely while agents orchestrate approved tools, call upstream MCP/OpenAPI servers, and transform data.
Current section
Files
Jump to
Current section
Files
lib/ptc_runner/sub_agent/compression.ex
defmodule PtcRunner.SubAgent.Compression do
@moduledoc """
Behaviour for message history compression strategies.
Compression strategies transform turn history into LLM messages at render time.
This enables various prompt optimization techniques like coalescing multiple turns
into a single USER message.
## Strategy Pattern
Strategies implement `to_messages/3` which receives:
- `turns` - List of completed turns to compress
- `memory` - Accumulated definitions from all turns
- `opts` - Rendering options (mission, tools, data, limits, etc.)
## Configuration
Use `normalize/1` to handle the various compression option formats:
# Disabled (default)
normalize(nil) # => {nil, []}
normalize(false) # => {nil, []}
# Enabled with defaults
normalize(true) # => {SingleUserCoalesced, [println_limit: 15, tool_call_limit: 20]}
# Custom strategy or options
normalize(MyStrategy) # => {MyStrategy, [println_limit: 15, ...]}
normalize({MyStrategy, opts}) # => {MyStrategy, merged_opts}
"""
alias PtcRunner.Turn
@typedoc """
An LLM message with role and content.
"""
@type message :: %{role: :system | :user | :assistant, content: String.t()}
@typedoc """
Options passed to compression strategies.
- `mission` - The agent's mission/prompt text
- `system_prompt` - Static system prompt content
- `tools` - Map of available tools
- `data` - Input data provided to the agent
- `println_limit` - Max println calls to include (default: 15)
- `tool_call_limit` - Max tool calls to include (default: 20)
- `turns_left` - Remaining turns for the agent
"""
@type opts :: [
prompt: String.t(),
system_prompt: String.t(),
tools: map(),
data: map(),
println_limit: non_neg_integer(),
tool_call_limit: non_neg_integer(),
turns_left: non_neg_integer()
]
@typedoc """
Statistics about what compression did.
Returned alongside messages to report exactly what was dropped or collapsed.
"""
@type stats :: %{
enabled: boolean(),
strategy: String.t(),
turns_compressed: non_neg_integer(),
tool_calls_total: non_neg_integer(),
tool_calls_shown: non_neg_integer(),
tool_calls_dropped: non_neg_integer(),
printlns_total: non_neg_integer(),
printlns_shown: non_neg_integer(),
printlns_dropped: non_neg_integer(),
error_turns_collapsed: non_neg_integer()
}
@doc "Human-readable name for this compression strategy."
@callback name() :: String.t()
@doc """
Render turns into LLM messages with compression statistics.
Returns a tuple of `{messages, stats}` where stats reports exactly what
the compression did (items dropped, errors collapsed, etc.).
Compression is a pure function - same inputs always produce the same output.
Turn count is derived from `length(turns)`, not message count.
"""
@callback to_messages(
turns :: [Turn.t()],
memory :: map(),
opts :: opts()
) :: {[message()], stats()}
# Default strategy module - will be implemented in issue #620
@default_strategy PtcRunner.SubAgent.Compression.SingleUserCoalesced
@default_opts [println_limit: 15, tool_call_limit: 20]
@doc """
Normalize compression configuration into `{strategy, opts}` tuple.
Handles various configuration formats:
- `nil` or `false` - Compression disabled, returns `{nil, []}`
- `true` - Use default strategy with default options
- `Module` - Use custom strategy with default options
- `{Module, opts}` - Use custom strategy with merged options
## Examples
iex> PtcRunner.SubAgent.Compression.normalize(nil)
{nil, []}
iex> PtcRunner.SubAgent.Compression.normalize(false)
{nil, []}
iex> {strategy, opts} = PtcRunner.SubAgent.Compression.normalize(true)
iex> strategy
PtcRunner.SubAgent.Compression.SingleUserCoalesced
iex> opts[:println_limit]
15
iex> opts[:tool_call_limit]
20
iex> {strategy, _opts} = PtcRunner.SubAgent.Compression.normalize(SomeStrategy)
iex> strategy
SomeStrategy
iex> {strategy, opts} = PtcRunner.SubAgent.Compression.normalize({SomeStrategy, println_limit: 5})
iex> strategy
SomeStrategy
iex> opts[:println_limit]
5
iex> opts[:tool_call_limit]
20
"""
@spec normalize(boolean() | module() | {module(), keyword()} | nil) ::
{module() | nil, keyword()}
def normalize(nil), do: {nil, []}
def normalize(false), do: {nil, []}
def normalize(true), do: {@default_strategy, @default_opts}
def normalize(module) when is_atom(module) do
{module, @default_opts}
end
def normalize({module, opts}) when is_atom(module) and is_list(opts) do
{module, Keyword.merge(@default_opts, opts)}
end
@doc """
Returns the default compression options.
## Examples
iex> PtcRunner.SubAgent.Compression.default_opts()
[println_limit: 15, tool_call_limit: 20]
"""
@spec default_opts() :: keyword()
def default_opts, do: @default_opts
end