1use crate::constants::reasoning;
4use crate::core::PromptCachingConfig;
5use hashbrown::HashMap;
6use serde::{Deserialize, Deserializer, Serialize};
7use serde_json::Value;
8use std::collections::BTreeMap;
9use std::fmt;
10use std::path::PathBuf;
11
12#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
18#[serde(rename_all = "lowercase")]
19#[derive(Default)]
20pub enum ReasoningEffortLevel {
21 None,
23 Minimal,
25 Low,
27 #[default]
29 Medium,
30 High,
32 XHigh,
34 Max,
36}
37
38impl ReasoningEffortLevel {
39 pub fn as_str(self) -> &'static str {
41 match self {
42 Self::None => reasoning::NONE,
43 Self::Minimal => reasoning::MINIMAL,
44 Self::Low => reasoning::LOW,
45 Self::Medium => reasoning::MEDIUM,
46 Self::High => reasoning::HIGH,
47 Self::XHigh => reasoning::XHIGH,
48 Self::Max => reasoning::MAX,
49 }
50 }
51
52 pub fn parse(value: &str) -> Option<Self> {
54 let normalized = value.trim();
55 if normalized.eq_ignore_ascii_case(reasoning::NONE) {
56 Some(Self::None)
57 } else if normalized.eq_ignore_ascii_case(reasoning::MINIMAL) {
58 Some(Self::Minimal)
59 } else if normalized.eq_ignore_ascii_case(reasoning::LOW) {
60 Some(Self::Low)
61 } else if normalized.eq_ignore_ascii_case(reasoning::MEDIUM) {
62 Some(Self::Medium)
63 } else if normalized.eq_ignore_ascii_case(reasoning::HIGH) {
64 Some(Self::High)
65 } else if normalized.eq_ignore_ascii_case(reasoning::XHIGH) {
66 Some(Self::XHigh)
67 } else if normalized.eq_ignore_ascii_case(reasoning::MAX) {
68 Some(Self::Max)
69 } else {
70 None
71 }
72 }
73
74 pub fn allowed_values() -> &'static [&'static str] {
76 reasoning::ALLOWED_LEVELS
77 }
78}
79
80#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
83#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
84#[serde(rename_all = "lowercase")]
85#[derive(Default)]
86pub enum SystemPromptMode {
87 Minimal,
90 Lightweight,
93 #[default]
96 Default,
97 Specialized,
100}
101
102impl SystemPromptMode {
103 pub fn as_str(self) -> &'static str {
105 match self {
106 Self::Minimal => "minimal",
107 Self::Lightweight => "lightweight",
108 Self::Default => "default",
109 Self::Specialized => "specialized",
110 }
111 }
112
113 pub fn parse(value: &str) -> Option<Self> {
115 let normalized = value.trim();
116 if normalized.eq_ignore_ascii_case("minimal") {
117 Some(Self::Minimal)
118 } else if normalized.eq_ignore_ascii_case("lightweight") {
119 Some(Self::Lightweight)
120 } else if normalized.eq_ignore_ascii_case("default") {
121 Some(Self::Default)
122 } else if normalized.eq_ignore_ascii_case("specialized") {
123 Some(Self::Specialized)
124 } else {
125 None
126 }
127 }
128
129 pub fn allowed_values() -> &'static [&'static str] {
131 &["minimal", "lightweight", "default", "specialized"]
132 }
133}
134
135impl fmt::Display for SystemPromptMode {
136 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137 f.write_str(self.as_str())
138 }
139}
140
141impl<'de> Deserialize<'de> for SystemPromptMode {
142 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
143 where
144 D: Deserializer<'de>,
145 {
146 let raw = String::deserialize(deserializer)?;
147 if let Some(parsed) = Self::parse(&raw) {
148 Ok(parsed)
149 } else {
150 Ok(Self::default())
151 }
152 }
153}
154
155#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
158#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
159#[serde(rename_all = "lowercase")]
160#[derive(Default)]
161pub enum ToolDocumentationMode {
162 Minimal,
165 #[default]
168 Progressive,
169 Full,
172}
173
174impl ToolDocumentationMode {
175 pub fn as_str(self) -> &'static str {
177 match self {
178 Self::Minimal => "minimal",
179 Self::Progressive => "progressive",
180 Self::Full => "full",
181 }
182 }
183
184 pub fn parse(value: &str) -> Option<Self> {
186 let normalized = value.trim();
187 if normalized.eq_ignore_ascii_case("minimal") {
188 Some(Self::Minimal)
189 } else if normalized.eq_ignore_ascii_case("progressive") {
190 Some(Self::Progressive)
191 } else if normalized.eq_ignore_ascii_case("full") {
192 Some(Self::Full)
193 } else {
194 None
195 }
196 }
197
198 pub fn allowed_values() -> &'static [&'static str] {
200 &["minimal", "progressive", "full"]
201 }
202}
203
204impl fmt::Display for ToolDocumentationMode {
205 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206 f.write_str(self.as_str())
207 }
208}
209
210impl<'de> Deserialize<'de> for ToolDocumentationMode {
211 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
212 where
213 D: Deserializer<'de>,
214 {
215 let raw = String::deserialize(deserializer)?;
216 if let Some(parsed) = Self::parse(&raw) {
217 Ok(parsed)
218 } else {
219 Ok(Self::default())
220 }
221 }
222}
223
224#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
226#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
227#[serde(rename_all = "lowercase")]
228#[derive(Default)]
229pub enum VerbosityLevel {
230 Low,
231 #[default]
232 Medium,
233 High,
234}
235
236impl VerbosityLevel {
237 pub fn as_str(self) -> &'static str {
239 match self {
240 Self::Low => "low",
241 Self::Medium => "medium",
242 Self::High => "high",
243 }
244 }
245
246 pub fn parse(value: &str) -> Option<Self> {
248 let normalized = value.trim();
249 if normalized.eq_ignore_ascii_case("low") {
250 Some(Self::Low)
251 } else if normalized.eq_ignore_ascii_case("medium") {
252 Some(Self::Medium)
253 } else if normalized.eq_ignore_ascii_case("high") {
254 Some(Self::High)
255 } else {
256 None
257 }
258 }
259
260 pub fn allowed_values() -> &'static [&'static str] {
262 &["low", "medium", "high"]
263 }
264}
265
266impl fmt::Display for VerbosityLevel {
267 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
268 f.write_str(self.as_str())
269 }
270}
271
272impl<'de> Deserialize<'de> for VerbosityLevel {
273 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
274 where
275 D: Deserializer<'de>,
276 {
277 let raw = String::deserialize(deserializer)?;
278 if let Some(parsed) = Self::parse(&raw) {
279 Ok(parsed)
280 } else {
281 Ok(Self::default())
282 }
283 }
284}
285
286impl fmt::Display for ReasoningEffortLevel {
287 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288 f.write_str(self.as_str())
289 }
290}
291
292impl<'de> Deserialize<'de> for ReasoningEffortLevel {
293 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
294 where
295 D: Deserializer<'de>,
296 {
297 let raw = String::deserialize(deserializer)?;
298 if let Some(parsed) = Self::parse(&raw) {
299 Ok(parsed)
300 } else {
301 Ok(Self::default())
302 }
303 }
304}
305
306#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
308#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
309#[serde(rename_all = "lowercase")]
310#[derive(Default)]
311pub enum UiSurfacePreference {
312 #[default]
313 Auto,
314 Alternate,
315 Inline,
316}
317
318impl UiSurfacePreference {
319 pub fn as_str(self) -> &'static str {
321 match self {
322 Self::Auto => "auto",
323 Self::Alternate => "alternate",
324 Self::Inline => "inline",
325 }
326 }
327
328 pub fn parse(value: &str) -> Option<Self> {
330 let normalized = value.trim();
331 if normalized.eq_ignore_ascii_case("auto") {
332 Some(Self::Auto)
333 } else if normalized.eq_ignore_ascii_case("alternate")
334 || normalized.eq_ignore_ascii_case("alt")
335 {
336 Some(Self::Alternate)
337 } else if normalized.eq_ignore_ascii_case("inline") {
338 Some(Self::Inline)
339 } else {
340 None
341 }
342 }
343
344 pub fn allowed_values() -> &'static [&'static str] {
346 &["auto", "alternate", "inline"]
347 }
348}
349
350impl fmt::Display for UiSurfacePreference {
351 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
352 f.write_str(self.as_str())
353 }
354}
355
356impl<'de> Deserialize<'de> for UiSurfacePreference {
357 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
358 where
359 D: Deserializer<'de>,
360 {
361 let raw = String::deserialize(deserializer)?;
362 if let Some(parsed) = Self::parse(&raw) {
363 Ok(parsed)
364 } else {
365 Ok(Self::default())
366 }
367 }
368}
369
370#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
372pub enum ModelSelectionSource {
373 #[default]
375 WorkspaceConfig,
376 CliOverride,
378}
379
380#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
388#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
389#[serde(rename_all = "lowercase")]
390#[derive(Default)]
391pub enum EditingMode {
392 #[default]
395 Edit,
396 Plan,
400}
401
402impl EditingMode {
403 pub fn as_str(self) -> &'static str {
405 match self {
406 Self::Edit => "edit",
407 Self::Plan => "plan",
408 }
409 }
410
411 pub fn parse(value: &str) -> Option<Self> {
413 let normalized = value.trim();
414 if normalized.eq_ignore_ascii_case("edit") {
415 Some(Self::Edit)
416 } else if normalized.eq_ignore_ascii_case("plan") {
417 Some(Self::Plan)
418 } else {
419 None
420 }
421 }
422
423 pub fn allowed_values() -> &'static [&'static str] {
425 &["edit", "plan"]
426 }
427
428 pub fn can_modify_files(self) -> bool {
430 matches!(self, Self::Edit)
431 }
432
433 pub fn can_execute_commands(self) -> bool {
435 matches!(self, Self::Edit)
436 }
437
438 pub fn is_read_only(self) -> bool {
440 matches!(self, Self::Plan)
441 }
442}
443
444impl fmt::Display for EditingMode {
445 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
446 f.write_str(self.as_str())
447 }
448}
449
450impl<'de> Deserialize<'de> for EditingMode {
451 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
452 where
453 D: Deserializer<'de>,
454 {
455 let raw = String::deserialize(deserializer)?;
456 if let Some(parsed) = Self::parse(&raw) {
457 Ok(parsed)
458 } else {
459 Ok(Self::default())
460 }
461 }
462}
463
464#[derive(Debug, Clone)]
466pub struct AgentConfig {
467 pub model: String,
468 pub api_key: String,
469 pub provider: String,
470 pub openai_chatgpt_auth: Option<crate::auth::OpenAIChatGptAuthHandle>,
471 pub api_key_env: String,
472 pub workspace: PathBuf,
473 pub verbose: bool,
474 pub quiet: bool,
475 pub theme: String,
476 pub reasoning_effort: ReasoningEffortLevel,
477 pub ui_surface: UiSurfacePreference,
478 pub prompt_cache: PromptCachingConfig,
479 pub model_source: ModelSelectionSource,
480 pub custom_api_keys: BTreeMap<String, String>,
481 pub checkpointing_enabled: bool,
482 pub checkpointing_storage_dir: Option<PathBuf>,
483 pub checkpointing_max_snapshots: usize,
484 pub checkpointing_max_age_days: Option<u64>,
485 pub max_conversation_turns: usize,
486 pub model_behavior: Option<crate::core::ModelConfig>,
487}
488
489#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
491pub enum CapabilityLevel {
492 Basic,
494 FileReading,
496 FileListing,
498 Bash,
500 Editing,
502 CodeSearch,
504}
505
506#[derive(Debug, Clone, Serialize, Deserialize)]
508pub struct SessionInfo {
509 pub session_id: String,
510 pub start_time: u64,
511 pub total_turns: usize,
512 pub total_decisions: usize,
513 pub error_count: usize,
514}
515
516#[derive(Debug, Clone, Serialize, Deserialize)]
518pub struct ConversationTurn {
519 pub turn_number: usize,
520 pub timestamp: u64,
521 pub user_input: Option<String>,
522 pub agent_response: Option<String>,
523 pub tool_calls: Vec<ToolCallInfo>,
524 pub decision: Option<DecisionInfo>,
525}
526
527#[derive(Debug, Clone, Serialize, Deserialize)]
529pub struct ToolCallInfo {
530 pub name: String,
531 pub args: Value,
532 pub result: Option<Value>,
533 pub error: Option<String>,
534 pub execution_time_ms: Option<u64>,
535}
536
537#[derive(Debug, Clone, Serialize, Deserialize)]
539pub struct DecisionInfo {
540 pub turn_number: usize,
541 pub action_type: String,
542 pub description: String,
543 pub reasoning: String,
544 pub outcome: Option<String>,
545 pub confidence_score: Option<f64>,
546 pub timestamp: u64,
547}
548
549#[derive(Debug, Clone, Serialize, Deserialize)]
551pub struct ErrorInfo {
552 pub error_type: String,
553 pub message: String,
554 pub turn_number: usize,
555 pub recoverable: bool,
556 pub timestamp: u64,
557}
558
559#[derive(Debug, Clone, Serialize, Deserialize)]
561pub struct TaskInfo {
562 pub task_type: String,
563 pub description: String,
564 pub completed: bool,
565 pub success: bool,
566 pub duration_seconds: Option<u64>,
567 pub tools_used: Vec<String>,
568 pub dependencies: Vec<String>,
569}
570
571#[derive(Debug, Clone, Serialize, Deserialize)]
573pub struct ProjectSpec {
574 pub name: String,
575 pub features: Vec<String>,
576 pub template: Option<String>,
577 pub dependencies: HashMap<String, String>,
578}
579
580#[derive(Debug, Clone, Serialize, Deserialize)]
582pub struct WorkspaceAnalysis {
583 pub root_path: String,
584 pub project_type: Option<String>,
585 pub languages: Vec<String>,
586 pub frameworks: Vec<String>,
587 pub config_files: Vec<String>,
588 pub source_files: Vec<String>,
589 pub test_files: Vec<String>,
590 pub documentation_files: Vec<String>,
591 pub total_files: usize,
592 pub total_size_bytes: u64,
593}
594
595#[derive(Debug, Clone, Serialize, Deserialize)]
597pub struct CommandResult {
598 pub command: String,
599 pub success: bool,
600 pub stdout: String,
601 pub stderr: String,
602 pub exit_code: Option<i32>,
603 pub execution_time_ms: u64,
604}
605
606#[derive(Debug, Clone, Serialize, Deserialize)]
608pub struct FileOperationResult {
609 pub operation: String,
610 pub path: String,
611 pub success: bool,
612 pub details: HashMap<String, Value>,
613 pub error: Option<String>,
614}
615
616#[derive(Debug, Clone, Serialize, Deserialize)]
618pub struct PerformanceMetrics {
619 pub session_duration_seconds: u64,
620 pub total_api_calls: usize,
621 pub total_tokens_used: Option<usize>,
622 pub average_response_time_ms: f64,
623 pub tool_execution_count: usize,
624 pub error_count: usize,
625 pub recovery_success_rate: f64,
626}
627
628#[derive(Debug, Clone, Serialize, Deserialize)]
630pub struct QualityMetrics {
631 pub decision_confidence_avg: f64,
632 pub tool_success_rate: f64,
633 pub error_recovery_rate: f64,
634 pub context_preservation_rate: f64,
635 pub user_satisfaction_score: Option<f64>,
636}
637
638#[derive(Debug, Clone, Serialize, Deserialize)]
640pub struct ToolConfig {
641 pub enable_validation: bool,
642 pub max_execution_time_seconds: u64,
643 pub allow_file_creation: bool,
644 pub allow_file_deletion: bool,
645 pub working_directory: Option<String>,
646}
647
648#[derive(Debug, Clone, Serialize, Deserialize)]
650pub struct ContextConfig {
651 pub max_context_length: usize,
652 pub compression_threshold: usize,
653 pub summarization_interval: usize,
654 pub preservation_priority: Vec<String>,
655}
656
657#[derive(Debug, Clone, Serialize, Deserialize)]
659pub struct LoggingConfig {
660 pub level: String,
661 pub file_logging: bool,
662 pub log_directory: Option<String>,
663 pub max_log_files: usize,
664 pub max_log_size_mb: usize,
665}
666
667#[derive(Debug, Clone, Serialize, Deserialize)]
669pub enum AnalysisDepth {
670 Basic,
671 Standard,
672 Deep,
673}
674
675#[derive(Debug, Clone, Serialize, Deserialize)]
677pub enum OutputFormat {
678 Text,
679 Json,
680 Html,
681}
682
683#[derive(Debug, Clone, Serialize, Deserialize)]
685pub enum CompressionLevel {
686 Light,
687 Medium,
688 Aggressive,
689}
690
691#[cfg(test)]
692mod tests {
693 use super::*;
694
695 #[test]
696 fn test_reasoning_effort_parse_and_allowed_values_include_max() {
697 assert_eq!(
698 ReasoningEffortLevel::parse("max"),
699 Some(ReasoningEffortLevel::Max)
700 );
701 assert_eq!(ReasoningEffortLevel::Max.as_str(), "max");
702 assert!(ReasoningEffortLevel::allowed_values().contains(&"max"));
703 }
704
705 #[test]
706 fn test_editing_mode_parse() {
707 assert_eq!(EditingMode::parse("edit"), Some(EditingMode::Edit));
708 assert_eq!(EditingMode::parse("EDIT"), Some(EditingMode::Edit));
709 assert_eq!(EditingMode::parse("Edit"), Some(EditingMode::Edit));
710 assert_eq!(EditingMode::parse("plan"), Some(EditingMode::Plan));
711 assert_eq!(EditingMode::parse("PLAN"), Some(EditingMode::Plan));
712 assert_eq!(EditingMode::parse("Plan"), Some(EditingMode::Plan));
713 assert_eq!(EditingMode::parse("agent"), None);
714 assert_eq!(EditingMode::parse("invalid"), None);
715 assert_eq!(EditingMode::parse(""), None);
716 }
717
718 #[test]
719 fn test_editing_mode_as_str() {
720 assert_eq!(EditingMode::Edit.as_str(), "edit");
721 assert_eq!(EditingMode::Plan.as_str(), "plan");
722 }
723
724 #[test]
725 fn test_editing_mode_capabilities() {
726 assert!(EditingMode::Edit.can_modify_files());
728 assert!(EditingMode::Edit.can_execute_commands());
729 assert!(!EditingMode::Edit.is_read_only());
730
731 assert!(!EditingMode::Plan.can_modify_files());
733 assert!(!EditingMode::Plan.can_execute_commands());
734 assert!(EditingMode::Plan.is_read_only());
735 }
736
737 #[test]
738 fn test_editing_mode_default() {
739 assert_eq!(EditingMode::default(), EditingMode::Edit);
740 }
741
742 #[test]
743 fn test_editing_mode_display() {
744 assert_eq!(format!("{}", EditingMode::Edit), "edit");
745 assert_eq!(format!("{}", EditingMode::Plan), "plan");
746 }
747
748 #[test]
749 fn test_editing_mode_allowed_values() {
750 let values = EditingMode::allowed_values();
751 assert_eq!(values, &["edit", "plan"]);
752 }
753}