Skip to main content

mockforge_intelligence/ai_studio/
config.rs

1//! Configuration for AI Studio
2//!
3//! This module defines configuration structures for the AI Studio, including
4//! deterministic mode settings, budget controls, and feature toggles.
5
6use serde::{Deserialize, Serialize};
7
8/// AI mode for workspace
9///
10/// Controls how AI-generated artifacts are used at runtime.
11#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
12#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
13#[serde(rename_all = "snake_case")]
14#[derive(Default)]
15pub enum AiMode {
16    /// Generate once and freeze - AI is only used to produce config/templates.
17    /// Runtime mocks use frozen artifacts (no LLM calls).
18    GenerateOnceFreeze,
19
20    /// Live mode - AI is used dynamically at runtime for each request.
21    #[default]
22    Live,
23}
24
25/// AI Studio configuration
26#[derive(Debug, Clone, Serialize, Deserialize, Default)]
27#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
28pub struct AiStudioConfig {
29    /// Deterministic mode configuration
30    #[serde(default)]
31    pub deterministic_mode: DeterministicModeConfig,
32
33    /// Budget and cost controls
34    #[serde(default)]
35    pub budgets: BudgetConfig,
36
37    /// Feature toggles
38    #[serde(default)]
39    pub features: FeatureConfig,
40}
41
42/// Persisted authoring policy for a local/self-hosted workspace.
43/// Provider/model and credentials remain server configuration; workspace policy
44/// can select a sampling seed and snapshot behavior without storing API keys.
45#[derive(Debug, Clone, Serialize, Deserialize, Default)]
46#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
47#[serde(deny_unknown_fields)]
48pub struct AiGenerationConfig {
49    /// Override the server sampling seed for this workspace.
50    #[serde(default)]
51    pub seed: Option<i64>,
52    /// Format, destination, provenance and auto-freeze settings.
53    #[serde(default)]
54    pub deterministic_mode: DeterministicModeConfig,
55}
56
57/// Freeze mode for deterministic artifacts
58#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
59#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
60#[serde(rename_all = "snake_case")]
61#[derive(Default)]
62pub enum FreezeMode {
63    /// Auto-freeze: Automatically freeze artifacts after generation
64    Auto,
65    /// Manual freeze: Require explicit user action to freeze artifacts
66    #[default]
67    Manual,
68}
69
70/// Deterministic mode configuration
71///
72/// When enabled, AI-generated artifacts are frozen to deterministic YAML/JSON files
73/// for version control and reproducible testing. Supports both auto and manual freeze modes.
74#[derive(Debug, Clone, Serialize, Deserialize)]
75#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
76pub struct DeterministicModeConfig {
77    /// Whether deterministic mode is enabled
78    #[serde(default = "default_false")]
79    pub enabled: bool,
80
81    /// Freeze mode: auto (freeze immediately) or manual (require explicit action)
82    #[serde(default)]
83    pub mode: FreezeMode,
84
85    /// Automatically freeze AI-generated artifacts after creation
86    /// (deprecated: use mode instead, but kept for backward compatibility)
87    #[serde(default = "default_true")]
88    pub auto_freeze: bool,
89
90    /// Format for frozen artifacts (yaml or json)
91    #[serde(default = "default_freeze_format")]
92    pub freeze_format: String,
93
94    /// Directory to store frozen artifacts
95    #[serde(default = "default_freeze_directory")]
96    pub freeze_directory: String,
97
98    /// Whether to track metadata (LLM version, prompt hash, output hash)
99    #[serde(default = "default_true")]
100    pub track_metadata: bool,
101}
102
103impl DeterministicModeConfig {
104    /// Check if auto-freeze is enabled (either via mode or legacy auto_freeze flag)
105    pub fn is_auto_freeze_enabled(&self) -> bool {
106        self.mode == FreezeMode::Auto || self.auto_freeze
107    }
108}
109
110impl Default for DeterministicModeConfig {
111    fn default() -> Self {
112        Self {
113            enabled: false,
114            mode: FreezeMode::Manual,
115            auto_freeze: true, // Legacy support
116            freeze_format: "yaml".to_string(),
117            freeze_directory: ".mockforge/frozen".to_string(),
118            track_metadata: true,
119        }
120    }
121}
122
123/// Budget configuration for AI usage
124#[derive(Debug, Clone, Serialize, Deserialize)]
125#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
126pub struct BudgetConfig {
127    /// Maximum tokens per workspace
128    #[serde(default = "default_max_tokens")]
129    pub max_tokens_per_workspace: u64,
130
131    /// Maximum AI calls per day
132    #[serde(default = "default_max_calls")]
133    pub max_ai_calls_per_day: u64,
134
135    /// Rate limit per minute
136    #[serde(default = "default_rate_limit")]
137    pub rate_limit_per_minute: u64,
138
139    /// Budget alerts threshold (percentage)
140    #[serde(default = "default_alert_threshold")]
141    pub alert_threshold: f64,
142}
143
144impl Default for BudgetConfig {
145    fn default() -> Self {
146        Self {
147            max_tokens_per_workspace: 100_000,
148            max_ai_calls_per_day: 1_000,
149            rate_limit_per_minute: 10,
150            alert_threshold: 0.8, // Alert at 80% usage
151        }
152    }
153}
154
155/// Feature configuration for enabling/disabling specific AI features
156#[derive(Debug, Clone, Serialize, Deserialize)]
157#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
158pub struct FeatureConfig {
159    /// Enable mock generation from natural language
160    #[serde(default = "default_true")]
161    pub mock_generation: bool,
162
163    /// Enable AI contract diff analysis
164    #[serde(default = "default_true")]
165    pub contract_diff: bool,
166
167    /// Enable persona generation
168    #[serde(default = "default_true")]
169    pub persona_generation: bool,
170
171    /// Enable free-form generation (general chat)
172    #[serde(default = "default_true")]
173    pub free_form_generation: bool,
174
175    /// Enable AI-guided debugging
176    #[serde(default = "default_true")]
177    pub debug_analysis: bool,
178}
179
180impl Default for FeatureConfig {
181    fn default() -> Self {
182        Self {
183            mock_generation: true,
184            contract_diff: true,
185            persona_generation: true,
186            free_form_generation: true,
187            debug_analysis: true,
188        }
189    }
190}
191
192// Helper functions for default values
193
194fn default_false() -> bool {
195    false
196}
197
198fn default_true() -> bool {
199    true
200}
201
202fn default_freeze_format() -> String {
203    "yaml".to_string()
204}
205
206fn default_freeze_directory() -> String {
207    ".mockforge/frozen".to_string()
208}
209
210fn default_max_tokens() -> u64 {
211    100_000
212}
213
214fn default_max_calls() -> u64 {
215    1_000
216}
217
218fn default_rate_limit() -> u64 {
219    10
220}
221
222fn default_alert_threshold() -> f64 {
223    0.8
224}