use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use crate::session::SessionId;
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum Content {
Text {
text: String,
},
Image {
data: String,
mime_type: String,
},
Detail {
data: serde_json::Value,
},
}
impl Content {
pub fn text(s: impl Into<String>) -> Self {
Content::Text { text: s.into() }
}
pub fn image(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
Content::Image {
data: data.into(),
mime_type: mime_type.into(),
}
}
pub fn detail(data: serde_json::Value) -> Self {
Content::Detail { data }
}
}
impl From<Content> for Vec<Content> {
fn from(c: Content) -> Self {
vec![c]
}
}
pub fn content_text(contents: &[Content]) -> String {
contents
.iter()
.filter_map(|c| match c {
Content::Text { text } => Some(text.as_str()),
Content::Image { .. } | Content::Detail { .. } => None,
})
.collect::<Vec<_>>()
.join("\n")
}
pub fn content_details(contents: &[Content]) -> Option<serde_json::Value> {
let details: Vec<&serde_json::Value> = contents
.iter()
.filter_map(|c| match c {
Content::Detail { data } => Some(data),
_ => None,
})
.collect();
match details.len() {
0 => None,
1 => Some(details[0].clone()),
_ => {
let mut merged = serde_json::Map::new();
for d in details {
if let Some(obj) = d.as_object() {
for (k, v) in obj {
merged.insert(k.clone(), v.clone());
}
}
}
Some(serde_json::Value::Object(merged))
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ToolMetadata {
pub name: String,
pub description: String,
pub origin: String,
pub version: String,
pub requirements: Vec<String>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ToolExposure {
Direct,
Deferred,
Hidden,
}
#[derive(Clone, Debug)]
pub struct ActivationContext {
pub session_id: SessionId,
pub current_tools: Vec<String>,
pub workspace: PathBuf,
}