Skip to main content

synaps_cli/tools/
extension.rs

1use std::sync::Arc;
2
3use serde_json::Value;
4
5use crate::{Result, RuntimeError, ToolContext};
6use crate::extensions::runtime::ExtensionHandler;
7use crate::extensions::runtime::process::RegisteredExtensionToolSpec;
8
9pub struct ExtensionTool {
10    runtime_name: String,
11    description: String,
12    input_schema: Value,
13    handler: Arc<dyn ExtensionHandler>,
14    tool_name: String,
15    plugin_id: String,
16}
17
18impl ExtensionTool {
19    pub fn new(plugin_id: &str, spec: RegisteredExtensionToolSpec, handler: Arc<dyn ExtensionHandler>) -> Self {
20        Self {
21            runtime_name: format!("{}:{}", plugin_id, spec.name),
22            description: spec.description,
23            input_schema: spec.input_schema,
24            handler,
25            tool_name: spec.name,
26            plugin_id: plugin_id.to_string(),
27        }
28    }
29}
30
31#[async_trait::async_trait]
32impl crate::tools::Tool for ExtensionTool {
33    fn name(&self) -> &str {
34        &self.runtime_name
35    }
36
37    fn description(&self) -> &str {
38        &self.description
39    }
40
41    fn parameters(&self) -> Value {
42        self.input_schema.clone()
43    }
44
45    async fn execute(&self, params: Value, _ctx: ToolContext) -> Result<String> {
46        let value = self
47            .handler
48            .call_tool(&self.tool_name, params)
49            .await
50            .map_err(RuntimeError::Tool)?;
51        if let Some(text) = value.as_str() {
52            Ok(text.to_string())
53        } else if let Some(text) = value.get("content").and_then(Value::as_str) {
54            Ok(text.to_string())
55        } else {
56            Ok(value.to_string())
57        }
58    }
59
60    fn extension_id(&self) -> Option<&str> {
61        Some(&self.plugin_id)
62    }
63}