Module System¶
Modules are the building blocks of Amplifier. They implement specific functionality according to stable contracts.
Module Types¶
| Type | Purpose | Mount Point | Example |
|---|---|---|---|
| Provider | LLM backends | providers | Anthropic, OpenAI |
| Tool | Agent capabilities | tools | Filesystem, Bash |
| Orchestrator | Execution loops | orchestrator | Basic, Streaming |
| Context | Memory management | context | Simple, Persistent |
| Hook | Observability | (registered) | Logging, Approval |
Module Discovery¶
Modules are discovered via Python entry points:
# In module's pyproject.toml
[project.entry-points."amplifier.modules"]
provider-anthropic = "amplifier_module_provider_anthropic:mount"
The kernel discovers all installed modules:
# Entry points group: amplifier.modules
discovered = {
"provider-anthropic": "amplifier_module_provider_anthropic:mount",
"tool-filesystem": "amplifier_module_tool_filesystem:mount",
...
}
Module Loading¶
Mount Function¶
Every module exposes a mount function:
async def mount(
coordinator: ModuleCoordinator,
config: dict | None = None
) -> Callable | None:
"""
Mount the module.
Args:
coordinator: Infrastructure context
config: Module configuration from Mount Plan
Returns:
Optional cleanup function
"""
config = config or {}
# Create module instance
module = MyModule(config)
# Mount to coordinator
await coordinator.mount("providers", module, name="my-provider")
# Return optional cleanup
async def cleanup():
await module.close()
return cleanup
Loading Sequence¶
Mount Plan
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 1. Load Orchestrator (required) │
│ mount_plan["session"]["orchestrator"] │
└──────┬──────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 2. Load Context Manager (required) │
│ mount_plan["session"]["context"] │
└──────┬──────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 3. Load Providers │
│ mount_plan["providers"] │
└──────┬──────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 4. Load Tools │
│ mount_plan["tools"] │
└──────┬──────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 5. Load Hooks │
│ mount_plan["hooks"] │
└─────────────────────────────────────────────────────────────┘
Module Contracts¶
Provider Contract¶
class Provider(Protocol):
@property
def name(self) -> str: ...
def get_info(self) -> ProviderInfo: ...
async def list_models(self) -> list[ModelInfo]: ...
async def complete(
self,
request: ChatRequest,
**kwargs
) -> ChatResponse: ...
def parse_tool_calls(
self,
response: ChatResponse
) -> list[ToolCall]: ...
Tool Contract¶
class Tool(Protocol):
@property
def name(self) -> str: ...
@property
def description(self) -> str: ...
@property
def input_schema(self) -> dict: ...
async def execute(
self,
input: dict[str, Any]
) -> ToolResult: ...
Orchestrator Contract¶
class Orchestrator(Protocol):
async def execute(
self,
prompt: str,
context: ContextManager,
providers: dict[str, Provider],
tools: dict[str, Tool],
hooks: HookRegistry,
) -> str: ...
Context Contract¶
class ContextManager(Protocol):
async def add_message(self, message: dict) -> None: ...
async def get_messages(self) -> list[dict]: ...
async def should_compact(self) -> bool: ...
async def compact(self) -> None: ...
async def clear(self) -> None: ...
Hook Contract¶
The Coordinator¶
The ModuleCoordinator provides infrastructure context to modules:
class ModuleCoordinator:
# Mount points
orchestrator: Orchestrator
context: ContextManager
providers: dict[str, Provider]
tools: dict[str, Tool]
hooks: HookRegistry
# Infrastructure
session_id: str
parent_id: str | None
config: dict
# Capabilities
def register_capability(self, name: str, impl: Any) -> None
def get_capability(self, name: str) -> Any
# Contribution channels
def register_contributor(self, channel: str, name: str, callback: Callable)
async def collect_contributions(self, channel: str) -> list
# Module lifecycle
async def mount(self, mount_point: str, module: Any, name: str)
async def unmount(self, mount_point: str, name: str)
Module Communication¶
Via Events¶
Modules communicate through events:
# Emitter
await coordinator.hooks.emit("my-module:event", {"data": ...})
# Observer (hook)
async def handler(event: str, data: dict) -> HookResult:
if event == "my-module:event":
# Handle event
...
return HookResult(action="continue")
Via Capabilities¶
Modules can register and use capabilities:
# Register
coordinator.register_capability("cache", my_cache_impl)
# Use
cache = coordinator.get_capability("cache")
if cache:
cache.set("key", "value")
Via Contribution Channels¶
Non-interfering aggregation pattern:
# Module declares observable events
coordinator.register_contributor(
"observability.events",
"my-module",
lambda: ["my-module:start", "my-module:complete"]
)
# Consumer collects
events = await coordinator.collect_contributions("observability.events")
# Returns: [["my-module:start", ...], ["other:event", ...]]
Module Independence¶
Modules are designed to be independent:
- No peer awareness: Modules don't know about other modules
- Non-interference: Module failures don't crash others
- Swappable: Any module can be replaced
- Testable: Modules can be tested in isolation
Creating Modules¶
See Module Development Guide for creating your own modules.