From 775c66b287db1670b68d7f22d0d9aa544632d271 Mon Sep 17 00:00:00 2001 From: Chris Beck Date: Tue, 9 Dec 2025 09:38:23 -0700 Subject: [PATCH] use builder pattern with gateway --- signal-gateway-bin/src/main.rs | 6 ++- signal-gateway/src/gateway/mod.rs | 74 ++++++++++++++++++++++++++++++- 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/signal-gateway-bin/src/main.rs b/signal-gateway-bin/src/main.rs index 69dce09..ae7441d 100644 --- a/signal-gateway-bin/src/main.rs +++ b/signal-gateway-bin/src/main.rs @@ -123,7 +123,11 @@ async fn main() -> Result<(), Box> { } 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); diff --git a/signal-gateway/src/gateway/mod.rs b/signal-gateway/src/gateway/mod.rs index 3bc46b5..0c396ff 100644 --- a/signal-gateway/src/gateway/mod.rs +++ b/signal-gateway/src/gateway/mod.rs @@ -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>, + /// Additional tool executors added via the builder. + extra_tool_executors: Vec>, +} + +/// Builder for creating a [`Gateway`] with optional additional tool executors. +pub struct GatewayBuilder { + config: GatewayConfig, + token: Option, + command_router: Option, + extra_tool_executors: Vec>, +} + +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) -> Self { + self.extra_tool_executors.push(executor); + self + } + + /// Build the gateway. + pub async fn build(self) -> Arc { + 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 { 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}")) } }