Architecture¶
The core is small and depends only on pydantic. Everything else plugs in around it.
Layers¶
flowchart TB
APP["Your app<br/>functions · bound methods · partials · callable objects"]
subgraph CORE["core + schema · pydantic only · available"]
direction LR
REG["Registry<br/>@tool · wrap() · subset · catalogue"] --> SPEC["ToolSpec<br/>validate_args · call · invoke"]
REG --> SCH["schema<br/>check_portable · transforms"]
end
subgraph RUN["execution · available"]
direction LR
EX["Executor<br/>run · run_many · run_plan"] ~~~ FLOW["Flow spec<br/>local runner"] ~~~ PORTS["hooks · approval<br/>cost · cache · models"]
end
subgraph INT["integrations"]
direction LR
R3["OpenAI · Gemini · Anthropic<br/>renderers · available"] ~~~ BR["LangChain · MCP · OpenAPI · agent frameworks<br/>available"] ~~~ FW["LangGraph · ADK<br/>available"]
end
APP --> CORE
CORE --> INT
CORE --> RUN
RUN --> INT
Package map¶
| Path | Holds | Status |
|---|---|---|
core/tool.py |
ToolSpec, build_spec |
Available |
core/registry.py |
Registry: add, get, remove, subset, catalogue |
Available |
core/context.py |
ToolContext |
Available |
core/errors.py |
AdapterError and subclasses |
Available |
core/error_base.py, model_errors.py |
the AdapterError root and logging helpers; the ModelError family (both re-exported by core/errors.py) |
Available |
core/_signature.py, _docstring.py, _policy.py, _nulls.py |
signature → fields, docstrings, policy checks, strict nulls | Available (private) |
schema/portable.py |
check_portable |
Available |
schema/transforms.py |
to_openai_parameters, to_gemini_parameters, to_anthropic_input_schema |
Available |
integrations/openai.py, gemini.py, anthropic.py |
renderers | Available |
exec/, hooks/, approval/, runtime/ |
executor, hooks, approval port, run_sync |
Available |
cost/, cache/, models/ |
ledger, caches, model ports, InstrumentedModel |
Available |
flow/ |
flow spec, local runner | Available |
integrations/langchain.py, mcp/, openapi/, openai_agents.py, ... |
framework bridges and importers | Available |
integrations/langgraph/ |
LangGraph tool node, approval bridge, flow compiler | Available |
integrations/adk/ |
ADK tools, confirmation bridge, flow compiler | Available |
server/ |
HTTP tool server, WSGI app, server CLI | Available |
tracing/ |
events, OpenTelemetry | Available |
optimiser/ |
offline harness optimiser | Available |
Import rules¶
An AST test (tests/test_boundaries.py) enforces these rules on every run.
flowchart LR
BASE["core.errors<br/>core.context<br/>(base layer)"]
SCH["schema"]
REGM["core.registry"]
CORE["other core modules"]
INTM["integrations.x"]
FWK["framework SDK"]
SCH --> BASE
CORE --> BASE
REGM --> SCH
REGM --> CORE
INTM --> REGM
INTM --> CORE
INTM --> SCH
INTM --> BASE
INTM -. "lazy import only" .-> FWK
coreandschemanever importintegrationsor any framework. No dynamic imports.- Base-layer modules (
core.errors,core.context,core.types, ...) import only the base layer. The error modules import only each other:core.error_baseis the leaf. schemaimports only the base layer fromcore.core.registryis the onlycoremodule that importsschema.integrations.<x>imports its framework lazily: inside functions or underTYPE_CHECKING.
Read next¶
| Page | Shows |
|---|---|
| Tool lifecycle | function → registry → render → LLM → tool call → executor |
| Schema pipeline | portable check and per-provider rewrites |
| Executor and flows | the executor pipeline, fan-out, waves and the flow runner |
| HITL, cost and cache | how approval, the ledger, the caches and the model ports wire together |