configuration - kongusen/loom-agent GitHub Wiki
This page describes Loom's advanced configuration and compatibility objects.
For new application code, start from:
from loom import Agent, Capability, Model, RuntimeUse AgentConfig, ModelRef, GenerationConfig, and create_agent() when maintaining existing 0.8.x applications or when you need the lower-level config object vocabulary directly.
Two rules define the surface:
- Top-level configuration uses explicit objects
- Extension capacity is preserved through
extensions, not loose dictionaries
from loom import AgentConfig, ModelRef
from loom.config import GenerationConfig, MemoryConfig, PolicyConfig, RuntimeConfig
config = AgentConfig(
model=ModelRef.openai("gpt-4.1-mini"),
instructions="You are a platform assistant.",
generation=GenerationConfig(temperature=0.2, max_output_tokens=512),
tools=[],
policy=PolicyConfig(),
memory=MemoryConfig(),
heartbeat=None,
runtime=RuntimeConfig(),
safety_rules=[],
knowledge=[],
)Field overview:
| Field | Type | Description |
|---|---|---|
model |
ModelRef |
Required model and provider definition |
instructions |
str |
System instructions |
generation |
GenerationConfig |
Generation controls |
tools |
list[ToolSpec] |
Tool declarations |
policy |
PolicyConfig | None |
Tool governance policy |
memory |
MemoryConfig | None |
Session memory configuration |
heartbeat |
HeartbeatConfig | None |
Heartbeat monitoring configuration |
runtime |
RuntimeConfig | None |
Execution engine controls |
safety_rules |
list[SafetyRule] | None |
Safety rules |
knowledge |
list[KnowledgeSource] |
Knowledge sources |
ModelRef is the stable model reference object and the provider selection entry point.
from loom import ModelRef
anthropic = ModelRef.anthropic("claude-sonnet-4")
openai = ModelRef.openai("gpt-4.1-mini")
gemini = ModelRef.gemini("gemini-2.5-flash")
qwen = ModelRef.qwen("qwen-max")
ollama = ModelRef.ollama("llama3")| Field | Description |
|---|---|
provider |
Provider name |
name |
Model name |
api_base |
Custom base URL |
organization |
OpenAI organization |
api_key_env |
Custom API key environment variable name |
extensions |
Extension fields |
If you are targeting an OpenAI-compatible gateway:
import os
from loom import ModelRef
model = ModelRef.openai(
"gpt-4.1-mini",
api_base=os.getenv("OPENAI_BASE_URL"),
)By default, the API key is loaded from OPENAI_API_KEY.
If your gateway uses a different environment variable name:
model = ModelRef.openai(
"my-model",
api_base=os.getenv("MY_LLM_BASE_URL"),
api_key_env="MY_LLM_API_KEY",
)from loom.config import GenerationConfig
generation = GenerationConfig(
temperature=0.2,
max_output_tokens=512,
extensions={"top_p": 0.95},
)Fields:
| Field | Description |
|---|---|
temperature |
Sampling temperature |
max_output_tokens |
Maximum output tokens |
extensions |
Reserved space for provider-specific extension data |
from loom import tool
@tool(description="Search documentation", read_only=True)
def search_docs(query: str) -> str:
return "..."from loom.config import ToolSpec
def search_docs(query: str) -> str:
return "..."
tool_spec = ToolSpec.from_function(
search_docs,
name="search_docs",
description="Search documentation",
read_only=True,
)| Field | Description |
|---|---|
name |
Tool name |
description |
Tool description |
parameters |
Parameter schema |
read_only |
Whether the tool is read-only |
destructive |
Whether the tool is destructive |
concurrency_safe |
Whether the tool is safe to run concurrently |
requires_permission |
Whether the tool requires permission |
handler |
Local execution handler |
extensions |
Extension fields |
Notes:
-
AgentConfig.toolsaccepts onlyToolSpec - raw Python callables are not accepted directly
-
@toolis the simplest way to stay on the public path
PolicyConfig defines how an agent is allowed to use tools.
from loom.config import (
PolicyConfig,
PolicyContext,
ToolAccessPolicy,
ToolPolicy,
ToolRateLimitPolicy,
)
policy = PolicyConfig(
tools=ToolPolicy(
access=ToolAccessPolicy(
allow=["read_file", "search_docs"],
deny=["delete_file"],
read_only_only=False,
allow_destructive=False,
),
rate_limits=ToolRateLimitPolicy(max_calls_per_minute=30),
),
context=PolicyContext.named("repo"),
)Use it to:
- restrict the tool set
- block destructive operations
- assign a named policy context
- rate-limit tool calls
MemoryConfig defines whether session memory is enabled and which backend is used.
from loom.config import MemoryBackend, MemoryConfig
memory = MemoryConfig(
enabled=True,
backend=MemoryBackend.in_memory(),
namespace="assistant-prod",
)If you have a custom backend:
memory = MemoryConfig(
enabled=True,
backend=MemoryBackend.custom(
"redis",
options={"url": "redis://localhost:6379/0"},
),
)Notes:
-
memory=Nonemeans no explicit memory config -
enabled=Falsemeans memory is explicitly disabled - the public API stabilizes the memory configuration shape first; backend behavior can continue to evolve
HeartbeatConfig declares watch sources and interrupt behavior.
from loom.config import FilesystemWatchMethod, HeartbeatConfig, WatchConfig
heartbeat = HeartbeatConfig(
interval=5.0,
min_entropy_delta=0.1,
watch_sources=[
WatchConfig.filesystem(
paths=["./src", "./configs"],
method=FilesystemWatchMethod.HASH,
),
],
)WatchConfig.filesystem(...)WatchConfig.process(...)WatchConfig.resource(...)WatchConfig.mf_events(...)
from loom.config import ResourceThresholds, WatchConfig
watch = WatchConfig.resource(
thresholds=ResourceThresholds(
cpu_pct=80.0,
memory_pct=90.0,
)
)from loom.config import HeartbeatConfig, HeartbeatInterruptPolicy
heartbeat = HeartbeatConfig(
interrupt_policy=HeartbeatInterruptPolicy(
low="queue",
high="request",
critical="force",
)
)RuntimeConfig controls execution engine behavior.
from loom.config import RuntimeConfig, RuntimeFallback, RuntimeFallbackMode, RuntimeFeatures, RuntimeLimits
runtime = RuntimeConfig(
limits=RuntimeLimits(
max_iterations=12,
max_context_tokens=64_000,
),
features=RuntimeFeatures(
enable_safety=True,
fallback=RuntimeFallback(mode=RuntimeFallbackMode.ERROR),
),
)| Object | Field | Description |
|---|---|---|
RuntimeLimits |
max_iterations |
Maximum execution iterations |
RuntimeLimits |
max_context_tokens |
Context token ceiling |
RuntimeFeatures |
enable_safety |
Whether safety is enabled |
RuntimeFallback |
mode |
Fallback behavior when the provider is unavailable |
Supported modes:
RuntimeFallbackMode.LOCAL_SUMMARYRuntimeFallbackMode.ERROR
The default is LOCAL_SUMMARY.
This means that when provider initialization fails, Loom can return a local fallback result instead of immediately failing.
If your production environment must fail hard when the model is unavailable:
runtime = RuntimeConfig(
features=RuntimeFeatures(
fallback=RuntimeFallback(mode=RuntimeFallbackMode.ERROR),
)
)SafetyRule declares tool-call veto rules.
from loom.config import SafetyRule
rule = SafetyRule.block_tool(
name="no_delete",
tool_names=["delete_file"],
reason="File deletion is forbidden.",
)rule = SafetyRule.when_argument_equals(
name="no_prod_deploy",
tool_name="deploy",
argument="env",
value="production",
reason="Production deployment requires manual approval.",
)rule = SafetyRule.when_argument_startswith(
name="no_prod_write",
tool_name="write_file",
argument="path",
prefix="/prod/",
reason="Writing into production paths is forbidden.",
)rule = SafetyRule.when_argument_contains_any(
name="no_dangerous_shell",
tool_name="execute_command",
argument="cmd",
values=["rm -rf", "mkfs"],
reason="Dangerous commands are forbidden.",
)from loom.config import SafetyEvaluator, SafetyRule
rule = SafetyRule.custom(
name="business_hours_only",
reason="Configuration changes are only allowed during business hours.",
evaluator=SafetyEvaluator.callable(
lambda tool_name, arguments: tool_name == "modify_config"
and arguments.get("approved") is not True
),
)The stable knowledge contract currently has two parts:
- declare knowledge sources in
AgentConfig.knowledge - resolve them into a
KnowledgeBundlewithagent.resolve_knowledge()
from loom import KnowledgeDocument, KnowledgeSource
knowledge = [
KnowledgeSource.inline(
"product-docs",
[
KnowledgeDocument(
title="Session",
content="Loom provides Session and Run primitives.",
uri="docs://session",
),
KnowledgeDocument(
title="Safety",
content="Loom supports declarative safety rules.",
),
],
description="Product documentation",
)
]from loom.config import (
KnowledgeCitation,
KnowledgeEvidence,
KnowledgeEvidenceItem,
KnowledgeResolver,
)
from loom import KnowledgeQuery, KnowledgeSource
def resolve_docs(query: KnowledgeQuery) -> KnowledgeEvidence:
item = KnowledgeEvidenceItem(
source_name="docs",
title="API Overview",
content=f"Answer for: {query.text}",
uri="kb://api-overview",
score=0.9,
)
citation = KnowledgeCitation(
source_name="docs",
title="API Overview",
uri="kb://api-overview",
snippet=item.content,
)
return KnowledgeEvidence(
query=query,
items=[item],
citations=[citation],
relevance_score=0.9,
)
knowledge = [
KnowledgeSource.dynamic(
"docs",
resolver=KnowledgeResolver.callable(
resolve_docs,
description="Dynamic knowledge retrieval",
),
)
]from loom import KnowledgeQuery, RunContext
bundle = agent.resolve_knowledge(
KnowledgeQuery(
text="How does Loom handle tool calling?",
top_k=3,
)
)
result = await agent.run(
"Explain Loom's tool-calling model based on the provided knowledge.",
context=RunContext(knowledge=bundle),
)This is currently the clearest, most stable, and most controllable integration pattern.
Most stable config objects keep an extensions field.
Use it to:
- attach business-specific metadata
- pass through values that are not yet promoted into stable first-class fields
- preserve future extension capacity
Do not push core control logic into extensions, or the API will drift back toward an opaque dictionary surface.
This example shows the lower-level compatibility path for existing applications. New applications should usually prefer Agent(model=Model..., runtime=Runtime..., capabilities=[...]).
from loom import AgentConfig, ModelRef, create_agent, tool
from loom.config import (
GenerationConfig,
HeartbeatConfig,
MemoryBackend,
MemoryConfig,
PolicyConfig,
PolicyContext,
RuntimeConfig,
RuntimeLimits,
SafetyRule,
ToolAccessPolicy,
ToolPolicy,
ToolRateLimitPolicy,
WatchConfig,
)
@tool(description="Search documentation", read_only=True)
def search_docs(query: str) -> str:
return f"results for {query}"
agent = create_agent(
AgentConfig(
model=ModelRef.openai("gpt-4.1-mini"),
instructions="You are a platform engineering assistant.",
generation=GenerationConfig(
temperature=0.2,
max_output_tokens=512,
),
tools=[search_docs],
policy=PolicyConfig(
tools=ToolPolicy(
access=ToolAccessPolicy(
allow=["search_docs"],
allow_destructive=False,
),
rate_limits=ToolRateLimitPolicy(max_calls_per_minute=30),
),
context=PolicyContext.named("repo-assistant"),
),
memory=MemoryConfig(
enabled=True,
backend=MemoryBackend.in_memory(),
namespace="repo-assistant",
),
heartbeat=HeartbeatConfig(
interval=5.0,
watch_sources=[
WatchConfig.filesystem(paths=["./src"]),
],
),
safety_rules=[
SafetyRule.block_tool(
name="no_delete",
tool_names=["delete_file"],
reason="Delete operations are forbidden.",
)
],
runtime=RuntimeConfig(
limits=RuntimeLimits(max_iterations=12),
),
)
)Related examples: