Runtimes¶
The toolregistry.runtimes subpackage provides the PTC (Programmatic Tool Calling) protocols and types. It allows LLMs to write Python code that calls tools programmatically, reducing round-trips and token consumption.
Zero toolregistry imports
This subpackage has no imports from toolregistry internals. It operates
exclusively on callables, dicts, and its own protocol types — same
constraint as executor/.
CodeResult¶
Structured result from code execution.
from toolregistry.runtimes import CodeResult
result = CodeResult(
stdout="42\n",
stderr="",
return_code=0,
error=None,
)
| Field | Type | Description |
|---|---|---|
stdout |
str |
Captured standard output |
stderr |
str |
Content written to stderr during execution |
return_code |
int |
0 = success, 1 = exception |
error |
str \| None |
Exception traceback, or None if clean exit |
CodeResult is a frozen dataclass — instances are immutable.
ToolProjection¶
Protocol defining how a tool appears inside a code runtime's namespace.
from toolregistry.runtimes import ToolProjection
class MyProjection:
@property
def name(self) -> str:
return "my_tool"
@property
def doc(self) -> str | None:
return "Does something useful."
def __call__(self, **kwargs):
return do_something(**kwargs)
assert isinstance(MyProjection(), ToolProjection) # True
| Member | Description |
|---|---|
name (property) |
Tool name in the code namespace |
doc (property) |
Docstring for introspection |
__call__(**kwargs) |
Invoke the tool synchronously |
DirectProjection¶
In-process ToolProjection implementation. Wraps a bare callable with zero overhead.
from toolregistry.runtimes import DirectProjection
def add(a: int, b: int) -> int:
return a + b
proj = DirectProjection(name="add", fn=add, doc="Add two numbers.")
proj(a=3, b=4) # → 7
# From a Tool object:
proj = DirectProjection(name=tool.name, fn=tool.fn, doc=tool.description)
- Sync callables are called directly.
- Async callables are dispatched via
asyncio.run().
asyncio.run() nesting
DirectProjection.__call__ uses asyncio.run() for async callables.
This cannot be called from within a running event loop. If your
CodeRuntime.execute() runs inside an event loop, it must handle
this — e.g. by running exec() in a separate thread.
CodeRuntime¶
Protocol for code execution engines. Accepts code strings with a tool namespace and returns structured results.
from toolregistry.runtimes import CodeRuntime, CodeResult, ToolProjection
class MyRuntime:
async def execute(
self,
code: str,
namespace: dict[str, ToolProjection],
*,
timeout: float | None = None,
extra_globals: dict[str, Any] | None = None,
) -> CodeResult:
# Execute code with tools available...
...
assert isinstance(MyRuntime(), CodeRuntime) # True
| Parameter | Type | Description |
|---|---|---|
code |
str |
Python source code to execute |
namespace |
dict[str, ToolProjection] |
Tools injected into execution namespace |
timeout |
float \| None |
Max wall-clock seconds. None = no limit |
extra_globals |
dict[str, Any] \| None |
Non-tool objects (imports, constants). Tool entries win on collision |
validate_namespace¶
Helper to check that namespace keys match their ToolProjection.name.
from toolregistry.runtimes import DirectProjection, validate_namespace
ns = {
"add": DirectProjection(name="add", fn=lambda a, b: a + b),
"mul": DirectProjection(name="mul", fn=lambda a, b: a * b),
}
validate_namespace(ns) # OK
ns_bad = {"wrong": DirectProjection(name="add", fn=lambda a, b: a + b)}
validate_namespace(ns_bad) # raises ValueError