openfunctions_rs/core/
registry.rs

1//! Registry for managing and accessing tools and agents.
2
3use crate::core::{Agent, Tool};
4use anyhow::Result;
5use std::collections::HashMap;
6use std::path::PathBuf;
7use tracing::{info, warn};
8
9/// A registry for discovering, storing, and retrieving tools and agents.
10///
11/// The `Registry` is responsible for loading tools and agents from the filesystem
12/// and providing a central place to access them by name.
13#[derive(Debug, Default)]
14pub struct Registry {
15    tools: HashMap<String, Tool>,
16    agents: HashMap<String, Agent>,
17}
18
19impl Registry {
20    /// Creates a new, empty `Registry`.
21    pub fn new() -> Self {
22        Self::default()
23    }
24
25    /// Loads all tools from the specified directories.
26    ///
27    /// This method scans the provided directories for supported script files
28    /// (e.g., `.sh`, `.js`, `.py`) and loads them as `Tool`s.
29    pub async fn load_tools(&mut self, tool_dirs: &[PathBuf]) -> Result<()> {
30        info!(dirs = ?tool_dirs, "Loading tools from directories");
31        for dir in tool_dirs {
32            if !dir.exists() {
33                warn!(dir = %dir.display(), "Tool directory not found, skipping");
34                continue;
35            }
36            if dir.is_dir() {
37                let mut entries = tokio::fs::read_dir(dir).await?;
38                while let Some(entry) = entries.next_entry().await? {
39                    let path = entry.path();
40                    if path.is_file() {
41                        if let Some(ext) = path.extension() {
42                            if matches!(ext.to_str(), Some("sh" | "js" | "py")) {
43                                match Tool::from_file(&path).await {
44                                    Ok(tool) => {
45                                        info!(name = %tool.name, "Loaded tool");
46                                        self.tools.insert(tool.name.clone(), tool);
47                                    }
48                                    Err(e) => {
49                                        warn!(path = %path.display(), error = ?e, "Failed to load tool");
50                                    }
51                                }
52                            }
53                        }
54                    }
55                }
56            }
57        }
58
59        Ok(())
60    }
61
62    /// Loads all agents from the specified directories.
63    ///
64    /// This method scans the provided directories for agent definitions and
65    /// loads them into the registry.
66    pub async fn load_agents(&mut self, agent_dirs: &[PathBuf]) -> Result<()> {
67        info!(dirs = ?agent_dirs, "Loading agents from directories");
68        for dir in agent_dirs {
69            if !dir.exists() {
70                warn!(dir = %dir.display(), "Agent directory not found, skipping");
71                continue;
72            }
73            if dir.is_dir() {
74                let mut entries = tokio::fs::read_dir(dir).await?;
75                while let Some(entry) = entries.next_entry().await? {
76                    let path = entry.path();
77                    if path.is_dir() {
78                        match Agent::from_directory(&path).await {
79                            Ok(agent) => {
80                                info!(name = %agent.name, "Loaded agent");
81                                self.agents.insert(agent.name.clone(), agent);
82                            }
83                            Err(e) => {
84                                warn!(path = %path.display(), error = ?e, "Failed to load agent");
85                            }
86                        }
87                    }
88                }
89            }
90        }
91
92        Ok(())
93    }
94
95    /// Retrieves a reference to a tool by its name.
96    pub fn get_tool(&self, name: &str) -> Option<&Tool> {
97        self.tools.get(name)
98    }
99
100    /// Retrieves a reference to an agent by its name.
101    pub fn get_agent(&self, name: &str) -> Option<&Agent> {
102        self.agents.get(name)
103    }
104
105    /// Returns a list of names of all registered tools.
106    pub fn list_tools(&self) -> Vec<String> {
107        self.tools.keys().cloned().collect()
108    }
109
110    /// Returns a list of names of all registered agents.
111    pub fn list_agents(&self) -> Vec<String> {
112        self.agents.keys().cloned().collect()
113    }
114}