vtcode_config/types/
mod.rs

1//! Common types and interfaces used throughout the application
2
3use crate::constants::reasoning;
4use crate::core::PromptCachingConfig;
5use serde::{Deserialize, Deserializer, Serialize};
6use serde_json::Value;
7use std::collections::{BTreeMap, HashMap};
8use std::fmt;
9use std::path::PathBuf;
10
11/// Supported reasoning effort levels configured via vtcode.toml
12/// These map to different provider-specific parameters:
13/// - For Gemini 3 Pro: Maps to thinking_level (low, high) - medium coming soon
14/// - For other models: Maps to provider-specific reasoning parameters
15#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
17#[serde(rename_all = "lowercase")]
18#[derive(Default)]
19pub enum ReasoningEffortLevel {
20    /// No reasoning configuration - for models that don't support configurable reasoning
21    None,
22    /// Minimal reasoning effort - maps to low thinking level for Gemini 3 Pro
23    Minimal,
24    /// Low reasoning effort - maps to low thinking level for Gemini 3 Pro
25    Low,
26    /// Medium reasoning effort - Note: Not fully available for Gemini 3 Pro yet, defaults to high
27    #[default]
28    Medium,
29    /// High reasoning effort - maps to high thinking level for Gemini 3 Pro
30    High,
31}
32
33impl ReasoningEffortLevel {
34    /// Return the textual representation expected by downstream APIs
35    pub fn as_str(self) -> &'static str {
36        match self {
37            Self::None => "none",
38            Self::Minimal => "minimal",
39            Self::Low => reasoning::LOW,
40            Self::Medium => reasoning::MEDIUM,
41            Self::High => reasoning::HIGH,
42        }
43    }
44
45    /// Attempt to parse an effort level from user configuration input
46    pub fn parse(value: &str) -> Option<Self> {
47        let normalized = value.trim();
48        if normalized.eq_ignore_ascii_case("none") {
49            Some(Self::None)
50        } else if normalized.eq_ignore_ascii_case("minimal") {
51            Some(Self::Minimal)
52        } else if normalized.eq_ignore_ascii_case(reasoning::LOW) {
53            Some(Self::Low)
54        } else if normalized.eq_ignore_ascii_case(reasoning::MEDIUM) {
55            Some(Self::Medium)
56        } else if normalized.eq_ignore_ascii_case(reasoning::HIGH) {
57            Some(Self::High)
58        } else {
59            None
60        }
61    }
62
63    /// Enumerate the allowed configuration values for validation and messaging
64    pub fn allowed_values() -> &'static [&'static str] {
65        reasoning::ALLOWED_LEVELS
66    }
67}
68
69/// Verbosity level for model output (GPT-5.1 and compatible models)
70#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
71#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
72#[serde(rename_all = "lowercase")]
73#[derive(Default)]
74pub enum VerbosityLevel {
75    Low,
76    #[default]
77    Medium,
78    High,
79}
80
81impl VerbosityLevel {
82    /// Return the textual representation expected by downstream APIs
83    pub fn as_str(self) -> &'static str {
84        match self {
85            Self::Low => "low",
86            Self::Medium => "medium",
87            Self::High => "high",
88        }
89    }
90
91    /// Attempt to parse a verbosity level from user configuration input
92    pub fn parse(value: &str) -> Option<Self> {
93        let normalized = value.trim();
94        if normalized.eq_ignore_ascii_case("low") {
95            Some(Self::Low)
96        } else if normalized.eq_ignore_ascii_case("medium") {
97            Some(Self::Medium)
98        } else if normalized.eq_ignore_ascii_case("high") {
99            Some(Self::High)
100        } else {
101            None
102        }
103    }
104
105    /// Enumerate the allowed configuration values
106    pub fn allowed_values() -> &'static [&'static str] {
107        &["low", "medium", "high"]
108    }
109}
110
111impl fmt::Display for VerbosityLevel {
112    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113        f.write_str(self.as_str())
114    }
115}
116
117impl<'de> Deserialize<'de> for VerbosityLevel {
118    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
119    where
120        D: Deserializer<'de>,
121    {
122        let raw = String::deserialize(deserializer)?;
123        if let Some(parsed) = Self::parse(&raw) {
124            Ok(parsed)
125        } else {
126            tracing::warn!(
127                input = raw,
128                allowed = ?Self::allowed_values(),
129                "Invalid verbosity level provided; falling back to default"
130            );
131            Ok(Self::default())
132        }
133    }
134}
135
136impl fmt::Display for ReasoningEffortLevel {
137    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138        f.write_str(self.as_str())
139    }
140}
141
142impl<'de> Deserialize<'de> for ReasoningEffortLevel {
143    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
144    where
145        D: Deserializer<'de>,
146    {
147        let raw = String::deserialize(deserializer)?;
148        if let Some(parsed) = Self::parse(&raw) {
149            Ok(parsed)
150        } else {
151            tracing::warn!(
152                input = raw,
153                allowed = ?Self::allowed_values(),
154                "Invalid reasoning effort level provided; falling back to default"
155            );
156            Ok(Self::default())
157        }
158    }
159}
160
161/// Preferred rendering surface for the interactive chat UI
162#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
163#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
164#[serde(rename_all = "lowercase")]
165#[derive(Default)]
166pub enum UiSurfacePreference {
167    #[default]
168    Auto,
169    Alternate,
170    Inline,
171}
172
173impl UiSurfacePreference {
174    /// String representation used in configuration and logging
175    pub fn as_str(self) -> &'static str {
176        match self {
177            Self::Auto => "auto",
178            Self::Alternate => "alternate",
179            Self::Inline => "inline",
180        }
181    }
182
183    /// Parse a surface preference from configuration input
184    pub fn parse(value: &str) -> Option<Self> {
185        let normalized = value.trim();
186        if normalized.eq_ignore_ascii_case("auto") {
187            Some(Self::Auto)
188        } else if normalized.eq_ignore_ascii_case("alternate")
189            || normalized.eq_ignore_ascii_case("alt")
190        {
191            Some(Self::Alternate)
192        } else if normalized.eq_ignore_ascii_case("inline") {
193            Some(Self::Inline)
194        } else {
195            None
196        }
197    }
198
199    /// Enumerate the accepted configuration values for validation messaging
200    pub fn allowed_values() -> &'static [&'static str] {
201        &["auto", "alternate", "inline"]
202    }
203}
204
205impl fmt::Display for UiSurfacePreference {
206    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207        f.write_str(self.as_str())
208    }
209}
210
211impl<'de> Deserialize<'de> for UiSurfacePreference {
212    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
213    where
214        D: Deserializer<'de>,
215    {
216        let raw = String::deserialize(deserializer)?;
217        if let Some(parsed) = Self::parse(&raw) {
218            Ok(parsed)
219        } else {
220            tracing::warn!(
221                input = raw,
222                allowed = ?Self::allowed_values(),
223                "Invalid UI surface preference provided; falling back to default"
224            );
225            Ok(Self::default())
226        }
227    }
228}
229
230/// Source describing how the active model was selected
231#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
232pub enum ModelSelectionSource {
233    /// Model provided by workspace configuration
234    #[default]
235    WorkspaceConfig,
236    /// Model provided by CLI override
237    CliOverride,
238}
239
240/// Configuration for the agent
241#[derive(Debug, Clone)]
242pub struct AgentConfig {
243    pub model: String,
244    pub api_key: String,
245    pub provider: String,
246    pub api_key_env: String,
247    pub workspace: std::path::PathBuf,
248    pub verbose: bool,
249    pub theme: String,
250    pub reasoning_effort: ReasoningEffortLevel,
251    pub ui_surface: UiSurfacePreference,
252    pub prompt_cache: PromptCachingConfig,
253    pub model_source: ModelSelectionSource,
254    pub custom_api_keys: BTreeMap<String, String>,
255    pub checkpointing_enabled: bool,
256    pub checkpointing_storage_dir: Option<PathBuf>,
257    pub checkpointing_max_snapshots: usize,
258    pub checkpointing_max_age_days: Option<u64>,
259}
260
261/// Workshop agent capability levels
262#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
263pub enum CapabilityLevel {
264    /// Basic chat only
265    Basic,
266    /// Can read files
267    FileReading,
268    /// Can read files and list directories
269    FileListing,
270    /// Can read files, list directories, and run bash commands
271    Bash,
272    /// Can read files, list directories, run bash commands, and edit files
273    Editing,
274    /// Full capabilities including code search
275    CodeSearch,
276}
277
278/// Session information
279#[derive(Debug, Clone, Serialize, Deserialize)]
280pub struct SessionInfo {
281    pub session_id: String,
282    pub start_time: u64,
283    pub total_turns: usize,
284    pub total_decisions: usize,
285    pub error_count: usize,
286}
287
288/// Conversation turn information
289#[derive(Debug, Clone, Serialize, Deserialize)]
290pub struct ConversationTurn {
291    pub turn_number: usize,
292    pub timestamp: u64,
293    pub user_input: Option<String>,
294    pub agent_response: Option<String>,
295    pub tool_calls: Vec<ToolCallInfo>,
296    pub decision: Option<DecisionInfo>,
297}
298
299/// Tool call information
300#[derive(Debug, Clone, Serialize, Deserialize)]
301pub struct ToolCallInfo {
302    pub name: String,
303    pub args: Value,
304    pub result: Option<Value>,
305    pub error: Option<String>,
306    pub execution_time_ms: Option<u64>,
307}
308
309/// Decision information for tracking
310#[derive(Debug, Clone, Serialize, Deserialize)]
311pub struct DecisionInfo {
312    pub turn_number: usize,
313    pub action_type: String,
314    pub description: String,
315    pub reasoning: String,
316    pub outcome: Option<String>,
317    pub confidence_score: Option<f64>,
318    pub timestamp: u64,
319}
320
321/// Error information for tracking
322#[derive(Debug, Clone, Serialize, Deserialize)]
323pub struct ErrorInfo {
324    pub error_type: String,
325    pub message: String,
326    pub turn_number: usize,
327    pub recoverable: bool,
328    pub timestamp: u64,
329}
330
331/// Task information for project workflows
332#[derive(Debug, Clone, Serialize, Deserialize)]
333pub struct TaskInfo {
334    pub task_type: String,
335    pub description: String,
336    pub completed: bool,
337    pub success: bool,
338    pub duration_seconds: Option<u64>,
339    pub tools_used: Vec<String>,
340    pub dependencies: Vec<String>,
341}
342
343/// Project creation specification
344#[derive(Debug, Clone, Serialize, Deserialize)]
345pub struct ProjectSpec {
346    pub name: String,
347    pub features: Vec<String>,
348    pub template: Option<String>,
349    pub dependencies: HashMap<String, String>,
350}
351
352/// Workspace analysis result
353#[derive(Debug, Clone, Serialize, Deserialize)]
354pub struct WorkspaceAnalysis {
355    pub root_path: String,
356    pub project_type: Option<String>,
357    pub languages: Vec<String>,
358    pub frameworks: Vec<String>,
359    pub config_files: Vec<String>,
360    pub source_files: Vec<String>,
361    pub test_files: Vec<String>,
362    pub documentation_files: Vec<String>,
363    pub total_files: usize,
364    pub total_size_bytes: u64,
365}
366
367/// Command execution result
368#[derive(Debug, Clone, Serialize, Deserialize)]
369pub struct CommandResult {
370    pub command: String,
371    pub success: bool,
372    pub stdout: String,
373    pub stderr: String,
374    pub exit_code: Option<i32>,
375    pub execution_time_ms: u64,
376}
377
378/// File operation result
379#[derive(Debug, Clone, Serialize, Deserialize)]
380pub struct FileOperationResult {
381    pub operation: String,
382    pub path: String,
383    pub success: bool,
384    pub details: HashMap<String, Value>,
385    pub error: Option<String>,
386}
387
388/// Performance metrics
389#[derive(Debug, Clone, Serialize, Deserialize)]
390pub struct PerformanceMetrics {
391    pub session_duration_seconds: u64,
392    pub total_api_calls: usize,
393    pub total_tokens_used: Option<usize>,
394    pub average_response_time_ms: f64,
395    pub tool_execution_count: usize,
396    pub error_count: usize,
397    pub recovery_success_rate: f64,
398}
399
400/// Quality metrics for agent actions
401#[derive(Debug, Clone, Serialize, Deserialize)]
402pub struct QualityMetrics {
403    pub decision_confidence_avg: f64,
404    pub tool_success_rate: f64,
405    pub error_recovery_rate: f64,
406    pub context_preservation_rate: f64,
407    pub user_satisfaction_score: Option<f64>,
408}
409
410/// Configuration for tool behavior
411#[derive(Debug, Clone, Serialize, Deserialize)]
412pub struct ToolConfig {
413    pub enable_validation: bool,
414    pub max_execution_time_seconds: u64,
415    pub allow_file_creation: bool,
416    pub allow_file_deletion: bool,
417    pub working_directory: Option<String>,
418}
419
420/// Context management settings
421#[derive(Debug, Clone, Serialize, Deserialize)]
422pub struct ContextConfig {
423    pub max_context_length: usize,
424    pub compression_threshold: usize,
425    pub summarization_interval: usize,
426    pub preservation_priority: Vec<String>,
427}
428
429/// Logging configuration
430#[derive(Debug, Clone, Serialize, Deserialize)]
431pub struct LoggingConfig {
432    pub level: String,
433    pub file_logging: bool,
434    pub log_directory: Option<String>,
435    pub max_log_files: usize,
436    pub max_log_size_mb: usize,
437}
438
439/// Analysis depth for workspace analysis
440#[derive(Debug, Clone, Serialize, Deserialize)]
441pub enum AnalysisDepth {
442    Basic,
443    Standard,
444    Deep,
445}
446
447/// Output format for commands
448#[derive(Debug, Clone, Serialize, Deserialize)]
449pub enum OutputFormat {
450    Text,
451    Json,
452    Html,
453}
454
455/// Compression level for context compression
456#[derive(Debug, Clone, Serialize, Deserialize)]
457pub enum CompressionLevel {
458    Light,
459    Medium,
460    Aggressive,
461}