use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
use crate::{
ToolError,
interaction::{ToolInteractionRequest, ToolInteractionResponse},
};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ToolInvocation {
pub call_id: Option<String>,
pub arguments: serde_json::Value,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case", deny_unknown_fields)]
pub enum ToolContent {
Text {
text: String,
metadata: Option<serde_json::Value>,
},
Json {
value: serde_json::Value,
metadata: Option<serde_json::Value>,
},
Image {
mime_type: String,
data_base64: String,
metadata: Option<serde_json::Value>,
},
Audio {
mime_type: String,
data_base64: String,
metadata: Option<serde_json::Value>,
},
ResourceLink {
uri: String,
title: Option<String>,
metadata: serde_json::Value,
},
EmbeddedResource {
uri: String,
contents: serde_json::Value,
metadata: serde_json::Value,
},
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ToolResult {
pub content: Vec<ToolContent>,
pub structured_content: Option<serde_json::Value>,
pub is_error: bool,
pub metadata: BTreeMap<String, serde_json::Value>,
}
impl ToolResult {
pub fn new(content: Vec<ToolContent>) -> Result<Self, ToolError> {
if content.is_empty() {
return Err(ToolError::InvalidOutput {
message: "tool result content must not be empty".to_owned(),
issues: Vec::new(),
});
}
Ok(Self {
content,
structured_content: None,
is_error: false,
metadata: BTreeMap::new(),
})
}
#[must_use]
pub fn text(text: impl Into<String>) -> Self {
Self {
content: vec![ToolContent::Text {
text: text.into(),
metadata: None,
}],
structured_content: None,
is_error: false,
metadata: BTreeMap::new(),
}
}
#[must_use]
pub fn json(value: serde_json::Value) -> Self {
Self {
content: vec![ToolContent::Json {
value: value.clone(),
metadata: None,
}],
structured_content: Some(value),
is_error: false,
metadata: BTreeMap::new(),
}
}
#[must_use]
pub fn with_error(mut self, is_error: bool) -> Self {
self.is_error = is_error;
self
}
#[must_use]
pub fn with_structured_content(mut self, value: serde_json::Value) -> Self {
self.structured_content = Some(value);
self
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case", deny_unknown_fields)]
pub enum ToolHandlerInput {
Invoke(ToolInvocation),
Interaction(ToolInteractionResponse),
Cancel {
reason: Option<String>,
},
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case", deny_unknown_fields)]
pub enum ToolHandlerOutput {
Completed(ToolResult),
Interaction(ToolInteractionRequest),
}