use serde::{Deserialize, Serialize};
use crate::ir::cache::CacheControl;
use crate::ir::provider_echo::ProviderEchoSnapshot;
use crate::ir::source::{CitationSource, MediaSource};
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ContentPart {
Text {
text: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
cache_control: Option<CacheControl>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
provider_echoes: Vec<ProviderEchoSnapshot>,
},
Image {
source: MediaSource,
#[serde(default, skip_serializing_if = "Option::is_none")]
cache_control: Option<CacheControl>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
provider_echoes: Vec<ProviderEchoSnapshot>,
},
Audio {
source: MediaSource,
#[serde(default, skip_serializing_if = "Option::is_none")]
cache_control: Option<CacheControl>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
provider_echoes: Vec<ProviderEchoSnapshot>,
},
Video {
source: MediaSource,
#[serde(default, skip_serializing_if = "Option::is_none")]
cache_control: Option<CacheControl>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
provider_echoes: Vec<ProviderEchoSnapshot>,
},
Document {
source: MediaSource,
name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
cache_control: Option<CacheControl>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
provider_echoes: Vec<ProviderEchoSnapshot>,
},
Thinking {
text: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
cache_control: Option<CacheControl>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
provider_echoes: Vec<ProviderEchoSnapshot>,
},
RedactedThinking {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
provider_echoes: Vec<ProviderEchoSnapshot>,
},
Citation {
snippet: String,
source: CitationSource,
#[serde(default, skip_serializing_if = "Option::is_none")]
cache_control: Option<CacheControl>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
provider_echoes: Vec<ProviderEchoSnapshot>,
},
ToolUse {
id: String,
name: String,
input: serde_json::Value,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
provider_echoes: Vec<ProviderEchoSnapshot>,
},
ImageOutput {
source: MediaSource,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
provider_echoes: Vec<ProviderEchoSnapshot>,
},
AudioOutput {
source: MediaSource,
transcript: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
provider_echoes: Vec<ProviderEchoSnapshot>,
},
ToolResult {
tool_use_id: String,
name: String,
content: ToolResultContent,
#[serde(default)]
is_error: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
cache_control: Option<CacheControl>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
provider_echoes: Vec<ProviderEchoSnapshot>,
},
}
impl ContentPart {
#[must_use]
pub fn text(text: impl Into<String>) -> Self {
Self::Text {
text: text.into(),
cache_control: None,
provider_echoes: Vec::new(),
}
}
#[must_use]
pub fn image(source: MediaSource) -> Self {
Self::Image {
source,
cache_control: None,
provider_echoes: Vec::new(),
}
}
#[must_use]
pub fn audio(source: MediaSource) -> Self {
Self::Audio {
source,
cache_control: None,
provider_echoes: Vec::new(),
}
}
#[must_use]
pub fn video(source: MediaSource) -> Self {
Self::Video {
source,
cache_control: None,
provider_echoes: Vec::new(),
}
}
#[must_use]
pub fn document(source: MediaSource, name: Option<String>) -> Self {
Self::Document {
source,
name,
cache_control: None,
provider_echoes: Vec::new(),
}
}
#[must_use]
pub fn thinking(text: impl Into<String>) -> Self {
Self::Thinking {
text: text.into(),
cache_control: None,
provider_echoes: Vec::new(),
}
}
#[must_use]
pub fn redacted_thinking() -> Self {
Self::RedactedThinking {
provider_echoes: Vec::new(),
}
}
#[must_use]
pub const fn cache_control(&self) -> Option<&CacheControl> {
match self {
Self::Text { cache_control, .. }
| Self::Image { cache_control, .. }
| Self::Audio { cache_control, .. }
| Self::Video { cache_control, .. }
| Self::Document { cache_control, .. }
| Self::Thinking { cache_control, .. }
| Self::Citation { cache_control, .. }
| Self::ToolResult { cache_control, .. } => cache_control.as_ref(),
Self::ToolUse { .. }
| Self::ImageOutput { .. }
| Self::AudioOutput { .. }
| Self::RedactedThinking { .. } => None,
}
}
#[must_use]
pub fn provider_echoes(&self) -> &[ProviderEchoSnapshot] {
match self {
Self::Text {
provider_echoes, ..
}
| Self::Image {
provider_echoes, ..
}
| Self::Audio {
provider_echoes, ..
}
| Self::Video {
provider_echoes, ..
}
| Self::Document {
provider_echoes, ..
}
| Self::Thinking {
provider_echoes, ..
}
| Self::RedactedThinking { provider_echoes }
| Self::Citation {
provider_echoes, ..
}
| Self::ToolUse {
provider_echoes, ..
}
| Self::ImageOutput {
provider_echoes, ..
}
| Self::AudioOutput {
provider_echoes, ..
}
| Self::ToolResult {
provider_echoes, ..
} => provider_echoes,
}
}
#[must_use]
pub fn with_cache_control(self, cache: CacheControl) -> Self {
match self {
Self::Text {
text,
provider_echoes,
..
} => Self::Text {
text,
cache_control: Some(cache),
provider_echoes,
},
Self::Image {
source,
provider_echoes,
..
} => Self::Image {
source,
cache_control: Some(cache),
provider_echoes,
},
Self::Audio {
source,
provider_echoes,
..
} => Self::Audio {
source,
cache_control: Some(cache),
provider_echoes,
},
Self::Video {
source,
provider_echoes,
..
} => Self::Video {
source,
cache_control: Some(cache),
provider_echoes,
},
Self::Document {
source,
name,
provider_echoes,
..
} => Self::Document {
source,
name,
cache_control: Some(cache),
provider_echoes,
},
Self::Thinking {
text,
provider_echoes,
..
} => Self::Thinking {
text,
cache_control: Some(cache),
provider_echoes,
},
Self::Citation {
snippet,
source,
provider_echoes,
..
} => Self::Citation {
snippet,
source,
cache_control: Some(cache),
provider_echoes,
},
Self::ToolResult {
tool_use_id,
name,
content,
is_error,
provider_echoes,
..
} => Self::ToolResult {
tool_use_id,
name,
content,
is_error,
cache_control: Some(cache),
provider_echoes,
},
other @ (Self::ToolUse { .. }
| Self::ImageOutput { .. }
| Self::AudioOutput { .. }
| Self::RedactedThinking { .. }) => other,
}
}
#[must_use]
pub fn with_provider_echo(mut self, echo: ProviderEchoSnapshot) -> Self {
match &mut self {
Self::Text {
provider_echoes, ..
}
| Self::Image {
provider_echoes, ..
}
| Self::Audio {
provider_echoes, ..
}
| Self::Video {
provider_echoes, ..
}
| Self::Document {
provider_echoes, ..
}
| Self::Thinking {
provider_echoes, ..
}
| Self::RedactedThinking { provider_echoes }
| Self::Citation {
provider_echoes, ..
}
| Self::ToolUse {
provider_echoes, ..
}
| Self::ImageOutput {
provider_echoes, ..
}
| Self::AudioOutput {
provider_echoes, ..
}
| Self::ToolResult {
provider_echoes, ..
} => provider_echoes.push(echo),
}
self
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
#[non_exhaustive]
pub enum ToolResultContent {
Text(String),
Json(serde_json::Value),
}