use serde_json::Value;
use crate::types::anthropic::{OutputConfig, ThinkingParam};
use crate::types::common::{EffortLevel, ThinkingConfig};
pub fn build_thinking_params(
config: Option<&ThinkingConfig>,
) -> (Option<ThinkingParam>, Option<OutputConfig>) {
match config {
Some(ThinkingConfig::Adaptive { effort }) => {
let output_config = if *effort == EffortLevel::High {
None
} else {
Some(OutputConfig {
effort: effort.as_str().to_string(),
})
};
(Some(ThinkingParam::Adaptive), output_config)
}
Some(ThinkingConfig::Enabled { budget_tokens }) => (
Some(ThinkingParam::Enabled {
budget_tokens: *budget_tokens,
}),
None,
),
None => (None, None),
}
}
pub fn build_thinking_params_json(
config: Option<&ThinkingConfig>,
) -> (Option<Value>, Option<Value>) {
let (thinking, output_config) = build_thinking_params(config);
(
thinking.and_then(|tp| serde_json::to_value(&tp).ok()),
output_config.and_then(|oc| serde_json::to_value(&oc).ok()),
)
}
pub fn parse_model_suffix(model: &str) -> (String, Option<String>) {
let Some(open_paren) = model.rfind('(') else {
return (model.to_string(), None);
};
if !model.ends_with(')') {
return (model.to_string(), None);
}
let base_model = &model[..open_paren];
let suffix = &model[open_paren + 1..model.len() - 1];
let is_valid = matches!(
suffix.to_lowercase().as_str(),
"none"
| "off"
| "disabled"
| "low"
| "minimal"
| "medium"
| "med"
| "high"
| "xhigh"
| "max"
| "auto"
) || suffix.parse::<u32>().is_ok();
if is_valid {
(base_model.to_string(), Some(suffix.to_string()))
} else {
(model.to_string(), None)
}
}
pub fn supports_adaptive_thinking(model: &str) -> bool {
let lower = model.to_lowercase();
lower.contains("opus-4-6")
|| lower.contains("sonnet-4-6")
|| lower.starts_with("claude-opus-4-6")
|| lower.starts_with("claude-sonnet-4-6")
}
pub fn build_thinking_for_model(model: &str, effort: &str) -> Option<ThinkingConfig> {
let effort_lower = effort.to_lowercase();
if matches!(effort_lower.as_str(), "none" | "off" | "disabled") {
return None;
}
if supports_adaptive_thinking(model) {
let level = match effort_lower.as_str() {
"low" | "minimal" => EffortLevel::Low,
"medium" | "med" | "auto" => EffortLevel::Medium,
"high" => EffortLevel::High,
"xhigh" | "max" => EffortLevel::Max,
_ => {
if let Ok(n) = effort.parse::<u32>() {
match n {
0 => return None,
1..=2048 => EffortLevel::Low,
2049..=16384 => EffortLevel::Medium,
16385..=49152 => EffortLevel::High,
_ => EffortLevel::Max,
}
} else {
EffortLevel::High
}
}
};
Some(ThinkingConfig::Adaptive { effort: level })
} else {
let budget_tokens = match effort_lower.as_str() {
"low" | "minimal" => 1024,
"medium" | "med" => 8192,
"high" => 32000,
"xhigh" | "max" => 64000,
"auto" => 16000,
_ => effort.parse::<u32>().unwrap_or(8192),
};
Some(ThinkingConfig::Enabled { budget_tokens })
}
}