vtcode_core/utils/
safety.rs1use crate::config::models::ModelId;
7use crate::ui::user_confirmation::{AgentMode, UserConfirmation};
8use anyhow::Result;
9use console::style;
10
11pub struct SafetyValidator;
13
14impl SafetyValidator {
15 pub fn validate_model_usage(
18 requested_model: &str,
19 task_description: Option<&str>,
20 skip_confirmations: bool,
21 ) -> Result<String> {
22 use crate::config::constants::models;
23 let model_id = match requested_model {
25 s if s == models::GEMINI_2_5_PRO => Some(ModelId::Gemini25Pro),
26 s if s == models::GEMINI_2_5_FLASH => Some(ModelId::Gemini25Flash),
27 s if s == models::GEMINI_2_5_FLASH_LITE => Some(ModelId::Gemini25FlashLite),
28 _ => None,
29 };
30
31 if let Some(ModelId::Gemini25Pro) = model_id {
33 let current_default = ModelId::default();
34
35 if skip_confirmations {
36 println!(
37 "{}",
38 style("Using Gemini 2.5 Pro model (confirmations skipped)").yellow()
39 );
40 return Ok(requested_model.to_string());
41 }
42
43 if let Some(task) = task_description {
44 println!("{}", style("Model Selection Review").cyan().bold());
45 println!("Task: {}", style(task).cyan());
46 println!();
47 }
48
49 let confirmed = UserConfirmation::confirm_pro_model_usage(current_default.as_str())?;
51 if !confirmed {
52 println!(
53 "Falling back to default model: {}",
54 current_default.display_name()
55 );
56 return Ok(current_default.as_str().to_string());
57 }
58 }
59
60 Ok(requested_model.to_string())
61 }
62
63 pub fn validate_agent_mode(
66 _task_description: &str,
67 _skip_confirmations: bool,
68 ) -> Result<AgentMode> {
69 println!(
71 "{}",
72 style("Using single-agent mode with Decision Ledger").green()
73 );
74 Ok(AgentMode::SingleCoder)
75 }
76
77 pub fn is_model_switch_safe(from_model: &str, to_model: &str) -> bool {
79 let from_id = ModelId::from_str(from_model).ok();
80 let to_id = ModelId::from_str(to_model).ok();
81
82 match (from_id, to_id) {
83 (Some(from), Some(to)) => {
84 !matches!(to, ModelId::Gemini25Pro) || matches!(from, ModelId::Gemini25Pro)
86 }
87 _ => true, }
89 }
90
91 pub fn display_safety_recommendations(
93 model: &str,
94 agent_mode: &AgentMode,
95 task_description: Option<&str>,
96 ) {
97 println!("{}", style(" Safety Configuration Summary").cyan().bold());
98 println!("Model: {}", style(model).green());
99 println!("Agent Mode: {}", style(format!("{:?}", agent_mode)).green());
100
101 if let Some(task) = task_description {
102 println!("Task: {}", style(task).cyan());
103 }
104
105 println!();
106
107 use crate::config::constants::models;
109 match model {
110 s if s == models::GEMINI_2_5_PRO => {
111 println!("{}", style("Using most capable model:").yellow());
112 println!("• Highest quality responses");
113 println!("• Higher cost per token");
114 println!("• Slower response times");
115 }
116 s if s == models::GEMINI_2_5_FLASH => {
117 println!("{}", style("[FAST] Using balanced model:").green());
118 println!("• Good quality responses");
119 println!("• Reasonable cost");
120 println!("• Fast response times");
121 }
122 s if s == models::GEMINI_2_5_FLASH_LITE => {
123 println!("{}", style("Using fast model:").blue());
124 println!("• Quick responses");
125 println!("• Most cost-effective");
126 println!("• Good for simple tasks");
127 }
128 _ => {}
129 }
130
131 match agent_mode {
133 AgentMode::SingleCoder => {
134 println!("{}", style("Single-Agent System:").blue());
135 println!("• Streamlined execution");
136 println!("• Decision Ledger tracking");
137 println!("• Lower API costs");
138 println!("• Faster task completion");
139 println!("• Best for most development tasks");
140 }
141 }
142
143 println!();
144 }
145
146 pub fn validate_resource_usage(
148 model: &str,
149 _agent_mode: &AgentMode,
150 estimated_tokens: Option<usize>,
151 ) -> Result<bool> {
152 use crate::config::constants::models;
153 let mut warnings = Vec::new();
154
155 if model == models::GEMINI_2_5_PRO {
157 warnings.push("Using most expensive model (Gemini 2.5 Pro)");
158 }
159
160 if let Some(tokens) = estimated_tokens {
164 if tokens > 10000 {
165 warnings.push("High token usage estimated (>10k tokens)");
166 }
167 }
168
169 if !warnings.is_empty() {
170 println!("{}", style(" Resource Usage Warning").yellow().bold());
171 for warning in &warnings {
172 println!("• {}", warning);
173 }
174 println!();
175
176 let confirmed = UserConfirmation::confirm_action(
177 "Do you want to proceed with these resource usage implications?",
178 false,
179 )?;
180
181 return Ok(confirmed);
182 }
183
184 Ok(true)
185 }
186}
187
188impl ModelId {
190 pub fn from_str(s: &str) -> Result<Self, &'static str> {
192 use crate::config::constants::models;
193 match s {
194 s if s == models::GEMINI_2_5_FLASH_LITE => Ok(ModelId::Gemini25FlashLite),
195 s if s == models::GEMINI_2_5_FLASH => Ok(ModelId::Gemini25Flash),
196 s if s == models::GEMINI_2_5_PRO => Ok(ModelId::Gemini25Pro),
197 _ => Err("Unknown model"),
198 }
199 }
200}