vtcode_core/utils/
safety.rs1use crate::config::models::ModelId;
7use crate::ui::user_confirmation::{AgentMode, ProModelConfirmationResult, UserConfirmation};
8use crate::utils::colors::style;
9use anyhow::Result;
10use std::sync::atomic::{AtomicBool, Ordering};
11
12static PRO_MODEL_AUTO_ACCEPT: AtomicBool = AtomicBool::new(false);
13
14pub struct SafetyValidator;
16
17impl SafetyValidator {
18 pub fn validate_model_usage(
21 requested_model: &str,
22 task_description: Option<&str>,
23 skip_confirmations: bool,
24 ) -> Result<String> {
25 use crate::config::constants::models;
26 let model_id = match requested_model {
28 s if s == models::google::GEMINI_3_8_FLASH => Some(ModelId::Gemini38Flash),
29 s if s == models::GEMINI_3_8_FLASH => Some(ModelId::Gemini38Flash),
30 _ => None,
31 };
32
33 if let Some(ModelId::Gemini38Flash) = model_id {
35 let current_default = ModelId::default();
36
37 if skip_confirmations {
38 println!("{}", style("Using Gemini 3.1 Pro model (confirmations skipped)").cyan());
39 return Ok(requested_model.to_string());
40 }
41
42 if PRO_MODEL_AUTO_ACCEPT.load(Ordering::Relaxed) {
43 println!("{}", style("Using Gemini 3.1 Pro model (auto-accept enabled)").cyan());
44 return Ok(requested_model.to_string());
45 }
46
47 if let Some(task) = task_description {
48 println!("{}", style("Model Selection Review").cyan().bold());
49 println!("Task: {}", style(task).cyan());
50 println!();
51 }
52
53 match UserConfirmation::confirm_pro_model_usage(¤t_default.as_str())? {
55 ProModelConfirmationResult::Yes => {}
56 ProModelConfirmationResult::YesAutoAccept => {
57 PRO_MODEL_AUTO_ACCEPT.store(true, Ordering::Relaxed);
58 }
59 ProModelConfirmationResult::No => {
60 println!("Falling back to default model: {}", current_default.display_name());
61 return Ok(current_default.to_string());
62 }
63 }
64 }
65
66 Ok(requested_model.to_string())
67 }
68
69 pub fn validate_agent_mode(_task_description: &str, _skip_confirmations: bool) -> Result<AgentMode> {
72 println!("{}", style("Using single-agent mode with Decision Ledger").green());
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::Gemini38Flash) || matches!(from, ModelId::Gemini38Flash)
87 }
88 _ => true, }
90 }
91
92 pub fn display_safety_recommendations(model: &str, agent_mode: &AgentMode, task_description: Option<&str>) {
94 println!("{}", style(" Safety Configuration Summary").cyan().bold());
95 println!("Model: {}", style(model).green());
96 println!("Agent Mode: {}", style(format!("{agent_mode:?}")).green());
97
98 if let Some(task) = task_description {
99 println!("Task: {}", style(task).cyan());
100 }
101
102 println!();
103
104 use crate::config::constants::models;
106 match model {
107 s if s == models::google::GEMINI_3_FLASH_PREVIEW => {
108 println!("{}", style("[FAST] Using balanced model:").green());
109 println!("• Good quality responses");
110 println!("• Reasonable cost");
111 println!("• Fast response times");
112 }
113 s if s == models::google::GEMINI_3_8_FLASH => {
114 println!("{}", style("Using most capable model:").cyan());
115 println!("• Highest quality responses");
116 println!("• Higher cost per token");
117 println!("• Slower response times");
118 }
119 _ => {}
120 }
121
122 match agent_mode {
124 AgentMode::SingleCoder => {
125 println!("{}", style("Single-Agent System:").cyan());
126 println!("• Streamlined execution");
127 println!("• Decision Ledger tracking");
128 println!("• Lower API costs");
129 println!("• Faster task completion");
130 println!("• Best for most development tasks");
131 }
132 }
133
134 println!();
135 }
136
137 pub fn validate_resource_usage(
139 model: &str,
140 _agent_mode: &AgentMode,
141 estimated_tokens: Option<usize>,
142 ) -> Result<bool> {
143 use crate::config::constants::models;
144 let mut warnings = Vec::new();
145
146 if model == models::google::GEMINI_3_8_FLASH {
148 warnings.push("Using most expensive model (Gemini 3 Pro)");
149 }
150
151 if let Some(tokens) = estimated_tokens
155 && tokens > 10000
156 {
157 warnings.push("High token usage estimated (>10k tokens)");
158 }
159
160 if !warnings.is_empty() {
161 println!("{}", style(" Resource Usage Warning").red().bold());
162 for warning in &warnings {
163 println!("• {warning}");
164 }
165 println!();
166
167 let confirmed = UserConfirmation::confirm_action(
168 "Do you want to proceed with these resource usage implications?",
169 false,
170 )?;
171
172 return Ok(confirmed);
173 }
174
175 Ok(true)
176 }
177}