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 use std::str::FromStr;
80 let from_id = ModelId::from_str(from_model).ok();
81 let to_id = ModelId::from_str(to_model).ok();
82
83 match (from_id, to_id) {
84 (Some(from), Some(to)) => {
85 !matches!(to, ModelId::Gemini25Pro) || matches!(from, ModelId::Gemini25Pro)
87 }
88 _ => true, }
90 }
91
92 pub fn display_safety_recommendations(
94 model: &str,
95 agent_mode: &AgentMode,
96 task_description: Option<&str>,
97 ) {
98 println!("{}", style(" Safety Configuration Summary").cyan().bold());
99 println!("Model: {}", style(model).green());
100 println!("Agent Mode: {}", style(format!("{:?}", agent_mode)).green());
101
102 if let Some(task) = task_description {
103 println!("Task: {}", style(task).cyan());
104 }
105
106 println!();
107
108 use crate::config::constants::models;
110 match model {
111 s if s == models::GEMINI_2_5_FLASH_PREVIEW => {
112 println!("{}", style("[FAST] Using balanced model:").green());
113 println!("• Good quality responses");
114 println!("• Reasonable cost");
115 println!("• Fast response times");
116 }
117 s if s == models::GEMINI_2_5_PRO => {
118 println!("{}", style("Using most capable model:").yellow());
119 println!("• Highest quality responses");
120 println!("• Higher cost per token");
121 println!("• Slower response times");
122 }
123 _ => {}
124 }
125
126 match agent_mode {
128 AgentMode::SingleCoder => {
129 println!("{}", style("Single-Agent System:").blue());
130 println!("• Streamlined execution");
131 println!("• Decision Ledger tracking");
132 println!("• Lower API costs");
133 println!("• Faster task completion");
134 println!("• Best for most development tasks");
135 }
136 }
137
138 println!();
139 }
140
141 pub fn validate_resource_usage(
143 model: &str,
144 _agent_mode: &AgentMode,
145 estimated_tokens: Option<usize>,
146 ) -> Result<bool> {
147 use crate::config::constants::models;
148 let mut warnings = Vec::new();
149
150 if model == models::GEMINI_2_5_PRO {
152 warnings.push("Using most expensive model (Gemini 2.5 Pro)");
153 }
154
155 if let Some(tokens) = estimated_tokens
159 && tokens > 10000
160 {
161 warnings.push("High token usage estimated (>10k tokens)");
162 }
163
164 if !warnings.is_empty() {
165 println!("{}", style(" Resource Usage Warning").yellow().bold());
166 for warning in &warnings {
167 println!("• {}", warning);
168 }
169 println!();
170
171 let confirmed = UserConfirmation::confirm_action(
172 "Do you want to proceed with these resource usage implications?",
173 false,
174 )?;
175
176 return Ok(confirmed);
177 }
178
179 Ok(true)
180 }
181}
182
183impl ModelId {
185 pub fn parse_from_str(s: &str) -> Result<Self, &'static str> {
187 use std::str::FromStr;
188
189 <Self as FromStr>::from_str(s).map_err(|_| "Unknown model")
190 }
191}