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#[cfg(not(target_arch = "wasm32"))]
12pub type ToolFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
13
14#[cfg(target_arch = "wasm32")]
16pub type ToolFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;
17
18#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
20pub struct ToolOutput {
21 pub content: Vec<ContentPart>,
23 #[serde(default, skip_serializing_if = "Option::is_none")]
26 pub structured_content: Option<Value>,
27 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
29 pub metadata: BTreeMap<String, Value>,
30 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
32 pub is_error: bool,
33 pub model_visible: bool,
35}
36
37impl ToolOutput {
38 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 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 #[must_use]
70 pub fn with_structured_content(mut self, value: Value) -> Self {
71 self.structured_content = Some(value);
72 self
73 }
74
75 #[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 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 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
105pub trait Tool: Send + Sync {
107 fn descriptor(&self) -> &ToolDescriptor;
109
110 fn invoke(
112 &self,
113 input: Value,
114 context: ToolContext,
115 ) -> ToolFuture<'_, Result<ToolOutput, ToolError>>;
116}