use builder pattern with gateway

This commit is contained in:
Chris Beck
2025-12-09 09:38:23 -07:00
parent 6ac6beb9be
commit 775c66b287
2 changed files with 77 additions and 3 deletions
+5 -1
View File
@@ -123,7 +123,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
}
let command_router = router_builder.build();
let gateway = Gateway::new(config.gateway, token.clone(), command_router).await;
let gateway = Gateway::builder(config.gateway)
.with_cancellation_token(token.clone())
.with_command_router(command_router)
.build()
.await;
let listener = TcpListener::bind(config.http_listen_addr).await.unwrap();
info!("Listening for http on {}", config.http_listen_addr);
+72 -2
View File
@@ -255,14 +255,74 @@ pub struct Gateway {
/// Claude API client for AI-powered responses.
/// Initialized after Arc creation so it can hold a weak reference back to Gateway.
claude: OnceLock<Box<ClaudeAgent>>,
/// Additional tool executors added via the builder.
extra_tool_executors: Vec<Arc<dyn ToolExecutor>>,
}
/// Builder for creating a [`Gateway`] with optional additional tool executors.
pub struct GatewayBuilder {
config: GatewayConfig,
token: Option<CancellationToken>,
command_router: Option<CommandRouter>,
extra_tool_executors: Vec<Arc<dyn ToolExecutor>>,
}
impl GatewayBuilder {
/// Create a new gateway builder with the given configuration.
pub fn new(config: GatewayConfig) -> Self {
Self {
config,
token: None,
command_router: None,
extra_tool_executors: Vec::new(),
}
}
/// Set the cancellation token for the gateway.
pub fn with_cancellation_token(mut self, token: CancellationToken) -> Self {
self.token = Some(token);
self
}
/// Set the command router for the gateway.
pub fn with_command_router(mut self, router: CommandRouter) -> Self {
self.command_router = Some(router);
self
}
/// Add an additional tool executor to the gateway.
///
/// Tools from these executors will be available to Claude alongside
/// the built-in tools (log handler, prometheus, etc.).
pub fn with_tools(mut self, executor: Arc<dyn ToolExecutor>) -> Self {
self.extra_tool_executors.push(executor);
self
}
/// Build the gateway.
pub async fn build(self) -> Arc<Gateway> {
Gateway::new_internal(
self.config,
self.token.unwrap_or_default(),
self.command_router.unwrap_or_default(),
self.extra_tool_executors,
)
.await
}
}
impl Gateway {
/// Create a new gateway with the given configuration.
pub async fn new(
/// Create a new gateway builder with the given configuration.
pub fn builder(config: GatewayConfig) -> GatewayBuilder {
GatewayBuilder::new(config)
}
/// Internal constructor used by `GatewayBuilder::build`.
async fn new_internal(
config: GatewayConfig,
token: CancellationToken,
command_router: CommandRouter,
extra_tool_executors: Vec<Arc<dyn ToolExecutor>>,
) -> Arc<Self> {
let (signal_alert_mq_tx, signal_alert_mq_rx) = unbounded_channel();
@@ -286,6 +346,7 @@ impl Gateway {
log_handler,
command_router,
claude: OnceLock::new(),
extra_tool_executors,
});
// Initialize Claude with a weak reference back to the gateway
@@ -930,6 +991,9 @@ impl ToolExecutor for Gateway {
if let Some(prometheus) = &self.prometheus {
tools.extend(prometheus.tools());
}
for executor in &self.extra_tool_executors {
tools.extend(executor.tools());
}
tools
}
@@ -944,6 +1008,12 @@ impl ToolExecutor for Gateway {
return prometheus.execute(name, input).await;
}
for executor in &self.extra_tool_executors {
if executor.has_tool(name) {
return executor.execute(name, input).await;
}
}
Err(format!("unknown tool: {name}"))
}
}