Skip to main content

mofa_plugins/tools/
rhai.rs

1use super::*;
2use mofa_extra::rhai::{RhaiScriptEngine, ScriptContext, ScriptEngineConfig};
3use serde_json::json;
4
5/// Rhai 脚本执行工具 - 执行 Rhai 脚本
6pub struct RhaiScriptTool {
7    definition: ToolDefinition,
8    engine: RhaiScriptEngine,
9}
10
11impl RhaiScriptTool {
12    pub fn new() -> PluginResult<Self> {
13        let config = ScriptEngineConfig::default();
14        let engine = RhaiScriptEngine::new(config)?;
15        Ok(Self {
16            definition: ToolDefinition {
17                name: "rhai_script".to_string(),
18                description: "Execute Rhai scripts for complex calculations or data processing. Rhai is a safe embedded scripting language.".to_string(),
19                parameters: json!({
20                    "type": "object",
21                    "properties": {
22                        "script": {
23                            "type": "string",
24                            "description": "Rhai script code to execute"
25                        },
26                        "variables": {
27                            "type": "object",
28                            "description": "Variables to inject into script context",
29                            "additionalProperties": true
30                        }
31                    },
32                    "required": ["script"]
33                }),
34                requires_confirmation: true,
35            },
36            engine,
37        })
38    }
39}
40
41#[async_trait::async_trait]
42impl ToolExecutor for RhaiScriptTool {
43    fn definition(&self) -> &ToolDefinition {
44        &self.definition
45    }
46
47    async fn execute(&self, arguments: serde_json::Value) -> PluginResult<serde_json::Value> {
48        let script = arguments["script"]
49            .as_str()
50            .ok_or_else(|| anyhow::anyhow!("Script is required"))?;
51
52        let mut context = ScriptContext::new();
53
54        // Inject variables if provided
55        if let Some(vars) = arguments.get("variables").and_then(|v| v.as_object()) {
56            for (key, value) in vars {
57                // Convert JSON values to Rhai-compatible types
58                match value {
59                    serde_json::Value::Number(n) => {
60                        if let Some(i) = n.as_i64() {
61                            context = context.with_variable(key, i)?;
62                        } else if let Some(f) = n.as_f64() {
63                            context = context.with_variable(key, f)?;
64                        }
65                    }
66                    serde_json::Value::String(s) => {
67                        context = context.with_variable(key, s.clone())?;
68                    }
69                    serde_json::Value::Bool(b) => {
70                        context = context.with_variable(key, *b)?;
71                    }
72                    _ => {
73                        // For complex types, pass as JSON string
74                        context = context.with_variable(key, value.to_string())?;
75                    }
76                }
77            }
78        }
79
80        let result = self.engine.execute(script, &context).await?;
81
82        Ok(json!({
83            "success": true,
84            "result": result.value,
85            "execution_time_ms": result.execution_time_ms
86        }))
87    }
88}