use serde::Deserialize;
use serde::Serialize;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum FinishReasonKind {
Stop,
Length,
ContentFilter,
ToolCalls,
Error,
Other,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FinishReason {
pub unified: FinishReasonKind,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub raw: Option<String>,
}
impl FinishReason {
#[must_use]
pub fn new(unified: FinishReasonKind) -> Self {
Self { unified, raw: None }
}
#[must_use]
pub fn with_raw(unified: FinishReasonKind, raw: impl Into<String>) -> Self {
Self {
unified,
raw: Some(raw.into()),
}
}
#[must_use]
pub fn stop() -> Self {
Self::new(FinishReasonKind::Stop)
}
#[must_use]
pub fn tool_calls() -> Self {
Self::new(FinishReasonKind::ToolCalls)
}
#[must_use]
pub fn error() -> Self {
Self::new(FinishReasonKind::Error)
}
}
impl From<FinishReasonKind> for FinishReason {
fn from(unified: FinishReasonKind) -> Self {
Self::new(unified)
}
}
impl std::fmt::Display for FinishReasonKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let text = match self {
Self::Stop => "stop",
Self::Length => "length",
Self::ContentFilter => "content-filter",
Self::ToolCalls => "tool-calls",
Self::Error => "error",
Self::Other => "other",
};
f.write_str(text)
}
}
impl std::fmt::Display for FinishReason {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.raw {
Some(raw) if raw != &self.unified.to_string() => {
write!(f, "{} ({raw})", self.unified)
}
_ => write!(f, "{}", self.unified),
}
}
}