Skip to main content

runifold_tool/
tool.rs

1use std::{collections::BTreeMap, future::Future, pin::Pin};
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6use runifold_model::ContentPart;
7
8use crate::{ToolContext, ToolDescriptor, ToolError};
9
10/// A boxed, sendable future returned by a tool.
11#[cfg(not(target_arch = "wasm32"))]
12pub type ToolFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
13
14/// A boxed Tool future on single-threaded WASM.
15#[cfg(target_arch = "wasm32")]
16pub type ToolFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;
17
18/// Successful canonical tool output.
19#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
20pub struct ToolOutput {
21    /// Ordered model-visible presentation content.
22    pub content: Vec<ContentPart>,
23    /// Optional structured output value used for contract validation and by
24    /// protocols that support a separate structured result channel.
25    #[serde(default, skip_serializing_if = "Option::is_none")]
26    pub structured_content: Option<Value>,
27    /// Namespaced host metadata retained across Agent and protocol bridges.
28    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
29    pub metadata: BTreeMap<String, Value>,
30    /// Whether execution completed with a model-visible application error.
31    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
32    pub is_error: bool,
33    /// Whether the value is safe to expose verbatim to a model.
34    pub model_visible: bool,
35}
36
37impl ToolOutput {
38    /// Creates model-visible output from a JSON value.
39    ///
40    /// JSON values are retained as structured content and mirrored as text
41    /// for providers that only accept textual function results.
42    pub fn model_visible(value: Value) -> Self {
43        let structured_content = Some(value.clone());
44        let text = match value {
45            Value::String(text) => text,
46            value => value.to_string(),
47        };
48        Self {
49            content: vec![ContentPart::text(text)],
50            structured_content,
51            metadata: BTreeMap::new(),
52            is_error: false,
53            model_visible: true,
54        }
55    }
56
57    /// Creates a model-visible rich result.
58    pub fn rich(content: Vec<ContentPart>) -> Self {
59        Self {
60            content,
61            structured_content: None,
62            metadata: BTreeMap::new(),
63            is_error: false,
64            model_visible: true,
65        }
66    }
67
68    /// Attaches a structured value alongside the presentation content.
69    #[must_use]
70    pub fn with_structured_content(mut self, value: Value) -> Self {
71        self.structured_content = Some(value);
72        self
73    }
74
75    /// Adds namespaced host metadata.
76    #[must_use]
77    pub fn with_metadata(mut self, key: impl Into<String>, value: Value) -> Self {
78        self.metadata.insert(key.into(), value);
79        self
80    }
81
82    /// Creates host-only output that must not be exposed to a model.
83    pub fn host_only(content: Vec<ContentPart>) -> Self {
84        Self {
85            content,
86            structured_content: None,
87            metadata: BTreeMap::new(),
88            is_error: false,
89            model_visible: false,
90        }
91    }
92
93    /// Creates a rich, model-visible application error result.
94    pub fn model_error(content: Vec<ContentPart>) -> Self {
95        Self {
96            content,
97            structured_content: None,
98            metadata: BTreeMap::new(),
99            is_error: true,
100            model_visible: true,
101        }
102    }
103}
104
105/// Object-safe execution boundary implemented by tools.
106pub trait Tool: Send + Sync {
107    /// Returns the tool's immutable semantic contract.
108    fn descriptor(&self) -> &ToolDescriptor;
109
110    /// Executes one invocation.
111    fn invoke(
112        &self,
113        input: Value,
114        context: ToolContext,
115    ) -> ToolFuture<'_, Result<ToolOutput, ToolError>>;
116}