loom.ai.compiler¶
Agent compiler: offline, multi-error compilation of authored artifacts.
Turns a decoded AgentSpecV1 into an immutable
AgentPlan — the only artifact-derived input every downstream stage
reads (FR-014). Compilation is fully offline: no network, no credentials, no
entry-point loading, and every problem found across every phase (and every
spec, in AgentCompiler.compile_all()) is reported at once through a
single AgentCompilationError with stable
AgentErrorCode values.
Example
>>> compiler = AgentCompiler(
... config=ai_config, registry=registry, supported_kinds=kinds
... )
>>> plan = compiler.compile(spec, source_path="agents/triage.agent.yaml")
- class loom.ai.compiler.AgentCompiler(*, config, registry, supported_kinds, sql=None, native_tools=None)[source]¶
Bases:
objectCompiles authored agent artifacts into immutable plans.
Runs every validation phase over every spec, accumulates all the issues found and raises a single
AgentCompilationError, so a generator sees the whole picture at once (FR-011, SC-003).- Parameters:
config (AiConfig) – Deployment configuration of the AI pillar.
registry (UseCaseRegistry) – Use-case registry the
usecasegrants resolve against.supported_kinds (frozenset[str]) – Capability kinds the configured engine serves, resolved by the bootstrap and passed as a plain value — the compiler never imports an engine.
sql (SqlConfig | None) – Data-layer configuration;
Nonefails everysqlgrant withSQL_CONFIG_MISSINGinstead of skipping silently.native_tools (NativeToolSupport | None) – Oracle answering which provider tools a model binding admits; resolved from the engine by the bootstrap, never imported here.
- compile(spec, *, source_path=None)[source]¶
Validate one spec statically and return its immutable plan.
Performs no network access, requires no model credentials and spends no tokens.
- Parameters:
spec (AgentSpecV1) – Decoded artifact to compile.
source_path (str | None) – Artifact provenance; when given, every issue points at it as its
component.
- Returns:
The compiled plan.
- Raises:
AgentCompilationError – Aggregating one issue per problem found.
- Return type:
- compile_all(specs)[source]¶
Compile a whole application, accumulating issues across specs.
Duplicate agent names are an application-level fault: a single spec is always unique by itself, so
AGENT_NAME_DUPLICATEcan only be detected — and is only reported — here.- Parameters:
specs (Sequence[AgentSpecV1 | DecodedSpec]) – Decoded artifacts of the application. Passing the
DecodedSpecvalues as returned byload_specskeeps each artifact’s path, which a./skill library resolves against; a bareAgentSpecV1has no path.- Returns:
One plan per spec, in input order.
- Raises:
AgentCompilationError – Aggregating every issue of every spec.
- Return type:
- class loom.ai.compiler.AgentPlan(*, name, description, instructions, state=None, spec_version, inference, price=None, output, output_check=None, capabilities=(), policies, on_output=None, conversation=None, metadata, source_path=None)[source]¶
Bases:
LoomFrozenStructImmutable compiled agent, the only input to every downstream stage.
- Parameters:
name (str)
description (str)
instructions (tuple[CompiledInstruction, ...])
state (StateShape | None)
spec_version (int)
inference (InferenceTarget)
price (ModelPrice | None)
output (CompiledOutput)
output_check (Callable[[Mapping[str, Any]], str | None] | None)
capabilities (tuple[CompiledUsecaseCapability | CompiledSqlCapability | CompiledMcpCapability | CompiledSkillsCapability | CompiledPythonCapability | CompiledA2ACapability | CompiledNativeCapability, ...])
policies (PolicySpec)
on_output (CompiledOutputHook | None)
conversation (CompiledConversation | None)
source_path (str | None)
- instructions¶
Instruction blocks the agent follows, in authored order; never published.
- state¶
Shape of the artifact’s declared state, or
Nonewhen the artifact declares neitherdeps_typenordeps_schema.- Type:
loom.ai.abc.StateShape | None
- inference¶
Resolved model binding; one binding, no fallback (FR-019a).
- price¶
Configured price of the bound model, from
ai.prices, orNonewhen the deployment declares none for it.- Type:
loom.ai.pricing.ModelPrice | None
- output¶
Structured-output contract with its compiled boundary type.
- output_check¶
Resolved predicate over the answer the engine parsed, when the artifact declares
output_check;Noneotherwise.- Type:
collections.abc.Callable[[collections.abc.Mapping[str, Any]], str | None] | None
- capabilities¶
Compiled capabilities with resolved handles.
- Type:
tuple[loom.ai.compiler._plan.CompiledUsecaseCapability | loom.ai.compiler._plan.CompiledSqlCapability | loom.ai.compiler._plan.CompiledMcpCapability | loom.ai.compiler._plan.CompiledSkillsCapability | loom.ai.compiler._plan.CompiledPythonCapability | loom.ai.compiler._plan.CompiledA2ACapability | loom.ai.compiler._plan.CompiledNativeCapability, …]
- policies¶
Validated execution limits.
- on_output¶
Output hook, when the artifact declares one.
- Type:
- conversation¶
Conversation loader, when the artifact declares one.
- Type:
- metadata¶
Free-form string labels carried alongside the agent.
- Type:
- property fingerprint: str¶
Stable sha256 of the declared facts that decide this agent’s answers.
Covers the format version, the instructions as compiled, the output schema, the output check, the kind and declared facts of every capability (use case keys, SQL connection, MCP server, skill library and names, Python factory reference, A2A agent, native tool, and the
include/excludefilters of MCP and A2A) and the bound provider, model, output mode and options. It covers what the plan declares, not the content of the skills, tools or use cases it names: editing a skill’s text or a tool’s code keeps the fingerprint. Leaves out the name, description, metadata, provenance, configured price, every policy (each bounds a run and fails it past that bound;max_history_bytesalso withholds from the hook a history above it; none decides the content of an accepted answer), where a capability is served from (URL, directory, credentials, timeouts) and the binding’s region, endpoint, credentials and streaming flag. The output check enters by its import path, so editing the check’s body keeps the fingerprint.
- class loom.ai.compiler.CompiledA2ACapability(*, agent, url, headers_ref=None, auth=None, include=(), exclude=())[source]¶
Bases:
LoomFrozenStructRemote-agent grant, resolved against
ai.a2a_agents.The card is fetched in
__aenter__and the filter applied against the skills it really advertises — one of the declared exceptions to “strings die at compile” (invariant 3).- Parameters:
- headers_ref¶
Reference to deployment-resolved headers; never a secret. Applied to every request the client makes, the card fetch included.
- Type:
str | None
- auth¶
Named authentication strategy, mutually exclusive with
headers_ref;Nonewhen the agent needs no credential.- Type:
- class loom.ai.compiler.CompiledNativeCapability(*, tool)[source]¶
Bases:
LoomFrozenStructProvider tool granted to an agent, already checked against its model.
The plan carries the stable loom name and not the engine’s class: the compiler never imports an engine, and the name was validated against the binding before reaching here.
- Parameters:
tool (str)
- class loom.ai.compiler.CompiledConversation(*, usecase, use_case, accepted)[source]¶
Bases:
LoomFrozenStructUse case executed before a run that carries a
conversation_id.The compiler proves that every required, user-supplied name of the use case’s Input is one of
CONVERSATION_CONTEXT_FIELDSand that the Input declaresconversation_id, so the loader always knows which conversation to load.acceptedis the run-time filter, computed once asCompiledOutputHook.acceptedis.
- class loom.ai.compiler.CompiledInstruction(*, text, name=None, template=None)[source]¶
Bases:
LoomFrozenStructOne instruction block in authored order, projected onto the plan.
A bare-string artifact and a one-block artifact compile to the same single-element tuple, so nothing downstream branches on which of the two forms an artifact declared (FR-003’s instruction-side counterpart).
- class loom.ai.compiler.CompiledMcpCapability(*, server, transport='http', url=None, headers_ref=None, auth=None, timeout_ms=20000, command=None, args=(), env=(), include=(), exclude=())[source]¶
Bases:
LoomFrozenStructMCP server grant, resolved against
ai.mcp_servers.The artifact names a server; the plan carries the resolved handle — the transport with its address or command, the credential reference and the deadline — so nothing downstream re-reads configuration. The URL or the command resolves in
__aenter__, over the network or by spawning a subprocess: it is one of the declared exceptions to “strings die at compile” (invariant 3), and so is the filter, which is applied against the server’s real tool list.Configuration already proved the fields coherent with the transport, so
urlis set exactly underhttpandcommandexactly understdio; the engine narrows by transport, never by field.- Parameters:
- auth¶
Named authentication strategy, mutually exclusive with
headers_ref;Nonewhen the server needs no credential.- Type:
- env¶
Environment handed to the subprocess as key-sorted pairs, so the plan stays a hashable value whatever order the operator wrote.
- class loom.ai.compiler.CompiledOutput(*, schema, loom_type)[source]¶
Bases:
LoomFrozenStructStructured-output contract with a
LoomTypebuilt at compile time.Interpreting the schema per response would be per-item reflection, so the boundary type is constructed exactly once, at compile (research R-004, invariant 5). For a
msgspec.Structoutput the decode is strict — unknown fields are rejected, which is what makes returning the validated bytes unchanged safe. For a pydantic output invariant 5 is satisfied differently (D7, FR-012):.typeis handed to pydantic-ai itself as the run’soutput_type, so pydantic-ai — not loom — validates, retries and builds the instance; loom never decodes that answer a second time.- schema¶
JSON Schema object handed to the model.
- Type:
collections.abc.Mapping[str, Any]
- loom_type¶
Boundary type produced at compile, whichever library built it (
loom_type()for atype_ref,msgspec_type()for ajson_schema): the start-up marker check (ai/_startup.py) reads.type. The engine reads it differently per library: for amsgspec.Struct,ai/engines/pydantic_ai/_output.pydecodes the model’s raw bytes through.decode_json(bytes); for a pydantic model,.typereachesAgent.from_spec(output_type=...)and the engine reads the validated answer straight offresult.output..to_builtinsprojects either answer to JSON-mode builtins once, at the wire.
- class loom.ai.compiler.CompiledOutputHook(*, usecase, use_case, accepted)[source]¶
Bases:
LoomFrozenStructUse case executed once per completed run, resolved and proven feedable.
The compiler proves that every required, user-supplied name of the use case’s Input is one of
HOOK_OUTPUT_FIELDorHOOK_CONTEXT_FIELDS, so the runtime never discovers a missing field at the end of a run.acceptedis computed here, once, frommsgspec.structs.fields: the runtime filters the offered dict to it beforefrom_payloadso a strict Command works without per-run reflection.- use_case¶
Registered use-case type, as
CompiledUsecaseCapability.use_casescarries them.- Type:
type[loom.core.engine.compilable.Compilable]
- class loom.ai.compiler.CompiledPythonCapability(*, factory_ref, factory, params=<factory>)[source]¶
Bases:
LoomFrozenStructApplication toolset factory resolved to an importable callable.
- factory¶
Imported factory, called once at build as
factory(context, **params)with aToolsetContextfirst.- Type:
- params¶
Keyword arguments the artifact declared for the factory. The names bind to the factory’s signature (checked at compile); the values are decoded YAML carried as-is.
- Type:
collections.abc.Mapping[str, Any]
- class loom.ai.compiler.CompiledRemoteAuth(*, kind, settings=())[source]¶
Bases:
LoomFrozenStructAuthentication strategy of one remote endpoint, resolved to name and settings.
One struct serves both outbound transports, MCP servers and A2A agents, as the one registry that builds it does: the strategy contract is
httpx.Auth, which knows nothing of either protocol.kindis separated from the rest of theauthblock once, here, so the engine never re-reads configuration to find out which strategy to build. The settings are carried as ordered pairs rather than a mapping because the plan is a frozen, hashable value.
- class loom.ai.compiler.CompiledSkillsCapability(*, library, directory, names)[source]¶
Bases:
LoomFrozenStructSkill library resolved to a directory and a selected set of skill names.
Both globs and the library reference die at compile: the plan carries the absolute directory and the exact skill names granted, so the engine loads them without re-interpreting the artifact.
- class loom.ai.compiler.CompiledSqlCapability(*, connection, config, max_rows, max_result_bytes)[source]¶
Bases:
LoomFrozenStructRead-only SQL access resolved to its connection configuration.
- Parameters:
connection (str)
config (SqlConnectionConfig)
max_rows (int)
max_result_bytes (int)
- config¶
Validated read-only connection configuration.
- class loom.ai.compiler.CompiledUsecaseCapability(*, keys, use_cases)[source]¶
Bases:
LoomFrozenStructGranted business operations resolved against the use-case registry.
- loom.ai.compiler.mcp_connection(capability)[source]¶
Return the connection identity of one
mcpgrant, its filters cleared.A worker opens one client per MCP connection and every agent granted it works over that one client, so what makes two grants the same client is every fact the connection is made of — transport, address, credential reference, deadline, subprocess command, arguments and environment.
includeandexcludeare per-agent views over the same connection, so they are emptied here and applied by the agent’s own toolset instead.- Parameters:
capability (CompiledMcpCapability) – Compiled grant of one agent.
- Returns:
The same grant with
includeandexcludeemptied. It is hashable, so it doubles as the key a shared client is stored under and as the capability that client is built from.- Return type:
Example:
assert mcp_connection(read_only) == mcp_connection(read_write)