ai_agent/utils/
thinking.rs1use std::env;
7
8#[derive(Debug, Clone)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub enum ThinkingConfig {
12 Adaptive,
13 Enabled { budget_tokens: u32 },
14 Disabled,
15}
16
17impl Default for ThinkingConfig {
18 fn default() -> Self {
19 ThinkingConfig::Disabled
20 }
21}
22
23pub fn has_ultrathink_keyword(text: &str) -> bool {
25 regex::Regex::new(r"(?i)\bultrathink\b")
26 .unwrap()
27 .is_match(text)
28}
29
30pub fn find_thinking_trigger_positions(text: &str) -> Vec<(String, usize, usize)> {
33 let mut positions = Vec::new();
34 let re = regex::Regex::new(r"(?i)\bultrathink\b").unwrap();
36 for cap in re.find_iter(text) {
37 positions.push((cap.as_str().to_string(), cap.start(), cap.end()));
38 }
39 positions
40}
41
42#[cfg(feature = "theme")]
43use crate::types::Theme;
44
45#[cfg(feature = "theme")]
48pub fn get_rainbow_color(char_index: usize, shimmer: bool) -> &'static str {
49 const RAINBOW_COLORS: &[&str] = &[
50 "rainbow_red",
51 "rainbow_orange",
52 "rainbow_yellow",
53 "rainbow_green",
54 "rainbow_blue",
55 "rainbow_indigo",
56 "rainbow_violet",
57 ];
58
59 const RAINBOW_SHIMMER_COLORS: &[&str] = &[
60 "rainbow_red_shimmer",
61 "rainbow_orange_shimmer",
62 "rainbow_yellow_shimmer",
63 "rainbow_green_shimmer",
64 "rainbow_blue_shimmer",
65 "rainbow_indigo_shimmer",
66 "rainbow_violet_shimmer",
67 ];
68
69 let colors = if shimmer {
70 RAINBOW_SHIMMER_COLORS
71 } else {
72 RAINBOW_COLORS
73 };
74 colors[char_index % colors.len()]
75}
76
77pub fn is_ultrathink_enabled() -> bool {
81 if !crate::utils::env_utils::is_env_truthy(std::env::var("AI_ULTRATHINK").ok().as_deref())
84 && !crate::utils::env_utils::is_env_truthy(std::env::var("ULTRATHINK").ok().as_deref())
85 {
86 return false;
87 }
88 true
91}
92
93pub fn model_supports_thinking(model: &str) -> bool {
96 let model_lower = model.to_lowercase();
101
102 if model_lower.contains("claude") {
104 if model_lower.contains("claude-3-") || model_lower.contains("claude 3") {
106 return false;
107 }
108 return true;
110 }
111
112 model_lower.contains("sonnet-4") || model_lower.contains("opus-4")
115}
116
117pub fn model_supports_adaptive_thinking(model: &str) -> bool {
120 let canonical = model.to_lowercase();
121
122 if canonical.contains("opus-4-6") || canonical.contains("sonnet-4-6") {
124 return true;
125 }
126
127 if canonical.contains("opus") || canonical.contains("sonnet") || canonical.contains("haiku") {
129 return false;
130 }
131
132 true
136}
137
138pub fn should_enable_thinking_by_default() -> bool {
140 if let Ok(val) = env::var("MAX_THINKING_TOKENS") {
142 if let Ok(tokens) = val.parse::<u32>() {
143 return tokens > 0;
144 }
145 }
146
147 if let Ok(val) = env::var("AI_MAX_THINKING_TOKENS") {
149 if let Ok(tokens) = val.parse::<u32>() {
150 return tokens > 0;
151 }
152 }
153
154 true
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163
164 #[test]
165 fn test_has_ultrathink_keyword() {
166 assert!(has_ultrathink_keyword("Let's ultrathink this problem"));
167 assert!(has_ultrathink_keyword("ULTRATHINK"));
168 assert!(has_ultrathink_keyword("UltraThink"));
169 assert!(!has_ultrathink_keyword("thinking is good"));
170 }
171
172 #[test]
173 fn test_find_thinking_trigger_positions() {
174 let text = "Use ultrathink for this. Also ULTRATHINK again.";
175 let positions = find_thinking_trigger_positions(text);
176 assert_eq!(positions.len(), 2);
177 assert_eq!(positions[0].0, "ultrathink");
178 assert_eq!(positions[1].0, "ULTRATHINK");
179 }
180
181 #[test]
182 fn test_model_supports_thinking() {
183 assert!(model_supports_thinking("claude-opus-4-20250514"));
184 assert!(model_supports_thinking("claude-sonnet-4-20250514"));
185 assert!(!model_supports_thinking("claude-3-5-sonnet-20241022"));
186 }
187
188 #[test]
189 fn test_model_supports_adaptive_thinking() {
190 assert!(model_supports_adaptive_thinking("opus-4-6-20250514"));
191 assert!(model_supports_adaptive_thinking("sonnet-4-6-20250514"));
192 assert!(!model_supports_adaptive_thinking("claude-3-5-sonnet"));
193 }
194
195 #[test]
196 fn test_should_enable_thinking_by_default() {
197 assert!(should_enable_thinking_by_default());
199 }
200}