Skip to main content
Model-generated code can call your registered tools directly, collapsing multi-step tool pipelines into a single LLM turn.
The user asks for a multi-step task; the agent emits code that calls registered tools through the in-process bridge.

Quick Start

1

Enable Tool Bridge on an Agent

Register tools on the agent and enable code_tools in ExecutionConfig.
2

Low-Level: Call the Executor Directly

Use execute_code_with_tools when you need to run a script outside an agent.

Why Use Code Mode?

Before code mode, fetching 20 URLs required 20 LLM round-trips and 20 page bodies entering the context window. After code mode, the model emits one short script — best = min(extract_price(fetch(u)) for u in urls) — executed in a single turn. Only the final value returns to the model. Three wins:
  • Fewer LLM round-trips — one turn instead of N
  • Less context-window pollution — intermediate results never enter the model
  • Lower cost and latency — especially on tool-heavy pipelines

How It Works

Intermediate tool results stay inside the executor and never re-enter the model’s context.

When to Use Code Mode


Configuration Options

Both options live on ExecutionConfig alongside code_execution and code_mode.

Safety

Code mode is opt-in and requires an explicit allow-list. Tools not on the allow-list cannot be called from model-generated code, even if registered on the agent.
  • Opt-in onlycode_tools=False by default; no tools are exposed unless you set code_tools=True.
  • Explicit allow-list requiredcode_tools_allow=None (the default) exposes zero tools. You must name each tool.
  • Every call passes the approval gaterequire_approval, allow-lists, and PRAISON_AUTO_APPROVE env var all apply on every invocation.
  • Approval is not sticky — a single approval does not silently unlock later calls in the same script.
  • Disallowed tool → PermissionError — attempting to call a tool not on the allow-list raises PermissionError.
  • Unregistered tool → NameError — calling a name that isn’t registered raises NameError.
  • Reserved name "tools"ValueError — you cannot add "tools" to the allow-list; it is always the namespace object.
  • Imports and dangerous builtins remain blocked — AST + blocklist checks from execute_code still apply in code mode.
  • Two execution paths — code mode runs either in-process or isolated:
    • In-process (default) — no bridge=, same as today. The ToolProxy stores its registry and allow-list in a closure that in-process code cannot reach.
    • Isolated + tool-capable (opt-in) — supply a CodeToolBridge; the script runs under isolation and tool calls are serviced in the parent via serve_tool_call, gated by the same allow-list and approval gate. The parent never trusts the child — the allow-list, registry, timeout, and max output size are all forwarded to the bridge, so the transport is gated by the caller’s policy, not its own defaults.

Isolated Code with Tools (Bridge)

Run code mode inside a sandbox while still calling registered tools — same allow-list, same approval gate, no in-process execution. Supply a bridge= object that implements run_code. The transport calls serve_tool_call in the parent for every tool request marshalled from the isolated child — never a weaker path than the in-process proxy.
Concrete transports (subprocess/Docker/etc.) live in the sandbox layer as follow-ups; the contract above is all a bridge needs to satisfy.

Bridge Contract

The run_code method receives the caller’s policy as keyword-only params and returns a result dict.
bridge=None (the default) is fully backward-compatible — omit it to keep today’s in-process behaviour. Bridged calls pass through the same allow-list and require_approval gate, so isolation is never a weaker path. Disallowed tools raise PermissionError, unregistered tools raise NameError, and a denied approval raises PermissionError — exactly as in-process.

Common Patterns

Map/Reduce Over a URL List

Pipeline: Fetch → Parse → Filter → Summarize

Bare-Name vs Namespaced Calls

Inside the script, tools are callable two ways:

Best Practices

Only list the tools that the script genuinely needs. A smaller allow-list limits the blast radius if the model generates unexpected code.
Tools that write to databases, send emails, or delete records should require explicit approval. Configure require_approval=True or use a webhook approval backend before allow-listing them.
For two or three lightweight tool calls where the model needs to reason about each result, plain tool calling is simpler and easier to debug.
Imports in model-generated code are blocked by AST checks. Pre-register Python functions as tools so the model can call them instead of importing.

Sandbox

Secure isolated environments for code execution — pair with a CodeToolBridge to service tool calls from an isolated run.

Approval

Configure tool approval gates that apply on every call in code mode.

Allowed Tools

Environment-level and agent-level tool allow-lists.

Code Agent

AI agents that write and execute Python code using external interpreters.