PtcRunner.Lisp.Eval.Context (PtcRunner v0.14.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_concurrencybuild-time schedulers * 2Max concurrent pmap/pcalls tasks
parallel_deadline_capnilAbsolute ceiling clamping every pmap/pcalls deadline

Tool-ledger retention

effects.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 evaluation-local effects.tool_cache entry keep the full result (they are built separately in record_tool_call). :args is left intact by default because later effect consumers may need the raw map for capability identity and canonical argument hashing. A trusted tool may instead install an explicit ledger-only argument projection for sensitive boundaries. :child_trace_id/:child_step remain intact.

Summary

Types

Parallel map/calls execution record for tracing.

t()

Tool call record for tracing.

Functions

Appends a pmap/pcalls execution record to the context.

Appends a print message to the context.

Appends a tool call record to the context.

Returns the current evaluator origin, if any.

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

Returns true when a namespace was directly selected by the session.

Increments the iteration count and checks against the limit.

Copies the attached prelude tables and shared run-scoped resources from source onto context.

Merges new bindings into the environment.

Whether a prelude ref may be resolved from the current origin.

Saves the user namespace active before entering a prelude export.

Pushes a prelude-export origin for private tool authorization.

Pushes a prelude returned-closure origin without granting private tool authority.

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

Extracts accumulated side effects that must survive a recur jump.

Returns the namespace part of a qualified prelude ref, or nil.

Atomically reserves one uncached tool invocation against the program-wide tool-call limit.

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

Whether an origin is session-authored rather than a prelude export.

Returns the direct prelude ids that pulled a transitive namespace in.

Updates the user namespace in the context.

Types

parallel_effects()

@type parallel_effects() :: recur_effects()

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: Trace IDs supplied by nested tool 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() :: PtcRunner.Lisp.Eval.Effects.t()

t()

@type t() :: %PtcRunner.Lisp.Eval.Context{
  ctx: map(),
  data_grants: [String.t()] | nil,
  direct_namespaces: MapSet.t(String.t()),
  effects: PtcRunner.Lisp.Eval.Effects.t(),
  env: map(),
  failure_origin: :capability | nil,
  iteration_count: integer(),
  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,
  missing_data_params_message: String.t() | nil,
  origin_stack: [map()],
  parallel_budget: PtcRunner.Lisp.Eval.ParallelBudget.t() | nil,
  parallel_deadline_cap: integer() | nil,
  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_export_mask: %{required(String.t()) => MapSet.t(String.t())} | nil,
  prelude_exports: %{required(String.t()) => {term(), map()}},
  private_tool_authority?: boolean(),
  return_origin: :direct_tool_call | nil,
  strict_data: boolean(),
  strict_transitive_calls: boolean(),
  tool_activity: :atomics.atomics_ref(),
  tool_call_budget: :atomics.atomics_ref(),
  tool_exec: (String.t(), map(), map() | nil -> term()),
  tool_failure_token: reference() | nil,
  tools_meta: %{required(String.t()) => %{optional(atom()) => term()}},
  transitive_namespace_requirers: %{required(String.t()) => [String.t()]},
  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 a nested tool execution, when supplied

Functions

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.

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.

direct_namespace?(context, namespace)

@spec direct_namespace?(t(), String.t()) :: boolean()

Returns true when a namespace was directly selected by the session.

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 and shared run-scoped resources 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.

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)
  • :pmap_timeout - Shared absolute deadline in ms for each pmap/pcalls operation, including nested parallel calls (default: 5000). Increase for LLM-backed tools.
  • :parallel_deadline_cap - Absolute monotonic-time ceiling in ms that clamps every parallel deadline regardless of when the operation starts (default: nil = no ceiling). Kernel runs pass their run deadline here.
  • :pmap_max_concurrency - Max concurrent tasks in pmap/pcalls (default: the build-time System.schedulers_online() * 2, frozen into the semantic revision)
  • :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).

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, [], pmap_timeout: 60_000)
iex> ctx.pmap_timeout
60000

prelude_ref_visible?(context, ref)

@spec prelude_ref_visible?(t(), String.t()) :: boolean()

Whether a prelude ref may be resolved from the current origin.

Under strict_transitive_calls a session-authored program may name a namespace it selected directly, or one no direct selection pulled in, but not one reached only transitively through another prelude's requirements. Calls made from inside a prelude export are never restricted.

Both the evaluator's resolution guard and PtcRunner.Lisp.Introspection's visibility filter read this, so what a program can discover and what it can call cannot drift apart.

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_prelude_returned_origin(context, namespace)

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

Pushes a prelude returned-closure origin without granting private tool authority.

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.

ref_namespace(ref)

@spec ref_namespace(String.t()) :: String.t() | nil

Returns the namespace part of a qualified prelude ref, or nil.

reserve_tool_call(map)

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

Atomically reserves one uncached tool invocation against the program-wide tool-call limit.

Returns :ok when unlimited (nil) or when the reservation succeeds, {:error, :tool_call_limit_exceeded} when the shared limit is exhausted.

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.

session_authored_origin?(arg1)

@spec session_authored_origin?(map() | nil) :: boolean()

Whether an origin is session-authored rather than a prelude export.

transitive_namespace_requirers(context, namespace)

@spec transitive_namespace_requirers(t(), String.t()) :: [String.t()]

Returns the direct prelude ids that pulled a transitive namespace in.

update_user_ns(context, new_user_ns)

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

Updates the user namespace in the context.