use serde::{Deserialize, Serialize};
#[cfg(feature = "openapi")]
use utoipa::ToSchema;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum ReasoningText {
Plain { text: String },
Summary { parts: Vec<String> },
Redacted,
}
impl ReasoningText {
pub fn display_text(&self) -> Option<String> {
match self {
Self::Plain { text } if !text.is_empty() => Some(text.clone()),
Self::Summary { parts } if !parts.is_empty() => Some(parts.join("\n\n")),
_ => None,
}
}
pub fn is_raw_chain_of_thought(&self) -> bool {
matches!(self, Self::Plain { .. })
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct ReasoningContentPart {
pub provider: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub item_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub signature: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub encrypted: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub text: Option<ReasoningText>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tokens: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bound_tool_call_id: Option<String>,
}
impl ReasoningContentPart {
pub fn opaque(provider: impl Into<String>) -> Self {
Self {
provider: provider.into(),
item_id: None,
signature: None,
encrypted: None,
text: None,
tokens: None,
bound_tool_call_id: None,
}
}
pub fn with_item_id(mut self, item_id: impl Into<String>) -> Self {
self.item_id = Some(item_id.into());
self
}
pub fn with_signature(mut self, signature: impl Into<String>) -> Self {
self.signature = Some(signature.into());
self
}
pub fn with_encrypted(mut self, encrypted: impl Into<String>) -> Self {
self.encrypted = Some(encrypted.into());
self
}
pub fn with_text(mut self, text: ReasoningText) -> Self {
self.text = Some(text);
self
}
pub fn with_tokens(mut self, tokens: u32) -> Self {
self.tokens = Some(tokens);
self
}
pub fn with_bound_tool_call_id(mut self, tool_call_id: impl Into<String>) -> Self {
self.bound_tool_call_id = Some(tool_call_id.into());
self
}
pub fn display_text(&self) -> Option<String> {
self.text.as_ref().and_then(ReasoningText::display_text)
}
pub fn has_replay_state(&self) -> bool {
self.signature.is_some() || self.encrypted.is_some() || self.item_id.is_some()
}
pub fn to_public(&self) -> Self {
Self {
provider: self.provider.clone(),
item_id: self.item_id.clone(),
signature: None,
encrypted: None,
text: self.text.clone(),
tokens: self.tokens,
bound_tool_call_id: self.bound_tool_call_id.clone(),
}
}
}