PtcRunner.Lisp (PtcRunner v0.4.1)
View SourceExecute PTC programs written in Lisp DSL (Clojure subset).
PTC-Lisp enables LLMs to write safe programs that orchestrate tools and transform data. Unlike raw code execution (Python, JavaScript), PTC-Lisp provides safety by design: no filesystem/network access, no unbounded recursion, and deterministic execution in isolated BEAM processes with resource limits.
See the PTC-Lisp Specification for the complete language reference.
Tool Registration
Tools are functions that receive a map of arguments and return results.
Note: tool names use kebab-case in Lisp (e.g., "get-user" not "get_user"):
tools = %{
"get-user" => fn %{"id" => id} -> MyApp.Users.get(id) end,
"search" => fn %{"query" => q} -> MyApp.Search.run(q) end
}
PtcRunner.Lisp.run(~S|(ctx/get-user {:id 123})|, tools: tools)Contract:
- Receives:
map()of arguments (may be empty%{}) - Returns: Any Elixir term (maps, lists, primitives)
- Should not raise (return
{:error, reason}for errors)
Summary
Functions
Format an error tuple into a human-readable string.
Useful for displaying errors to users or feeding back to LLMs for retry.
Examples
iex> PtcRunner.Lisp.format_error({:parse_error, "unexpected token"})
"Parse error: unexpected token"
iex> PtcRunner.Lisp.format_error({:eval_error, "undefined variable: x"})
"Eval error: undefined variable: x"
@spec run( String.t(), keyword() ) :: {:ok, PtcRunner.Step.t()} | {:error, PtcRunner.Step.t()}
Run a PTC-Lisp program.
Parameters
source: PTC-Lisp source code as a stringopts: Keyword list of options:context- Initial context map (default: %{}):memory- Initial memory map (default: %{}):tools- Map of tool names to functions (default: %{}):signature- Optional signature string for return value validation:float_precision- Number of decimal places for floats in result (default: nil = full precision):timeout- Timeout in milliseconds (default: 1000):max_heap- Max heap size in words (default: 1_250_000):max_symbols- Max unique symbols/keywords allowed (default: 10_000)
Return Value
On success, returns:
{:ok, Step.t()}with:step.return: The value returned to the callerstep.memory: Complete memory state after executionstep.usage: Execution metrics (duration_ms, memory_bytes)
On error, returns:
{:error, Step.t()}with:step.fail.reason: Error reason atomstep.fail.message: Human-readable error descriptionstep.memory: Memory state at time of error
Memory Contract
The memory contract is applied only at the top level (via apply_memory_contract/3):
- If result is not a map:
step.return= value, no memory update - If result is a map without
:return: merges map into memory, returns map asstep.return - If result is a map with
:return: merges remaining keys into memory, returns:returnvalue asstep.return
Related modules:
PtcRunner.SubAgent.Loop- Uses this contract to persist memory across turnsPtcRunner.Lisp.Eval- Evaluates programs with user_ns (memory) symbol resolution
Float Precision
When :float_precision is set, all floats in the result are rounded to that many decimal places.
This is useful for LLM-facing applications where excessive precision wastes tokens.
# Full precision (default)
{:ok, step} = PtcRunner.Lisp.run("(/ 10 3)")
step.return
#=> 3.3333333333333335
# Rounded to 2 decimals
{:ok, step} = PtcRunner.Lisp.run("(/ 10 3)", float_precision: 2)
step.return
#=> 3.33Resource Limits
Lisp programs execute with configurable timeout and memory limits:
PtcRunner.Lisp.run(source, timeout: 5000, max_heap: 5_000_000)Exceeding limits returns an error:
{:error, {:timeout, ms}}- execution exceeded timeout{:error, {:memory_exceeded, bytes}}- heap limit exceeded