1use std::path::PathBuf;
2
3const REASONING_CONFIG_KEYS: &[&str] = &[
4 "model_reasoning_effort",
5 "model_reasoning_summary",
6 "model_verbosity",
7 "model_reasoning_summary_format",
8 "model_supports_reasoning_summaries",
9];
10
11#[derive(Clone, Copy, Debug, Eq, PartialEq)]
13pub enum ColorMode {
14 Auto,
16 Always,
18 Never,
20}
21
22impl ColorMode {
23 pub(crate) const fn as_str(self) -> &'static str {
24 match self {
25 ColorMode::Auto => "auto",
26 ColorMode::Always => "always",
27 ColorMode::Never => "never",
28 }
29 }
30}
31
32#[derive(Clone, Copy, Debug, Eq, PartialEq)]
34pub enum ApprovalPolicy {
35 Untrusted,
36 OnFailure,
37 OnRequest,
38 Never,
39}
40
41impl ApprovalPolicy {
42 pub(super) const fn as_str(self) -> &'static str {
43 match self {
44 ApprovalPolicy::Untrusted => "untrusted",
45 ApprovalPolicy::OnFailure => "on-failure",
46 ApprovalPolicy::OnRequest => "on-request",
47 ApprovalPolicy::Never => "never",
48 }
49 }
50}
51
52#[derive(Clone, Copy, Debug, Eq, PartialEq)]
54pub enum SandboxMode {
55 ReadOnly,
56 WorkspaceWrite,
57 DangerFullAccess,
58}
59
60impl SandboxMode {
61 pub(super) const fn as_str(self) -> &'static str {
62 match self {
63 SandboxMode::ReadOnly => "read-only",
64 SandboxMode::WorkspaceWrite => "workspace-write",
65 SandboxMode::DangerFullAccess => "danger-full-access",
66 }
67 }
68}
69
70#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
72pub enum SafetyOverride {
73 #[default]
74 Inherit,
75 FullAuto,
76 DangerouslyBypass,
77}
78
79#[derive(Clone, Copy, Debug, Eq, PartialEq)]
81pub enum LocalProvider {
82 LmStudio,
83 Ollama,
84 Custom,
85}
86
87impl LocalProvider {
88 pub(super) const fn as_str(self) -> &'static str {
89 match self {
90 LocalProvider::LmStudio => "lmstudio",
91 LocalProvider::Ollama => "ollama",
92 LocalProvider::Custom => "custom",
93 }
94 }
95}
96
97#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
99pub enum FlagState {
100 #[default]
101 Inherit,
102 Enable,
103 Disable,
104}
105
106#[derive(Clone, Debug, Default, Eq, PartialEq)]
108pub struct FeatureToggles {
109 pub enable: Vec<String>,
110 pub disable: Vec<String>,
111}
112
113#[derive(Clone, Copy, Debug, Eq, PartialEq)]
115pub enum ReasoningEffort {
116 Minimal,
117 Low,
118 Medium,
119 High,
120}
121
122impl ReasoningEffort {
123 pub(super) const fn as_str(self) -> &'static str {
124 match self {
125 ReasoningEffort::Minimal => "minimal",
126 ReasoningEffort::Low => "low",
127 ReasoningEffort::Medium => "medium",
128 ReasoningEffort::High => "high",
129 }
130 }
131}
132
133#[derive(Clone, Copy, Debug, Eq, PartialEq)]
135pub enum ReasoningSummary {
136 Auto,
137 Concise,
138 Detailed,
139 None,
140}
141
142impl ReasoningSummary {
143 pub(super) const fn as_str(self) -> &'static str {
144 match self {
145 ReasoningSummary::Auto => "auto",
146 ReasoningSummary::Concise => "concise",
147 ReasoningSummary::Detailed => "detailed",
148 ReasoningSummary::None => "none",
149 }
150 }
151}
152
153#[derive(Clone, Copy, Debug, Eq, PartialEq)]
155pub enum ModelVerbosity {
156 Low,
157 Medium,
158 High,
159}
160
161impl ModelVerbosity {
162 pub(super) const fn as_str(self) -> &'static str {
163 match self {
164 ModelVerbosity::Low => "low",
165 ModelVerbosity::Medium => "medium",
166 ModelVerbosity::High => "high",
167 }
168 }
169}
170
171#[derive(Clone, Copy, Debug, Eq, PartialEq)]
173pub enum ReasoningSummaryFormat {
174 None,
175 Experimental,
176}
177
178impl ReasoningSummaryFormat {
179 pub(super) const fn as_str(self) -> &'static str {
180 match self {
181 ReasoningSummaryFormat::None => "none",
182 ReasoningSummaryFormat::Experimental => "experimental",
183 }
184 }
185}
186
187#[derive(Clone, Debug, Eq, PartialEq)]
189pub struct ConfigOverride {
190 pub key: String,
191 pub value: String,
192}
193
194impl ConfigOverride {
195 pub fn new(key: impl Into<String>, value: impl Into<String>) -> Self {
196 Self {
197 key: key.into(),
198 value: value.into(),
199 }
200 }
201
202 pub fn from_raw(raw: impl Into<String>) -> Self {
203 let raw = raw.into();
204 let (key, value) = raw
205 .split_once('=')
206 .map(|(key, value)| (key.to_string(), value.to_string()))
207 .unwrap_or_else(|| (raw.clone(), String::new()));
208 ConfigOverride { key, value }
209 }
210
211 pub(super) fn is_reasoning_key(&self) -> bool {
212 REASONING_CONFIG_KEYS.contains(&self.key.as_str())
213 }
214}
215
216#[derive(Clone, Debug, Default, Eq, PartialEq)]
218pub struct ReasoningOverrides {
219 pub effort: Option<ReasoningEffort>,
220 pub summary: Option<ReasoningSummary>,
221 pub verbosity: Option<ModelVerbosity>,
222 pub summary_format: Option<ReasoningSummaryFormat>,
223 pub supports_summaries: Option<bool>,
224}
225
226impl ReasoningOverrides {
227 pub(crate) fn has_overrides(&self) -> bool {
228 self.effort.is_some()
229 || self.summary.is_some()
230 || self.verbosity.is_some()
231 || self.summary_format.is_some()
232 || self.supports_summaries.is_some()
233 }
234
235 pub(super) fn append_overrides(&self, configs: &mut Vec<ConfigOverride>) {
236 if let Some(value) = self.effort {
237 configs.push(ConfigOverride::new(
238 "model_reasoning_effort",
239 value.as_str(),
240 ));
241 }
242 if let Some(value) = self.summary {
243 configs.push(ConfigOverride::new(
244 "model_reasoning_summary",
245 value.as_str(),
246 ));
247 }
248 if let Some(value) = self.verbosity {
249 configs.push(ConfigOverride::new("model_verbosity", value.as_str()));
250 }
251 if let Some(value) = self.summary_format {
252 configs.push(ConfigOverride::new(
253 "model_reasoning_summary_format",
254 value.as_str(),
255 ));
256 }
257 if let Some(value) = self.supports_summaries {
258 configs.push(ConfigOverride::new(
259 "model_supports_reasoning_summaries",
260 value.to_string(),
261 ));
262 }
263 }
264}
265
266#[derive(Clone, Debug, Eq, PartialEq)]
268pub struct CliOverrides {
269 pub config_overrides: Vec<ConfigOverride>,
270 pub feature_toggles: FeatureToggles,
271 pub reasoning: ReasoningOverrides,
272 pub approval_policy: Option<ApprovalPolicy>,
273 pub sandbox_mode: Option<SandboxMode>,
274 pub safety_override: SafetyOverride,
275 pub profile: Option<String>,
276 pub cd: Option<PathBuf>,
277 pub local_provider: Option<LocalProvider>,
278 pub oss: FlagState,
279 pub search: FlagState,
280 pub auto_reasoning_defaults: bool,
281}
282
283impl Default for CliOverrides {
284 fn default() -> Self {
285 Self {
286 config_overrides: Vec::new(),
287 feature_toggles: FeatureToggles::default(),
288 reasoning: ReasoningOverrides::default(),
289 approval_policy: None,
290 sandbox_mode: None,
291 safety_override: SafetyOverride::Inherit,
292 profile: None,
293 cd: None,
294 local_provider: None,
295 oss: FlagState::Inherit,
296 search: FlagState::Inherit,
297 auto_reasoning_defaults: true,
298 }
299 }
300}
301
302#[derive(Clone, Debug, Default, Eq, PartialEq)]
304pub struct CliOverridesPatch {
305 pub config_overrides: Vec<ConfigOverride>,
306 pub feature_toggles: FeatureToggles,
307 pub reasoning: ReasoningOverrides,
308 pub approval_policy: Option<ApprovalPolicy>,
309 pub sandbox_mode: Option<SandboxMode>,
310 pub safety_override: Option<SafetyOverride>,
311 pub profile: Option<String>,
312 pub cd: Option<PathBuf>,
313 pub local_provider: Option<LocalProvider>,
314 pub oss: FlagState,
315 pub search: FlagState,
316 pub auto_reasoning_defaults: Option<bool>,
317}