Builder Pattern
⚠️
This is a v2 design — not yet implemented. See Architecture v2 for context.
The problem
AgentRuntime requires several components — core, provider, registry, policy, memory, hooks. The current constructor has many positional parameters:
// Hard to read, easy to get wronglet runtime = AgentRuntime::new( Box::new(core), Box::new(provider), registry, Box::new(policy), Some(Box::new(memory)), Box::new(hooks), config,);
AgentRuntimeBuilder
Non-generic fluent builder with sensible defaults:
pub struct AgentRuntimeBuilder { core: Option<Box<dyn AgentCore>>, provider: Option<Box<dyn LlmProvider>>, registry: Option<PluginRegistry>, policy: Option<Box<dyn RuntimePolicy>>, memory: Option<Box<dyn MemoryPlugin>>, hooks: Option<Box<dyn AgentHooks>>, config: Option<AgentConfig>,}impl AgentRuntimeBuilder { pub fn new() -> Self; pub fn core(mut self, core: impl AgentCore + 'static) -> Self; // required pub fn provider(mut self, provider: impl LlmProvider + 'static) -> Self; // required pub fn registry(mut self, registry: PluginRegistry) -> Self; // optional pub fn policy(mut self, policy: impl RuntimePolicy + 'static) -> Self; pub fn memory(mut self, memory: impl MemoryPlugin + 'static) -> Self; pub fn hooks(mut self, hooks: impl AgentHooks + 'static) -> Self; pub fn config(mut self, config: AgentConfig) -> Self; pub fn build(self) -> Result<AgentRuntime, BuildError>; // sync, not async}
Usage examples
// Minimal — just core + providerlet runtime = AgentRuntime::builder() .core(ReActCore::default()) .provider(OpenAiProvider::new(url, key)) .build()?;// Full configurationlet runtime = AgentRuntime::builder() .core(ReActCore::new(system_prompt)) .provider(OpenAiProvider::new(url, key)) .registry(registry) .policy(DefaultPolicy::standard()) .memory(CompositeMemory::new() .add("conversation", ConversationMemory::new(thread_id)) .add("working", WorkingMemory::new())) .hooks(CompositeHooks::new() .add(LoggingHooks::new(log::Level::Info)) .add(RateLimitHooks::new(10))) .build()?;// Jan Desktop embeddinglet runtime = AgentRuntime::builder() .core(ReActCore::new(system_prompt)) .provider(OpenAiProvider::new("http://localhost:6767/v1", key)) .registry(PluginRegistry::new().with_tools(BuiltInTools::all())) .memory(ConversationMemory::new(thread_id)) .build()?;
Defaults
| Component | Default | Behavior |
|---|---|---|
registry | PluginRegistry::default() | Empty — no tools |
policy | HostPolicy | No restrictions |
memory | NullMemory | No persistence |
hooks | NoOpHooks | No hooks — zero overhead |
config | AgentConfig::default() | Default token limits |
Build-time validation
build() validates required components:
pub fn build(self) -> Result<AgentRuntime, BuildError> { let core = self.core.ok_or(BuildError::Missing("core"))?; let provider = self.provider.ok_or(BuildError::Missing("provider"))?; // optional components use defaults Ok(AgentRuntime { core, provider, ... })}pub enum BuildError { Missing(&'static str),}
Design decisions
| Decision | Rationale |
|---|---|
| No generics | All trait objects — AgentRuntimeBuilder has no type parameters |
| Sync build | No actors or async spawning — build() is synchronous |
| Two required fields | Core + provider are the minimum for a functional agent |
impl Trait parameters | Builder auto-boxes — callers pass concrete types, not Box<dyn ...> |