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