Skip to main content

agent_engine/tools/
extension.rs

1use std::sync::Arc;
2
3use serde_json::Value;
4
5use crate::extensions::runtime::process::RegisteredExtensionToolSpec;
6use crate::extensions::runtime::ExtensionHandler;
7use crate::{Result, RuntimeError, ToolContext};
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(
20        plugin_id: &str,
21        spec: RegisteredExtensionToolSpec,
22        handler: Arc<dyn ExtensionHandler>,
23    ) -> Self {
24        Self {
25            runtime_name: format!("{}:{}", plugin_id, spec.name),
26            description: spec.description,
27            input_schema: spec.input_schema,
28            handler,
29            tool_name: spec.name,
30            plugin_id: plugin_id.to_string(),
31        }
32    }
33}
34
35#[async_trait::async_trait]
36impl crate::tools::Tool for ExtensionTool {
37    fn name(&self) -> &str {
38        &self.runtime_name
39    }
40
41    fn description(&self) -> &str {
42        &self.description
43    }
44
45    fn parameters(&self) -> Value {
46        self.input_schema.clone()
47    }
48
49    async fn execute(&self, params: Value, ctx: ToolContext) -> Result<String> {
50        let binding = ctx
51            .capabilities
52            .memory_backend
53            .unwrap_or_else(crate::memory_backend::MemoryBinding::configured_current);
54        if binding.exclusive() && crate::memory_backend::competing_note_tool(&self.tool_name) {
55            return Err(RuntimeError::Tool("independent extension memory is disabled by the selected host backend; use short memory tools".into()));
56        }
57        let value = self
58            .handler
59            .call_tool(&self.tool_name, params)
60            .await
61            .map_err(RuntimeError::Tool)?;
62        if let Some(text) = value.as_str() {
63            Ok(text.to_string())
64        } else if let Some(text) = value.get("content").and_then(Value::as_str) {
65            Ok(text.to_string())
66        } else {
67            Ok(value.to_string())
68        }
69    }
70
71    fn extension_id(&self) -> Option<&str> {
72        Some(&self.plugin_id)
73    }
74}