use std::env;
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum ThinkingConfig {
Adaptive,
Enabled { budget_tokens: u32 },
Disabled,
}
impl Default for ThinkingConfig {
fn default() -> Self {
ThinkingConfig::Disabled
}
}
pub fn has_ultrathink_keyword(text: &str) -> bool {
text.to_lowercase().contains("ultrathink")
}
pub fn find_thinking_trigger_positions(text: &str) -> Vec<(String, usize, usize)> {
let mut positions = Vec::new();
let text_lower = text.to_lowercase();
let search_term = "ultrathink";
let mut start = 0;
while let Some(pos) = text_lower[start..].find(search_term) {
let actual_start = start + pos;
let actual_end = actual_start + search_term.len();
let word = text[actual_start..actual_end].to_string();
positions.push((word, actual_start, actual_end));
start = actual_end;
}
positions
}
#[cfg(feature = "theme")]
use crate::types::Theme;
#[cfg(feature = "theme")]
pub fn get_rainbow_color(char_index: usize, shimmer: bool) -> &'static str {
const RAINBOW_COLORS: &[&str] = &[
"rainbow_red",
"rainbow_orange",
"rainbow_yellow",
"rainbow_green",
"rainbow_blue",
"rainbow_indigo",
"rainbow_violet",
];
const RAINBOW_SHIMMER_COLORS: &[&str] = &[
"rainbow_red_shimmer",
"rainbow_orange_shimmer",
"rainbow_yellow_shimmer",
"rainbow_green_shimmer",
"rainbow_blue_shimmer",
"rainbow_indigo_shimmer",
"rainbow_violet_shimmer",
];
let colors = if shimmer {
RAINBOW_SHIMMER_COLORS
} else {
RAINBOW_COLORS
};
colors[char_index % colors.len()]
}
pub fn is_ultrathink_enabled() -> bool {
if env::var("AI_ULTRATHINK").is_err() && env::var("ULTRATHINK").is_err() {
return false;
}
true
}
pub fn model_supports_thinking(model: &str) -> bool {
let model_lower = model.to_lowercase();
if model_lower.contains("claude") {
if model_lower.contains("claude-3-") || model_lower.contains("claude 3") {
return false;
}
return true;
}
model_lower.contains("sonnet-4") || model_lower.contains("opus-4")
}
pub fn model_supports_adaptive_thinking(model: &str) -> bool {
let canonical = model.to_lowercase();
if canonical.contains("opus-4-6") || canonical.contains("sonnet-4-6") {
return true;
}
if canonical.contains("opus") || canonical.contains("sonnet") || canonical.contains("haiku") {
return false;
}
true
}
pub fn should_enable_thinking_by_default() -> bool {
if let Ok(val) = env::var("MAX_THINKING_TOKENS") {
if let Ok(tokens) = val.parse::<u32>() {
return tokens > 0;
}
}
if let Ok(val) = env::var("AI_MAX_THINKING_TOKENS") {
if let Ok(tokens) = val.parse::<u32>() {
return tokens > 0;
}
}
true
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_has_ultrathink_keyword() {
assert!(has_ultrathink_keyword("Let's ultrathink this problem"));
assert!(has_ultrathink_keyword("ULTRATHINK"));
assert!(has_ultrathink_keyword("UltraThink"));
assert!(!has_ultrathink_keyword("thinking is good"));
}
#[test]
fn test_find_thinking_trigger_positions() {
let text = "Use ultrathink for this. Also ULTRATHINK again.";
let positions = find_thinking_trigger_positions(text);
assert_eq!(positions.len(), 2);
assert_eq!(positions[0].0, "ultrathink");
assert_eq!(positions[1].0, "ULTRATHINK");
}
#[test]
fn test_model_supports_thinking() {
assert!(model_supports_thinking("claude-opus-4-20250514"));
assert!(model_supports_thinking("claude-sonnet-4-20250514"));
assert!(!model_supports_thinking("claude-3-5-sonnet-20241022"));
}
#[test]
fn test_model_supports_adaptive_thinking() {
assert!(model_supports_adaptive_thinking("opus-4-6-20250514"));
assert!(model_supports_adaptive_thinking("sonnet-4-6-20250514"));
assert!(!model_supports_adaptive_thinking("claude-3-5-sonnet"));
}
#[test]
fn test_should_enable_thinking_by_default() {
assert!(should_enable_thinking_by_default());
}
}