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