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_PREVIEW => Some(ModelId::Gemini25FlashPreview),
27 s if s == models::GEMINI_2_5_PRO => Some(ModelId::Gemini25Pro),
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_FLASH_PREVIEW => {
111 println!("{}", style("[FAST] Using balanced model:").green());
112 println!("• Good quality responses");
113 println!("• Reasonable cost");
114 println!("• Fast response times");
115 }
116 s if s == models::GEMINI_2_5_PRO => {
117 println!("{}", style("Using most capable model:").yellow());
118 println!("• Highest quality responses");
119 println!("• Higher cost per token");
120 println!("• Slower response times");
121 }
122 _ => {}
123 }
124
125 match agent_mode {
127 AgentMode::SingleCoder => {
128 println!("{}", style("Single-Agent System:").blue());
129 println!("• Streamlined execution");
130 println!("• Decision Ledger tracking");
131 println!("• Lower API costs");
132 println!("• Faster task completion");
133 println!("• Best for most development tasks");
134 }
135 }
136
137 println!();
138 }
139
140 pub fn validate_resource_usage(
142 model: &str,
143 _agent_mode: &AgentMode,
144 estimated_tokens: Option<usize>,
145 ) -> Result<bool> {
146 use crate::config::constants::models;
147 let mut warnings = Vec::new();
148
149 if model == models::GEMINI_2_5_PRO {
151 warnings.push("Using most expensive model (Gemini 2.5 Pro)");
152 }
153
154 if let Some(tokens) = estimated_tokens {
158 if tokens > 10000 {
159 warnings.push("High token usage estimated (>10k tokens)");
160 }
161 }
162
163 if !warnings.is_empty() {
164 println!("{}", style(" Resource Usage Warning").yellow().bold());
165 for warning in &warnings {
166 println!("• {}", warning);
167 }
168 println!();
169
170 let confirmed = UserConfirmation::confirm_action(
171 "Do you want to proceed with these resource usage implications?",
172 false,
173 )?;
174
175 return Ok(confirmed);
176 }
177
178 Ok(true)
179 }
180}
181
182impl ModelId {
184 pub fn from_str(s: &str) -> Result<Self, &'static str> {
186 use std::str::FromStr;
187
188 <Self as FromStr>::from_str(s).map_err(|_| "Unknown model")
189 }
190}