openfunctions_rs/core/
agent.rs

1//! Agent management and representation.
2//!
3//! This module defines the `Agent` struct, which represents an AI agent,
4//! its configuration, and its associated tools.
5
6use crate::core::Tool;
7use crate::models::AgentDefinition;
8use anyhow::Result;
9use std::collections::HashMap;
10use std::path::{Path, PathBuf};
11
12/// Represents an AI agent, including its definition, tools, and configuration.
13///
14/// An agent is a fundamental concept in this framework, encapsulating the logic
15/// and capabilities needed to perform tasks.
16#[derive(Debug, Clone)]
17pub struct Agent {
18    /// A unique name for the agent.
19    pub name: String,
20
21    /// The file system path to the agent's directory.
22    pub path: PathBuf,
23
24    /// The agent's definition, loaded from its `index.yaml` file.
25    pub definition: AgentDefinition,
26
27    /// A list of tools that are specific to this agent.
28    pub tools: Vec<Tool>,
29
30    /// A list of names of shared tools that this agent can use.
31    pub shared_tools: Vec<String>,
32
33    /// A map of variables and their values for this agent.
34    pub variables: HashMap<String, String>,
35}
36
37impl Agent {
38    /// Loads an agent from a specified directory.
39    ///
40    /// This function reads the agent's definition from `index.yaml`, loads its
41    /// agent-specific tools from a `tools` subdirectory, and reads the list of
42    /// shared tools from `tools.txt`.
43    pub async fn from_directory<P: AsRef<Path>>(path: P) -> Result<Self> {
44        let path = path.as_ref().to_path_buf();
45        let name = path
46            .file_name()
47            .ok_or_else(|| anyhow::anyhow!("Invalid agent directory"))?
48            .to_string_lossy()
49            .to_string();
50
51        let index_path = path.join("index.yaml");
52        let index_content = tokio::fs::read_to_string(&index_path).await?;
53        let definition: AgentDefinition = serde_yaml::from_str(&index_content)?;
54
55        // Load agent-specific tools from a 'tools' subdirectory
56        let mut tools = Vec::new();
57        let agent_tools_dir = path.join("tools");
58        if agent_tools_dir.is_dir() {
59            let mut entries = tokio::fs::read_dir(agent_tools_dir).await?;
60            while let Some(entry) = entries.next_entry().await? {
61                let tool_path = entry.path();
62                if tool_path.is_file() {
63                    match Tool::from_file(&tool_path).await {
64                        Ok(tool) => tools.push(tool),
65                        Err(e) => tracing::warn!(
66                            "Failed to load agent-specific tool {}: {}",
67                            tool_path.display(),
68                            e
69                        ),
70                    }
71                }
72            }
73        }
74
75        let shared_tools = if path.join("tools.txt").exists() {
76            let content = tokio::fs::read_to_string(path.join("tools.txt")).await?;
77            content
78                .lines()
79                .filter(|line| !line.trim().is_empty() && !line.starts_with('#'))
80                .map(|s| s.to_string())
81                .collect()
82        } else {
83            Vec::new()
84        };
85
86        let mut variables = HashMap::new();
87        for var in &definition.variables {
88            if let Some(default) = &var.default {
89                variables.insert(var.name.clone(), default.clone());
90            }
91        }
92
93        Ok(Self {
94            name,
95            path,
96            definition,
97            tools,
98            shared_tools,
99            variables,
100        })
101    }
102
103    /// Retrieves all tools available to this agent, including its own tools and shared ones.
104    ///
105    /// # Arguments
106    ///
107    /// * `registry` - A reference to the `Registry` where shared tools are stored.
108    pub fn get_all_tools<'a>(
109        &'a self,
110        registry: &'a crate::core::Registry,
111    ) -> Result<Vec<&'a Tool>> {
112        let mut all_tools: Vec<&'a Tool> = self.tools.iter().collect();
113
114        for tool_name in &self.shared_tools {
115            if let Some(tool) = registry.get_tool(tool_name) {
116                all_tools.push(tool);
117            } else {
118                tracing::warn!(
119                    "Shared tool '{}' for agent '{}' not found in registry.",
120                    tool_name,
121                    self.name
122                );
123            }
124        }
125
126        Ok(all_tools)
127    }
128}