gitdataai/libs/agent/tool/registry.rs
ZhenYi db0a2eca16 feat(ssh): add complete SSH server implementation for Git operations
- Implement SSHandle struct with comprehensive Git service handling capabilities
- Add support for multiple authentication methods including password, public key and certificate
- Integrate Git command parsing and execution with proper channel management
- Implement branch protection rules enforcement during Git operations
- Add robust error handling and logging for SSH connections and Git processes
- Create secure Git command execution with environment isolation
- Implement proper resource cleanup for channels and subprocesses
- Add support for receive-pack, upload-pack and upload-archive services
- Integrate with existing authz and database services for permission checks
- Implement proper data forwarding between SSH channels and Git processes

fix(config): improve environment loading with error reporting

- Replace silent dotenv loading failures with informative error messages
- Handle global config race conditions safely during application startup
- Improve config loading reliability and debugging capabilities

fix(link-unfurl): handle server-side rendering compatibility

- Add undefined window object check for SSR environments
- Prevent client-side only code from breaking server-side rendering

refactor(agent): improve tool registry error handling

- Replace panics with graceful error logging for duplicate tool registrations
- Add proper error type definitions for tool registry operations
- Implement safe merging of registries with duplicate detection

fix(room-context): enhance WebSocket connection reliability

- Add proper error handling for room subscription operations
- Improve connection management with better error suppression
- Add console warnings for debugging connection issues

feat(ws-client): add comprehensive WebSocket client implementation

- Create RoomWsClient class with complete WebSocket communication layer
- Implement request-response pattern with timeout handling
- Add support for various room-related events and actions
- Include proper connection status tracking and management
- Implement callback system for different event types
- Add automatic reconnection and error recovery mechanisms
2026-04-28 21:29:34 +08:00

132 lines
4.0 KiB
Rust

//! Request-scoped tool registry.
//!
//! Tools are registered per-request (not globally) to keep the system testable
//! and allow different request contexts to have different tool sets.
use std::collections::HashMap;
use futures::FutureExt;
use super::call::ToolError;
use super::context::ToolContext;
use super::definition::ToolDefinition;
/// Error type for tool registry operations.
#[derive(Debug, Clone, thiserror::Error)]
pub enum ToolRegistryError {
#[error("tool already registered: {0}")]
AlreadyRegistered(String),
}
/// Inner function pointer type for tool handlers.
type InnerHandlerFn = dyn Fn(
ToolContext,
serde_json::Value,
) -> std::pin::Pin<
Box<dyn std::future::Future<Output = Result<serde_json::Value, ToolError>> + Send>,
> + Send
+ Sync;
/// Wrapper around `Arc<dyn Fn(...)>` for `Clone` implementability.
#[derive(Clone)]
pub struct ToolHandler(std::sync::Arc<InnerHandlerFn>);
impl ToolHandler {
/// Creates a new handler from an async closure.
/// The closure should return `Result<serde_json::Value, String>` (as used by git_tools),
/// which is converted to `Result<serde_json::Value, ToolError>`.
pub fn new<F>(f: F) -> Self
where
F: Fn(ToolContext, serde_json::Value) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<serde_json::Value, ToolError>> + Send>>
+ Send
+ Sync
+ 'static,
{
Self(std::sync::Arc::new(f))
}
pub async fn execute(
&self,
ctx: ToolContext,
args: serde_json::Value,
) -> Result<serde_json::Value, ToolError> {
(self.0)(ctx, args).await
}
}
/// A request-scoped registry mapping tool names to their handlers.
#[derive(Clone, Default)]
pub struct ToolRegistry {
handlers: HashMap<String, ToolHandler>,
definitions: HashMap<String, ToolDefinition>,
}
impl ToolRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register_fn<F, Fut>(&mut self, name: impl Into<String>, handler: F) -> &mut Self
where
F: Fn(ToolContext, serde_json::Value) -> Fut + Send + Sync + 'static,
Fut: std::future::Future<Output = Result<serde_json::Value, ToolError>> + Send + 'static,
{
let name_str = name.into();
let def = ToolDefinition::new(&name_str);
let handler_fn: std::sync::Arc<InnerHandlerFn> =
std::sync::Arc::new(move |ctx, args| handler(ctx, args).boxed());
self.register(def, ToolHandler(handler_fn));
self
}
pub fn register(&mut self, def: ToolDefinition, handler: ToolHandler) -> &mut Self {
let name = def.name.clone();
if self.handlers.contains_key(&name) {
tracing::warn!("tool already registered (skipping duplicate): {}", name);
return self;
}
self.handlers.insert(name.clone(), handler);
self.definitions.insert(name, def);
self
}
/// Looks up a handler by tool name.
pub fn get(&self, name: &str) -> Option<&ToolHandler> {
self.handlers.get(name)
}
pub fn definitions(&self) -> std::collections::hash_map::Values<'_, String, ToolDefinition> {
self.definitions.values()
}
pub fn to_openai_tools(&self) -> Vec<serde_json::Value> {
self.definitions
.values()
.map(|d| d.to_openai_tool())
.collect()
}
pub fn len(&self) -> usize {
self.handlers.len()
}
pub fn is_empty(&self) -> bool {
self.handlers.is_empty()
}
/// Merges another registry's tools into this one.
/// Skips tools with duplicate names and logs a warning.
pub fn merge(&mut self, other: ToolRegistry) {
for (name, handler) in other.handlers {
if self.handlers.contains_key(&name) {
tracing::warn!("merge skipped duplicate tool: {}", name);
continue;
}
self.handlers.insert(name, handler);
}
for (name, def) in other.definitions {
self.definitions.insert(name, def);
}
}
}