use anyhow::Result;
use async_trait::async_trait;
use oxi_agent::{AgentTool, AgentToolResult, ToolError};
use serde_json::Value;
use std::sync::Arc;
use tokio::sync::oneshot;
use super::wasm::WasmExtensionManager;
pub struct WasmTool {
manager: Arc<WasmExtensionManager>,
tool_name: String,
description: String,
schema: Value,
}
impl WasmTool {
pub fn new(
manager: Arc<WasmExtensionManager>,
tool_name: String,
description: String,
schema: Value,
) -> Self {
Self { manager, tool_name, description, schema }
}
}
#[async_trait]
impl AgentTool for WasmTool {
fn name(&self) -> &str { &self.tool_name }
fn label(&self) -> &str { &self.description }
fn description(&self) -> &str { &self.description }
fn parameters_schema(&self) -> Value { self.schema.clone() }
async fn execute(
&self,
_tool_call_id: &str,
params: Value,
_signal: Option<oneshot::Receiver<()>>,
_ctx: &oxi_agent::ToolContext,
) -> Result<AgentToolResult, ToolError> {
let manager = self.manager.clone();
let tool_name = self.tool_name.clone();
let result = tokio::task::spawn_blocking(move || {
manager.execute_tool(&tool_name, params)
})
.await
.map_err(|e| format!("WASM execution panicked: {}", e))?;
match result {
Ok(value) => {
let success = value.get("success")
.and_then(|v| v.as_bool())
.unwrap_or(true);
let output = value.get("output")
.and_then(|v| v.as_str())
.unwrap_or("(no output)")
.to_string();
if success {
let mut tool_result = AgentToolResult::success(output);
if let Some(meta) = value.get("metadata").cloned() {
tool_result = tool_result.with_metadata(meta);
}
Ok(tool_result)
} else {
Ok(AgentToolResult::error(output))
}
}
Err(e) => Ok(AgentToolResult::error(format!(
"WASM tool '{}' error: {}", self.tool_name, e
))),
}
}
}