use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ThinkingLevel {
Off,
Minimal,
Low,
Medium,
High,
XHigh,
}
impl Default for ThinkingLevel {
fn default() -> Self {
ThinkingLevel::Off
}
}
impl std::fmt::Display for ThinkingLevel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ThinkingLevel::Off => write!(f, "off"),
ThinkingLevel::Minimal => write!(f, "minimal"),
ThinkingLevel::Low => write!(f, "low"),
ThinkingLevel::Medium => write!(f, "medium"),
ThinkingLevel::High => write!(f, "high"),
ThinkingLevel::XHigh => write!(f, "xhigh"),
}
}
}
impl From<&str> for ThinkingLevel {
fn from(s: &str) -> Self {
match s.to_lowercase().as_str() {
"off" => ThinkingLevel::Off,
"minimal" => ThinkingLevel::Minimal,
"low" => ThinkingLevel::Low,
"medium" => ThinkingLevel::Medium,
"high" => ThinkingLevel::High,
"xhigh" => ThinkingLevel::XHigh,
_ => ThinkingLevel::Off,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ThinkingDisplay {
Summarized,
Omitted,
}
impl Default for ThinkingDisplay {
fn default() -> Self {
ThinkingDisplay::Summarized
}
}
impl std::fmt::Display for ThinkingDisplay {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ThinkingDisplay::Summarized => write!(f, "summarized"),
ThinkingDisplay::Omitted => write!(f, "omitted"),
}
}
}
impl From<&str> for ThinkingDisplay {
fn from(s: &str) -> Self {
match s.to_lowercase().as_str() {
"omitted" => ThinkingDisplay::Omitted,
_ => ThinkingDisplay::Summarized,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ThinkingConfig {
pub level: ThinkingLevel,
#[serde(skip_serializing_if = "Option::is_none")]
pub budget_tokens: Option<u32>,
}
impl Default for ThinkingConfig {
fn default() -> Self {
Self {
level: ThinkingLevel::Off,
budget_tokens: None,
}
}
}
impl ThinkingConfig {
pub fn new(level: ThinkingLevel) -> Self {
Self {
level,
budget_tokens: None,
}
}
pub fn with_budget(level: ThinkingLevel, budget_tokens: u32) -> Self {
Self {
level,
budget_tokens: Some(budget_tokens),
}
}
pub fn default_budget(level: ThinkingLevel) -> u32 {
match level {
ThinkingLevel::Off => 0,
ThinkingLevel::Minimal => 128,
ThinkingLevel::Low => 512,
ThinkingLevel::Medium => 1024,
ThinkingLevel::High => 2048,
ThinkingLevel::XHigh => 4096,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct OpenAIThinkingOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub reasoning_effort: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reasoning_summary: Option<String>,
}
impl OpenAIThinkingOptions {
pub fn from_level(level: ThinkingLevel) -> Self {
let effort = match level {
ThinkingLevel::Off => None,
ThinkingLevel::Minimal => Some("minimal".to_string()),
ThinkingLevel::Low => Some("low".to_string()),
ThinkingLevel::Medium => Some("medium".to_string()),
ThinkingLevel::High => Some("high".to_string()),
ThinkingLevel::XHigh => Some("xhigh".to_string()),
};
Self {
reasoning_effort: effort,
reasoning_summary: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AnthropicThinkingOptions {
pub thinking_enabled: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub budget_tokens: Option<u32>,
#[serde(default)]
pub adaptive: bool,
}
impl AnthropicThinkingOptions {
pub fn from_config(config: &ThinkingConfig) -> Self {
Self {
thinking_enabled: config.level != ThinkingLevel::Off,
budget_tokens: config.budget_tokens.or_else(|| {
if config.level == ThinkingLevel::Off {
None
} else {
Some(ThinkingConfig::default_budget(config.level))
}
}),
adaptive: false,
}
}
pub fn adaptive(budget_tokens: Option<u32>) -> Self {
Self {
thinking_enabled: true,
budget_tokens,
adaptive: true,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct GoogleThinkingOptions {
pub enabled: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub budget_tokens: Option<u32>,
}
impl GoogleThinkingOptions {
pub fn from_config(config: &ThinkingConfig) -> Self {
Self {
enabled: config.level != ThinkingLevel::Off,
budget_tokens: config.budget_tokens,
}
}
}