Skip to main content

mofa_sdk/
llm_tools.rs

1//! LLM tool executor adapters for the SDK.
2
3use mofa_foundation::llm::{LLMError, LLMResult, Tool, ToolExecutor};
4use mofa_plugins::{ToolCall, ToolDefinition, ToolPlugin};
5use std::sync::Arc;
6use tokio::sync::RwLock;
7
8/// Adapter that exposes a ToolPlugin as an LLM ToolExecutor.
9///
10/// This lets LLM agents discover tools directly from a ToolPlugin without
11/// manually constructing tool definitions.
12pub struct ToolPluginExecutor {
13    tool_plugin: Arc<RwLock<ToolPlugin>>,
14    cached_tools: Arc<RwLock<Option<Vec<Tool>>>>,
15}
16
17impl ToolPluginExecutor {
18    /// Create a new adapter from an owned ToolPlugin.
19    pub fn new(tool_plugin: ToolPlugin) -> Self {
20        Self {
21            tool_plugin: Arc::new(RwLock::new(tool_plugin)),
22            cached_tools: Arc::new(RwLock::new(None)),
23        }
24    }
25
26    /// Create a new adapter from a shared ToolPlugin handle.
27    pub fn with_shared(tool_plugin: Arc<RwLock<ToolPlugin>>) -> Self {
28        Self {
29            tool_plugin,
30            cached_tools: Arc::new(RwLock::new(None)),
31        }
32    }
33
34    /// Get the underlying ToolPlugin handle.
35    pub fn tool_plugin(&self) -> Arc<RwLock<ToolPlugin>> {
36        self.tool_plugin.clone()
37    }
38
39    /// Clear the cached tool list so it will be refreshed on next access.
40    pub async fn invalidate_cache(&self) {
41        let mut cache = self.cached_tools.write().await;
42        *cache = None;
43    }
44
45    /// Refresh and return the latest tool list from the plugin.
46    pub async fn refresh_tools(&self) -> LLMResult<Vec<Tool>> {
47        let plugin = self.tool_plugin.read().await;
48        let tools = plugin
49            .list_tools()
50            .into_iter()
51            .map(|def| Self::definition_to_tool(&def))
52            .collect::<Vec<_>>();
53
54        let mut cache = self.cached_tools.write().await;
55        *cache = Some(tools.clone());
56        Ok(tools)
57    }
58
59    fn definition_to_tool(def: &ToolDefinition) -> Tool {
60        Tool::function(&def.name, &def.description, def.parameters.clone())
61    }
62}
63
64#[async_trait::async_trait]
65impl ToolExecutor for ToolPluginExecutor {
66    async fn execute(&self, name: &str, arguments: &str) -> LLMResult<String> {
67        let args: serde_json::Value = serde_json::from_str(arguments)
68            .map_err(|e| LLMError::Other(format!("参数解析失败: {}", e)))?;
69
70        let call = ToolCall {
71            call_id: uuid::Uuid::now_v7().to_string(),
72            name: name.to_string(),
73            arguments: args,
74        };
75
76        let mut plugin = self.tool_plugin.write().await;
77        let result = plugin
78            .call_tool(call)
79            .await
80            .map_err(|e| LLMError::Other(format!("工具执行失败: {}", e)))?;
81
82        if result.success {
83            serde_json::to_string(&result.result)
84                .map_err(|e| LLMError::Other(format!("结果序列化失败: {}", e)))
85        } else {
86            Err(LLMError::Other(
87                result.error.unwrap_or_else(|| "未知错误".to_string()),
88            ))
89        }
90    }
91
92    async fn available_tools(&self) -> LLMResult<Vec<Tool>> {
93        let cached = { self.cached_tools.read().await.clone() };
94        if let Some(tools) = cached {
95            return Ok(tools);
96        }
97
98        self.refresh_tools().await
99    }
100}