PtcRunner.Lisp.Eval.Context (PtcRunner v0.13.0)

Copy Markdown View Source

Evaluation context for the Lisp interpreter.

Bundles the parameters that flow through recursive evaluation:

  • ctx: External data (read-only)
  • user_ns: User namespace (mutable bindings from def)
  • env: Lexical environment (variable bindings)
  • tool_exec: Tool executor function
  • turn_history: Previous turn results for multi-turn loops

Limits

FieldDefaultHard CapPurpose
loop_limit1,00010,000Max loop/recur jumps
max_print_length2,000Max chars per println call
max_tool_call_result_bytes16,384Per-entry cap on the :result retained in the in-eval tool ledger
pmap_max_concurrencyschedulers * 2Max concurrent pmap/pcalls tasks

Tool-ledger retention

tool_calls records every call's :result and :args for post-eval telemetry/envelope rendering. To stop a long-running or looping tool use (e.g. a paginated read fold) from accumulating full payloads in live eval state, append_tool_call/2 bounds each entry's :result to a preview once it exceeds max_tool_call_result_bytes, marking the entry with :result_truncated. Only the LEDGER copy is bounded — the value returned to the program and any tool_cache entry keep the full result (they are built separately in record_tool_call). :args is left intact (it is tiny in the fold case and TurnEvent.tool_call_summary/1 needs the raw map for upstream identity + the canonical args hash), as are :child_trace_id/:child_step.

Summary

Types

Discovery operation record for tracing.

Parallel map/calls execution record for tracing.

t()

Tool call record for tracing.

Trace context for nested agent execution tracing.

Functions

Appends a catalog operation record to the context.

Appends a pmap/pcalls execution record to the context.

Appends a print message to the context.

Appends a tool call record to the context.

Checks whether the tool call limit has been reached.

Returns the current evaluator origin, if any.

Returns the user namespace active before the current prelude export, if any.

Increments the iteration count and checks against the limit.

Copies the attached prelude tables (prelude_exports/prelude) from source onto context.

Merges new bindings into the environment.

Saves the user namespace active before entering a prelude export.

Pushes a prelude-export origin for private tool authorization.

Pushes a user-code origin that masks inherited prelude-export authority.

Extracts accumulated side effects that must survive a recur jump.

Restores side effects carried by a recur signal onto the next iteration context.

Updates the user namespace in the context.

Types

catalog_op()

@type catalog_op() :: %{
  operation: atom(),
  args: map(),
  outcome: :ok | :nil_world_fault | :error,
  reason: atom() | nil,
  duration_ms: non_neg_integer()
}

Discovery operation record for tracing.

Fields:

  • operation: Which discovery operation was called
  • args: Arguments passed to the operation
  • outcome: :ok, :nil_world_fault, or :error
  • reason: Reason for nil/error outcome (e.g., :catalog_cap_exhausted)
  • duration_ms: How long the operation took

pmap_call()

@type pmap_call() :: %{
  type: :pmap | :pcalls,
  count: non_neg_integer(),
  child_trace_ids: [String.t()],
  child_steps: [any()],
  timestamp: DateTime.t(),
  duration_ms: non_neg_integer(),
  success_count: non_neg_integer(),
  error_count: non_neg_integer()
}

Parallel map/calls execution record for tracing.

Fields:

  • type: :pmap or :pcalls
  • count: Number of parallel tasks
  • child_trace_ids: List of trace IDs from SubAgentTool executions
  • timestamp: When execution started
  • duration_ms: Total execution time
  • success_count: Number of successful executions
  • error_count: Number of failed executions

recur_effects()

@type recur_effects() :: %{
  prints: [String.t()],
  tool_calls: [tool_call()],
  pmap_calls: [pmap_call()],
  catalog_ops: [catalog_op()],
  tool_cache: map()
}

t()

@type t() :: %PtcRunner.Lisp.Eval.Context{
  budget: map() | nil,
  catalog_ops: [catalog_op()],
  ctx: map(),
  discovery_exec: (atom(), list() -> term()) | nil,
  env: map(),
  iteration_count: integer(),
  journal: map() | nil,
  locals: term(),
  loop_limit: integer(),
  max_heap: pos_integer() | nil,
  max_print_length: pos_integer(),
  max_tool_call_result_bytes: pos_integer(),
  max_tool_calls: pos_integer() | nil,
  origin_stack: [map()],
  parallel_budget: PtcRunner.Lisp.Eval.ParallelBudget.t() | nil,
  pmap_calls: [pmap_call()],
  pmap_deadline: integer() | nil,
  pmap_max_concurrency: pos_integer(),
  pmap_timeout: pos_integer(),
  prelude: PtcRunner.Lisp.Prelude.t() | nil,
  prelude_caller_user_ns_stack: [map()],
  prelude_exports: %{required(String.t()) => {term(), map()}},
  prints: [String.t()],
  strict_data: boolean(),
  summaries: %{required(String.t()) => String.t()},
  tool_cache: map(),
  tool_calls: [tool_call()],
  tool_exec: (String.t(), map(), map() | nil -> term()),
  tools_meta: %{required(String.t()) => %{cache: boolean()}},
  trace_context: trace_context(),
  turn_history: list(),
  user_ns: map(),
  worker_max_heap: pos_integer() | nil
}

tool_call()

@type tool_call() :: %{
  :name => String.t(),
  :args => map(),
  :result => term(),
  :error => String.t() | nil,
  :timestamp => DateTime.t(),
  :duration_ms => non_neg_integer(),
  optional(:child_trace_id) => String.t(),
  optional(:child_step) => term(),
  optional(:cached) => boolean()
}

Tool call record for tracing.

Fields:

  • name: Tool name
  • args: Arguments passed to tool
  • result: Tool result
  • error: Error message if tool failed
  • timestamp: When tool was called
  • duration_ms: How long tool took
  • child_trace_id: Trace ID of nested SubAgentTool execution (if any)

trace_context()

@type trace_context() ::
  %{
    trace_id: String.t(),
    parent_span_id: String.t() | nil,
    depth: non_neg_integer()
  }
  | nil

Trace context for nested agent execution tracing.

Fields:

  • trace_id: Unique identifier for this trace session
  • parent_span_id: Span ID of the parent operation (nil for root)
  • depth: Nesting depth for visualization

Functions

append_catalog_op(context, catalog_op)

@spec append_catalog_op(t(), catalog_op()) :: t()

Appends a catalog operation record to the context.

append_pmap_call(context, pmap_call)

@spec append_pmap_call(t(), pmap_call()) :: t()

Appends a pmap/pcalls execution record to the context.

append_print(context, message)

@spec append_print(t(), String.t()) :: t()

Appends a print message to the context.

Long messages are truncated to max_print_length characters (default: 2000).

append_tool_call(context, tool_call)

@spec append_tool_call(t(), tool_call()) :: t()

Appends a tool call record to the context.

The entry's :result and :args are bounded to a preview when they exceed max_tool_call_result_bytes, so a looping/large tool use cannot accumulate full payloads in live eval state. See the "Tool-ledger retention" moduledoc section. Only the ledger copy is bounded; callers keep the full result for the program return and cache separately.

check_tool_call_limit(map)

@spec check_tool_call_limit(t()) :: :ok | {:error, :tool_call_limit_exceeded}

Checks whether the tool call limit has been reached.

Returns :ok when unlimited (nil) or under the limit, {:error, :tool_call_limit_exceeded} when at or over.

current_origin(context)

@spec current_origin(t()) :: map() | nil

Returns the current evaluator origin, if any.

current_prelude_caller_user_ns(context)

@spec current_prelude_caller_user_ns(t()) :: map() | nil

Returns the user namespace active before the current prelude export, if any.

increment_iteration(context)

@spec increment_iteration(t()) :: {:ok, t()} | {:error, :loop_limit_exceeded}

Increments the iteration count and checks against the limit.

inherit_prelude(context, source)

@spec inherit_prelude(t(), t()) :: t()

Copies the attached prelude tables (prelude_exports/prelude) from source onto context.

Sub-contexts built with new/6 for closure/thunk evaluation start with empty prelude tables; this re-installs them so a qualified prelude call made from inside a user closure still resolves (Capability Prelude V1, plan §5).

merge_env(context, bindings)

@spec merge_env(t(), map()) :: t()

Merges new bindings into the environment.

new(ctx, user_ns, env, tool_exec, turn_history, opts \\ [])

@spec new(
  map(),
  map(),
  map(),
  (String.t(), map(), map() | nil -> term()),
  list(),
  keyword()
) :: t()

Creates a new evaluation context.

Options

  • :max_print_length - Max characters per println call (default: 2000)
  • :budget - Budget info map for (budget/remaining) introspection (default: nil)
  • :pmap_timeout - Timeout in ms for each pmap task (default: 5000). Increase for LLM-backed tools.
  • :pmap_max_concurrency - Max concurrent tasks in pmap/pcalls (default: System.schedulers_online() * 2)
  • :max_heap - Sandbox per-process heap cap in words (default: nil).
  • :worker_max_heap - FIXED max_heap_size (in words) for every pmap/pcalls worker, top-level and nested (default: the :max_heap value). Not divided by concurrency. See PtcRunner.Lisp.Eval.ParallelRunner.
  • :parallel_budget - shared PtcRunner.Lisp.Eval.ParallelBudget semaphore bounding the number of parallel workers alive at once across the whole run (default: nil = uncounted).
  • :trace_context - Trace context for nested agent tracing (default: nil)

Examples

iex> ctx = PtcRunner.Lisp.Eval.Context.new(%{}, %{}, %{}, fn _, _, _ -> nil end, [])
iex> ctx.user_ns
%{}

iex> ctx = PtcRunner.Lisp.Eval.Context.new(%{}, %{}, %{}, fn _, _, _ -> nil end, [], max_print_length: 500)
iex> ctx.max_print_length
500

iex> ctx = PtcRunner.Lisp.Eval.Context.new(%{}, %{}, %{}, fn _, _, _ -> nil end, [], budget: %{turns: 10})
iex> ctx.budget
%{turns: 10}

iex> ctx = PtcRunner.Lisp.Eval.Context.new(%{}, %{}, %{}, fn _, _, _ -> nil end, [], pmap_timeout: 60_000)
iex> ctx.pmap_timeout
60000

push_prelude_caller_user_ns(context, user_ns)

@spec push_prelude_caller_user_ns(t(), map()) :: t()

Saves the user namespace active before entering a prelude export.

push_prelude_origin(context, export)

@spec push_prelude_origin(t(), map()) :: t()

Pushes a prelude-export origin for private tool authorization.

push_user_origin(context)

@spec push_user_origin(t()) :: t()

Pushes a user-code origin that masks inherited prelude-export authority.

recur_effects(context)

@spec recur_effects(t()) :: recur_effects()

Extracts accumulated side effects that must survive a recur jump.

restore_recur_effects(context, effects)

@spec restore_recur_effects(t(), recur_effects()) :: t()

Restores side effects carried by a recur signal onto the next iteration context.

update_user_ns(context, new_user_ns)

@spec update_user_ns(t(), map()) :: t()

Updates the user namespace in the context.