use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Clone)]
pub struct ChatMessage {
pub role: String,
#[serde(flatten)]
pub reasoning: super::WireReasoning,
pub content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_calls: Option<Vec<crate::tool_parser::ToolCall>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub annotations: Option<Vec<Annotation>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub refusal: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum Annotation {
UrlCitation { url_citation: UrlCitation },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UrlCitation {
pub start_index: usize,
pub end_index: usize,
pub url: String,
pub title: String,
}
impl From<crate::citation::Citation> for Annotation {
fn from(c: crate::citation::Citation) -> Self {
Annotation::UrlCitation {
url_citation: UrlCitation {
start_index: c.start_index,
end_index: c.end_index,
url: c.url,
title: c.title,
},
}
}
}
pub fn extract_url_annotations(content: &str) -> Option<Vec<Annotation>> {
let cits = crate::citation::extract_url_citations(content);
if cits.is_empty() {
None
} else {
Some(cits.into_iter().map(Annotation::from).collect())
}
}
pub fn merged_annotations(content: &str) -> Option<Vec<Annotation>> {
crate::citation::merged_citations(content)
.map(|cits| cits.into_iter().map(Annotation::from).collect())
}