1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
// Source: /data/home/swei/claudecode/openclaudecode/src/utils/thinking.ts
//! Thinking configuration and utilities
//! Translated from /data/home/swei/claudecode/openclaudecode/src/utils/config.ts (thinking section)
//! and /data/home/swei/claudecode/openclaudecode/src/utils/thinking.ts
use std::env;
/// Thinking configuration types
#[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
}
}
/// Check if text contains the "ultrathink" keyword (case insensitive, word boundary)
pub fn has_ultrathink_keyword(text: &str) -> bool {
regex::Regex::new(r"(?i)\bultrathink\b")
.unwrap()
.is_match(text)
}
/// Find positions of "ultrathink" keyword in text for UI highlighting/notification
/// Returns a vector of (word, start, end) tuples
pub fn find_thinking_trigger_positions(text: &str) -> Vec<(String, usize, usize)> {
let mut positions = Vec::new();
// Use fresh /g regex each call — shared regex would leak lastIndex state
let re = regex::Regex::new(r"(?i)\bultrathink\b").unwrap();
for cap in re.find_iter(text) {
positions.push((cap.as_str().to_string(), cap.start(), cap.end()));
}
positions
}
#[cfg(feature = "theme")]
use crate::types::Theme;
/// Get rainbow color for a character index
/// Note: Theme color types need to be defined in the SDK
#[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()]
}
/// Check if ultrathink is enabled
/// This checks the build-time feature flag and runtime GrowthBook flag
/// Note: In Rust, we use environment variable AI_ULTRATHINK instead of bun:bundle feature
pub fn is_ultrathink_enabled() -> bool {
// Check environment variable (AI_ prefix for localization)
// feature('ULTRATHINK') in TS — we check the env var as the runtime gate
if !crate::utils::env_utils::is_env_truthy(std::env::var("AI_ULTRATHINK").ok().as_deref())
&& !crate::utils::env_utils::is_env_truthy(std::env::var("ULTRATHINK").ok().as_deref())
{
return false;
}
// GrowthBook feature check would go here if we have analytics integrated
// For now, we just check the env var
true
}
/// Provider-aware thinking support detection
/// Note: This references getCanonicalName and getAPIProvider which need model utilities
pub fn model_supports_thinking(model: &str) -> bool {
// TODO: Integrate with model capability overrides
// TODO: Check USER_TYPE environment variable
// TODO: Implement getCanonicalName and getAPIProvider
let model_lower = model.to_lowercase();
// Anthropic models: all Claude 4+ support thinking
if model_lower.contains("claude") {
// Check for Claude 3 models (don't support thinking)
if model_lower.contains("claude-3-") || model_lower.contains("claude 3") {
return false;
}
// Claude 4+ supports thinking
return true;
}
// Other providers: check for specific model versions
// Sonnet 4+ and Opus 4+ support thinking
model_lower.contains("sonnet-4") || model_lower.contains("opus-4")
}
/// Check if model supports adaptive thinking
/// Adaptive thinking is supported by a subset of Claude 4 models
pub fn model_supports_adaptive_thinking(model: &str) -> bool {
let canonical = model.to_lowercase();
// Supported by opus-4-6 and sonnet-4-6 variants
if canonical.contains("opus-4-6") || canonical.contains("sonnet-4-6") {
return true;
}
// Exclude legacy models
if canonical.contains("opus") || canonical.contains("sonnet") || canonical.contains("haiku") {
return false;
}
// Default to true for unknown models on first-party and foundry
// (these are proxies that should support adaptive thinking)
// TODO: Check getAPIProvider() - need to implement
true
}
/// Check if thinking should be enabled by default
pub fn should_enable_thinking_by_default() -> bool {
// Check MAX_THINKING_TOKENS environment variable
if let Ok(val) = env::var("MAX_THINKING_TOKENS") {
if let Ok(tokens) = val.parse::<u32>() {
return tokens > 0;
}
}
// Check AI_MAX_THINKING_TOKENS (localized env var)
if let Ok(val) = env::var("AI_MAX_THINKING_TOKENS") {
if let Ok(tokens) = val.parse::<u32>() {
return tokens > 0;
}
}
// Check settings for alwaysThinkingEnabled
// TODO: Integrate with settings system
// For now, default to enabled
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() {
// Without env var, should default to true
assert!(should_enable_thinking_by_default());
}
}