Skip to main content

apollo/tools/
toolsets.rs

1//! Toolset classification and filtering.
2
3use crate::config::ToolsetConfig;
4
5pub const CORE_TOOLSET_GROUPS: &[&str] = &["runtime", "fs", "memory", "sessions", "misc"];
6
7pub fn toolset_for_tool(name: &str) -> &'static str {
8    match name {
9        "exec" => "runtime",
10        "Read" | "Write" | "Edit" => "fs",
11        "web_search" | "web_fetch" | "browser" => "web",
12        "memory_search" | "memory_get" | "session_search" | "brain_search" | "brain_query"
13        | "brain_put" | "brain_get" => "memory",
14        "list_models" | "doctor" => "sessions",
15        "message" => "messaging",
16        "skill_manager" => "skills",
17        "praefectus" => "desktop",
18        "mcp" | "create_tool" | "list_custom_tools" | "vibemania" => "advanced",
19        "generate_image" | "text_to_speech" | "speech_to_text" => "media",
20        _ => "misc",
21    }
22}
23
24pub fn expand_package(name: &str) -> Vec<&'static str> {
25    match name.trim().to_ascii_lowercase().as_str() {
26        "web" => vec!["web"],
27        "browser" => vec!["browser"],
28        "skills" => vec!["skills"],
29        "advanced" => vec!["advanced"],
30        "desktop" => vec!["desktop"],
31        "media" => vec!["media"],
32        "apollo-live" | "live" => vec!["web", "browser", "skills", "advanced", "desktop", "media"],
33        "core" | "default" => CORE_TOOLSET_GROUPS.to_vec(),
34        _ => vec![],
35    }
36}
37
38pub fn apply_package_manifest(toolsets: &mut ToolsetConfig, packages: &[String]) {
39    if packages.is_empty() {
40        return;
41    }
42    for g in CORE_TOOLSET_GROUPS {
43        let s = (*g).to_string();
44        if !toolsets.enabled.contains(&s) {
45            toolsets.enabled.push(s);
46        }
47    }
48    for pkg in packages {
49        for g in expand_package(pkg) {
50            let s = g.to_string();
51            if !toolsets.enabled.contains(&s) {
52                toolsets.enabled.push(s);
53            }
54        }
55    }
56}
57
58pub fn is_tool_enabled(name: &str, config: &ToolsetConfig) -> bool {
59    let toolset = toolset_for_tool(name);
60    let enabled = config.enabled.is_empty()
61        || config
62            .enabled
63            .iter()
64            .any(|entry| entry == name || entry == toolset);
65    let disabled = config
66        .disabled
67        .iter()
68        .any(|entry| entry == name || entry == toolset);
69    enabled && !disabled
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn toolset_filtering_allows_by_group() {
78        let cfg = ToolsetConfig {
79            enabled: vec!["memory".to_string()],
80            disabled: Vec::new(),
81        };
82        assert!(is_tool_enabled("memory_search", &cfg));
83        assert!(!is_tool_enabled("exec", &cfg));
84    }
85
86    #[test]
87    fn package_live_enables_web() {
88        let mut cfg = ToolsetConfig {
89            enabled: vec![],
90            disabled: Vec::new(),
91        };
92        apply_package_manifest(&mut cfg, &["live".to_string()]);
93        assert!(is_tool_enabled("web_search", &cfg));
94        assert!(cfg.enabled.iter().any(|e| e == "runtime"));
95    }
96}