Mirrowel commited on
Commit
64859d9
·
1 Parent(s): 2384d86

feat(settings): ✨ add provider-specific settings management UI

Browse files

Introduces a comprehensive provider-specific settings management system for Antigravity and Gemini CLI providers with detection, display, and interactive configuration capabilities.

- Add `PROVIDER_SETTINGS_MAP` with detailed definitions for Antigravity (12 settings) and Gemini CLI (8 settings) including signature caching, tool fixes, and provider-specific parameters
- Implement `ProviderSettingsManager` class for managing provider settings with type-aware value parsing and modification tracking
- Add `detect_provider_settings()` method to `SettingsDetector` to identify modified provider settings from environment variables
- Integrate provider settings detection into launcher TUI summary display and detailed advanced settings view
- Add new menu option (4) in settings tool for provider-specific configuration management
- Implement interactive TUI for browsing, editing, and resetting individual or all provider settings with visual indication of modified values
- Display provider settings status in launcher with count of modified settings per provider
- Support bool, int, and string setting types with appropriate input handling and validation

src/proxy_app/launcher_tui.py CHANGED
@@ -100,7 +100,8 @@ class SettingsDetector:
100
  "custom_bases": SettingsDetector.detect_custom_api_bases(),
101
  "model_definitions": SettingsDetector.detect_model_definitions(),
102
  "concurrency_limits": SettingsDetector.detect_concurrency_limits(),
103
- "model_filters": SettingsDetector.detect_model_filters()
 
104
  }
105
 
106
  @staticmethod
@@ -198,6 +199,45 @@ class SettingsDetector:
198
  else:
199
  filters[provider]["has_whitelist"] = True
200
  return filters
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
 
202
 
203
  class LauncherTUI:
@@ -300,7 +340,8 @@ class LauncherTUI:
300
  self.console.print("━" * 70)
301
  provider_count = len(credentials)
302
  custom_count = len(custom_bases)
303
- has_advanced = bool(settings["model_definitions"] or settings["concurrency_limits"] or settings["model_filters"])
 
304
 
305
  self.console.print(f" Providers: {provider_count} configured")
306
  self.console.print(f" Custom Providers: {custom_count} configured")
@@ -422,6 +463,7 @@ class LauncherTUI:
422
  model_defs = settings["model_definitions"]
423
  concurrency = settings["concurrency_limits"]
424
  filters = settings["model_filters"]
 
425
 
426
  self.console.print(Panel.fit(
427
  "[bold cyan]📊 Provider & Advanced Settings[/bold cyan]",
@@ -472,7 +514,7 @@ class LauncherTUI:
472
  self.console.print("━" * 70)
473
  for provider, limit in concurrency.items():
474
  self.console.print(f" • {provider:15} {limit} requests/key")
475
- self.console.print(f" • Default: 1 request/key (all others)")
476
 
477
  # Model Filters (basic info only)
478
  if filters:
@@ -488,6 +530,22 @@ class LauncherTUI:
488
  status = " + ".join(status_parts) if status_parts else "None"
489
  self.console.print(f" • {provider:15} ✅ {status}")
490
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
491
  # Actions
492
  self.console.print()
493
  self.console.print("━" * 70)
 
100
  "custom_bases": SettingsDetector.detect_custom_api_bases(),
101
  "model_definitions": SettingsDetector.detect_model_definitions(),
102
  "concurrency_limits": SettingsDetector.detect_concurrency_limits(),
103
+ "model_filters": SettingsDetector.detect_model_filters(),
104
+ "provider_settings": SettingsDetector.detect_provider_settings()
105
  }
106
 
107
  @staticmethod
 
199
  else:
200
  filters[provider]["has_whitelist"] = True
201
  return filters
202
+
203
+ @staticmethod
204
+ def detect_provider_settings() -> dict:
205
+ """Detect provider-specific settings (Antigravity, Gemini CLI)"""
206
+ try:
207
+ from proxy_app.settings_tool import PROVIDER_SETTINGS_MAP
208
+ except ImportError:
209
+ # Fallback for direct execution or testing
210
+ from .settings_tool import PROVIDER_SETTINGS_MAP
211
+
212
+ provider_settings = {}
213
+ env_vars = SettingsDetector._load_local_env()
214
+
215
+ for provider, definitions in PROVIDER_SETTINGS_MAP.items():
216
+ modified_count = 0
217
+ for key, definition in definitions.items():
218
+ env_value = env_vars.get(key)
219
+ if env_value is not None:
220
+ # Check if value differs from default
221
+ default = definition.get("default")
222
+ setting_type = definition.get("type", "str")
223
+
224
+ try:
225
+ if setting_type == "bool":
226
+ current = env_value.lower() in ("true", "1", "yes")
227
+ elif setting_type == "int":
228
+ current = int(env_value)
229
+ else:
230
+ current = env_value
231
+
232
+ if current != default:
233
+ modified_count += 1
234
+ except (ValueError, AttributeError):
235
+ pass
236
+
237
+ if modified_count > 0:
238
+ provider_settings[provider] = modified_count
239
+
240
+ return provider_settings
241
 
242
 
243
  class LauncherTUI:
 
340
  self.console.print("━" * 70)
341
  provider_count = len(credentials)
342
  custom_count = len(custom_bases)
343
+ provider_settings = settings.get("provider_settings", {})
344
+ has_advanced = bool(settings["model_definitions"] or settings["concurrency_limits"] or settings["model_filters"] or provider_settings)
345
 
346
  self.console.print(f" Providers: {provider_count} configured")
347
  self.console.print(f" Custom Providers: {custom_count} configured")
 
463
  model_defs = settings["model_definitions"]
464
  concurrency = settings["concurrency_limits"]
465
  filters = settings["model_filters"]
466
+ provider_settings = settings.get("provider_settings", {})
467
 
468
  self.console.print(Panel.fit(
469
  "[bold cyan]📊 Provider & Advanced Settings[/bold cyan]",
 
514
  self.console.print("━" * 70)
515
  for provider, limit in concurrency.items():
516
  self.console.print(f" • {provider:15} {limit} requests/key")
517
+ self.console.print(" • Default: 1 request/key (all others)")
518
 
519
  # Model Filters (basic info only)
520
  if filters:
 
530
  status = " + ".join(status_parts) if status_parts else "None"
531
  self.console.print(f" • {provider:15} ✅ {status}")
532
 
533
+ # Provider-Specific Settings
534
+ self.console.print()
535
+ self.console.print("[bold]🔬 Provider-Specific Settings[/bold]")
536
+ self.console.print("━" * 70)
537
+ try:
538
+ from proxy_app.settings_tool import PROVIDER_SETTINGS_MAP
539
+ except ImportError:
540
+ from .settings_tool import PROVIDER_SETTINGS_MAP
541
+ for provider in PROVIDER_SETTINGS_MAP.keys():
542
+ display_name = provider.replace("_", " ").title()
543
+ modified = provider_settings.get(provider, 0)
544
+ if modified > 0:
545
+ self.console.print(f" • {display_name:20} [yellow]{modified} setting{'s' if modified > 1 else ''} modified[/yellow]")
546
+ else:
547
+ self.console.print(f" • {display_name:20} [dim]using defaults[/dim]")
548
+
549
  # Actions
550
  self.console.print()
551
  self.console.print("━" * 70)
src/proxy_app/settings_tool.py CHANGED
@@ -166,6 +166,184 @@ class ConcurrencyManager:
166
  self.settings.remove(key)
167
 
168
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
  class SettingsTool:
170
  """Main settings tool TUI"""
171
 
@@ -175,6 +353,7 @@ class SettingsTool:
175
  self.provider_mgr = CustomProviderManager(self.settings)
176
  self.model_mgr = ModelDefinitionManager(self.settings)
177
  self.concurrency_mgr = ConcurrencyManager(self.settings)
 
178
  self.running = True
179
 
180
  def get_available_providers(self) -> List[str]:
@@ -223,8 +402,9 @@ class SettingsTool:
223
  self.console.print(" 1. 🌐 Custom Provider API Bases")
224
  self.console.print(" 2. 📦 Provider Model Definitions")
225
  self.console.print(" 3. ⚡ Concurrency Limits")
226
- self.console.print(" 4. 💾 Save & Exit")
227
- self.console.print(" 5. 🚫 Exit Without Saving")
 
228
 
229
  self.console.print()
230
  self.console.print("━" * 70)
@@ -238,7 +418,7 @@ class SettingsTool:
238
  self.console.print("[dim]⚠️ Model filters not supported - edit .env for IGNORE_MODELS_* / WHITELIST_MODELS_*[/dim]")
239
  self.console.print()
240
 
241
- choice = Prompt.ask("Select option", choices=["1", "2", "3", "4", "5"], show_choices=False)
242
 
243
  if choice == "1":
244
  self.manage_custom_providers()
@@ -247,8 +427,10 @@ class SettingsTool:
247
  elif choice == "3":
248
  self.manage_concurrency_limits()
249
  elif choice == "4":
250
- self.save_and_exit()
251
  elif choice == "5":
 
 
252
  self.exit_without_saving()
253
 
254
  def manage_custom_providers(self):
@@ -631,6 +813,195 @@ class SettingsTool:
631
 
632
  input("Press Enter to return...")
633
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
634
  def manage_concurrency_limits(self):
635
  """Manage concurrency limits"""
636
  while True:
 
166
  self.settings.remove(key)
167
 
168
 
169
+ # =============================================================================
170
+ # PROVIDER-SPECIFIC SETTINGS DEFINITIONS
171
+ # =============================================================================
172
+
173
+ # Antigravity provider environment variables
174
+ ANTIGRAVITY_SETTINGS = {
175
+ "ANTIGRAVITY_SIGNATURE_CACHE_TTL": {
176
+ "type": "int",
177
+ "default": 3600,
178
+ "description": "Memory cache TTL for Gemini 3 thought signatures (seconds)",
179
+ },
180
+ "ANTIGRAVITY_SIGNATURE_DISK_TTL": {
181
+ "type": "int",
182
+ "default": 86400,
183
+ "description": "Disk cache TTL for Gemini 3 thought signatures (seconds)",
184
+ },
185
+ "ANTIGRAVITY_PRESERVE_THOUGHT_SIGNATURES": {
186
+ "type": "bool",
187
+ "default": True,
188
+ "description": "Preserve thought signatures in client responses",
189
+ },
190
+ "ANTIGRAVITY_ENABLE_SIGNATURE_CACHE": {
191
+ "type": "bool",
192
+ "default": True,
193
+ "description": "Enable signature caching for multi-turn conversations",
194
+ },
195
+ "ANTIGRAVITY_ENABLE_DYNAMIC_MODELS": {
196
+ "type": "bool",
197
+ "default": False,
198
+ "description": "Enable dynamic model discovery from API",
199
+ },
200
+ "ANTIGRAVITY_GEMINI3_TOOL_FIX": {
201
+ "type": "bool",
202
+ "default": True,
203
+ "description": "Enable Gemini 3 tool hallucination prevention",
204
+ },
205
+ "ANTIGRAVITY_CLAUDE_TOOL_FIX": {
206
+ "type": "bool",
207
+ "default": True,
208
+ "description": "Enable Claude tool hallucination prevention",
209
+ },
210
+ "ANTIGRAVITY_CLAUDE_THINKING_SANITIZATION": {
211
+ "type": "bool",
212
+ "default": True,
213
+ "description": "Sanitize thinking blocks for Claude multi-turn conversations",
214
+ },
215
+ "ANTIGRAVITY_GEMINI3_TOOL_PREFIX": {
216
+ "type": "str",
217
+ "default": "gemini3_",
218
+ "description": "Prefix added to tool names for Gemini 3 disambiguation",
219
+ },
220
+ "ANTIGRAVITY_GEMINI3_DESCRIPTION_PROMPT": {
221
+ "type": "str",
222
+ "default": "\n\nSTRICT PARAMETERS: {params}.",
223
+ "description": "Template for strict parameter hints in tool descriptions",
224
+ },
225
+ "ANTIGRAVITY_CLAUDE_DESCRIPTION_PROMPT": {
226
+ "type": "str",
227
+ "default": "\n\nSTRICT PARAMETERS: {params}.",
228
+ "description": "Template for Claude strict parameter hints in tool descriptions",
229
+ },
230
+ }
231
+
232
+ # Gemini CLI provider environment variables
233
+ GEMINI_CLI_SETTINGS = {
234
+ "GEMINI_CLI_SIGNATURE_CACHE_TTL": {
235
+ "type": "int",
236
+ "default": 3600,
237
+ "description": "Memory cache TTL for thought signatures (seconds)",
238
+ },
239
+ "GEMINI_CLI_SIGNATURE_DISK_TTL": {
240
+ "type": "int",
241
+ "default": 86400,
242
+ "description": "Disk cache TTL for thought signatures (seconds)",
243
+ },
244
+ "GEMINI_CLI_PRESERVE_THOUGHT_SIGNATURES": {
245
+ "type": "bool",
246
+ "default": True,
247
+ "description": "Preserve thought signatures in client responses",
248
+ },
249
+ "GEMINI_CLI_ENABLE_SIGNATURE_CACHE": {
250
+ "type": "bool",
251
+ "default": True,
252
+ "description": "Enable signature caching for multi-turn conversations",
253
+ },
254
+ "GEMINI_CLI_GEMINI3_TOOL_FIX": {
255
+ "type": "bool",
256
+ "default": True,
257
+ "description": "Enable Gemini 3 tool hallucination prevention",
258
+ },
259
+ "GEMINI_CLI_GEMINI3_TOOL_PREFIX": {
260
+ "type": "str",
261
+ "default": "gemini3_",
262
+ "description": "Prefix added to tool names for Gemini 3 disambiguation",
263
+ },
264
+ "GEMINI_CLI_GEMINI3_DESCRIPTION_PROMPT": {
265
+ "type": "str",
266
+ "default": "\n\nSTRICT PARAMETERS: {params}.",
267
+ "description": "Template for strict parameter hints in tool descriptions",
268
+ },
269
+ "GEMINI_CLI_PROJECT_ID": {
270
+ "type": "str",
271
+ "default": "",
272
+ "description": "GCP Project ID for paid tier users (required for paid tiers)",
273
+ },
274
+ }
275
+
276
+ # Map provider names to their settings definitions
277
+ PROVIDER_SETTINGS_MAP = {
278
+ "antigravity": ANTIGRAVITY_SETTINGS,
279
+ "gemini_cli": GEMINI_CLI_SETTINGS,
280
+ }
281
+
282
+
283
+ class ProviderSettingsManager:
284
+ """Manages provider-specific configuration settings"""
285
+
286
+ def __init__(self, settings: AdvancedSettings):
287
+ self.settings = settings
288
+
289
+ def get_available_providers(self) -> List[str]:
290
+ """Get list of providers with specific settings available"""
291
+ return list(PROVIDER_SETTINGS_MAP.keys())
292
+
293
+ def get_provider_settings_definitions(self, provider: str) -> Dict[str, Dict[str, Any]]:
294
+ """Get settings definitions for a provider"""
295
+ return PROVIDER_SETTINGS_MAP.get(provider, {})
296
+
297
+ def get_current_value(self, key: str, definition: Dict[str, Any]) -> Any:
298
+ """Get current value of a setting from environment"""
299
+ env_value = os.getenv(key)
300
+ if env_value is None:
301
+ return definition.get("default")
302
+
303
+ setting_type = definition.get("type", "str")
304
+ try:
305
+ if setting_type == "bool":
306
+ return env_value.lower() in ("true", "1", "yes")
307
+ elif setting_type == "int":
308
+ return int(env_value)
309
+ else:
310
+ return env_value
311
+ except (ValueError, AttributeError):
312
+ return definition.get("default")
313
+
314
+ def get_all_current_values(self, provider: str) -> Dict[str, Any]:
315
+ """Get all current values for a provider"""
316
+ definitions = self.get_provider_settings_definitions(provider)
317
+ values = {}
318
+ for key, definition in definitions.items():
319
+ values[key] = self.get_current_value(key, definition)
320
+ return values
321
+
322
+ def set_value(self, key: str, value: Any, definition: Dict[str, Any]):
323
+ """Set a setting value, converting to string for .env storage"""
324
+ setting_type = definition.get("type", "str")
325
+ if setting_type == "bool":
326
+ str_value = "true" if value else "false"
327
+ else:
328
+ str_value = str(value)
329
+ self.settings.set(key, str_value)
330
+
331
+ def reset_to_default(self, key: str):
332
+ """Remove a setting to reset it to default"""
333
+ self.settings.remove(key)
334
+
335
+ def get_modified_settings(self, provider: str) -> Dict[str, Any]:
336
+ """Get settings that differ from defaults"""
337
+ definitions = self.get_provider_settings_definitions(provider)
338
+ modified = {}
339
+ for key, definition in definitions.items():
340
+ current = self.get_current_value(key, definition)
341
+ default = definition.get("default")
342
+ if current != default:
343
+ modified[key] = current
344
+ return modified
345
+
346
+
347
  class SettingsTool:
348
  """Main settings tool TUI"""
349
 
 
353
  self.provider_mgr = CustomProviderManager(self.settings)
354
  self.model_mgr = ModelDefinitionManager(self.settings)
355
  self.concurrency_mgr = ConcurrencyManager(self.settings)
356
+ self.provider_settings_mgr = ProviderSettingsManager(self.settings)
357
  self.running = True
358
 
359
  def get_available_providers(self) -> List[str]:
 
402
  self.console.print(" 1. 🌐 Custom Provider API Bases")
403
  self.console.print(" 2. 📦 Provider Model Definitions")
404
  self.console.print(" 3. ⚡ Concurrency Limits")
405
+ self.console.print(" 4. 🔬 Provider-Specific Settings")
406
+ self.console.print(" 5. 💾 Save & Exit")
407
+ self.console.print(" 6. 🚫 Exit Without Saving")
408
 
409
  self.console.print()
410
  self.console.print("━" * 70)
 
418
  self.console.print("[dim]⚠️ Model filters not supported - edit .env for IGNORE_MODELS_* / WHITELIST_MODELS_*[/dim]")
419
  self.console.print()
420
 
421
+ choice = Prompt.ask("Select option", choices=["1", "2", "3", "4", "5", "6"], show_choices=False)
422
 
423
  if choice == "1":
424
  self.manage_custom_providers()
 
427
  elif choice == "3":
428
  self.manage_concurrency_limits()
429
  elif choice == "4":
430
+ self.manage_provider_settings()
431
  elif choice == "5":
432
+ self.save_and_exit()
433
+ elif choice == "6":
434
  self.exit_without_saving()
435
 
436
  def manage_custom_providers(self):
 
813
 
814
  input("Press Enter to return...")
815
 
816
+ def manage_provider_settings(self):
817
+ """Manage provider-specific settings (Antigravity, Gemini CLI)"""
818
+ while True:
819
+ self.console.clear()
820
+
821
+ available_providers = self.provider_settings_mgr.get_available_providers()
822
+
823
+ self.console.print(Panel.fit(
824
+ "[bold cyan]🔬 Provider-Specific Settings[/bold cyan]",
825
+ border_style="cyan"
826
+ ))
827
+
828
+ self.console.print()
829
+ self.console.print("[bold]📋 Available Providers with Custom Settings[/bold]")
830
+ self.console.print("━" * 70)
831
+
832
+ for provider in available_providers:
833
+ modified = self.provider_settings_mgr.get_modified_settings(provider)
834
+ status = f"[yellow]{len(modified)} modified[/yellow]" if modified else "[dim]defaults[/dim]"
835
+ display_name = provider.replace("_", " ").title()
836
+ self.console.print(f" • {display_name:20} {status}")
837
+
838
+ self.console.print()
839
+ self.console.print("━" * 70)
840
+ self.console.print()
841
+ self.console.print("[bold]⚙️ Select Provider to Configure[/bold]")
842
+ self.console.print()
843
+
844
+ for idx, provider in enumerate(available_providers, 1):
845
+ display_name = provider.replace("_", " ").title()
846
+ self.console.print(f" {idx}. {display_name}")
847
+ self.console.print(f" {len(available_providers) + 1}. ↩️ Back to Settings Menu")
848
+
849
+ self.console.print()
850
+ self.console.print("━" * 70)
851
+ self.console.print()
852
+
853
+ choices = [str(i) for i in range(1, len(available_providers) + 2)]
854
+ choice = Prompt.ask("Select option", choices=choices, show_choices=False)
855
+ choice_idx = int(choice)
856
+
857
+ if choice_idx == len(available_providers) + 1:
858
+ break
859
+
860
+ provider = available_providers[choice_idx - 1]
861
+ self._manage_single_provider_settings(provider)
862
+
863
+ def _manage_single_provider_settings(self, provider: str):
864
+ """Manage settings for a single provider"""
865
+ while True:
866
+ self.console.clear()
867
+
868
+ display_name = provider.replace("_", " ").title()
869
+ definitions = self.provider_settings_mgr.get_provider_settings_definitions(provider)
870
+ current_values = self.provider_settings_mgr.get_all_current_values(provider)
871
+
872
+ self.console.print(Panel.fit(
873
+ f"[bold cyan]🔬 {display_name} Settings[/bold cyan]",
874
+ border_style="cyan"
875
+ ))
876
+
877
+ self.console.print()
878
+ self.console.print("[bold]📋 Current Settings[/bold]")
879
+ self.console.print("━" * 70)
880
+
881
+ # Display all settings with current values
882
+ settings_list = list(definitions.keys())
883
+ for idx, key in enumerate(settings_list, 1):
884
+ definition = definitions[key]
885
+ current = current_values.get(key)
886
+ default = definition.get("default")
887
+ setting_type = definition.get("type", "str")
888
+ description = definition.get("description", "")
889
+
890
+ # Format value display
891
+ if setting_type == "bool":
892
+ value_display = "[green]✓ Enabled[/green]" if current else "[red]✗ Disabled[/red]"
893
+ elif setting_type == "int":
894
+ value_display = f"[cyan]{current}[/cyan]"
895
+ else:
896
+ value_display = f"[cyan]{current or '(not set)'}[/cyan]" if current else "[dim](not set)[/dim]"
897
+
898
+ # Check if modified from default
899
+ modified = current != default
900
+ mod_marker = "[yellow]*[/yellow]" if modified else " "
901
+
902
+ # Short key name for display (strip provider prefix)
903
+ short_key = key.replace(f"{provider.upper()}_", "")
904
+
905
+ self.console.print(f" {mod_marker}{idx:2}. {short_key:35} {value_display}")
906
+ self.console.print(f" [dim]{description}[/dim]")
907
+
908
+ self.console.print()
909
+ self.console.print("━" * 70)
910
+ self.console.print("[dim]* = modified from default[/dim]")
911
+ self.console.print()
912
+ self.console.print("[bold]⚙️ Actions[/bold]")
913
+ self.console.print()
914
+ self.console.print(" E. ✏️ Edit a Setting")
915
+ self.console.print(" R. 🔄 Reset Setting to Default")
916
+ self.console.print(" A. 🔄 Reset All to Defaults")
917
+ self.console.print(" B. ↩️ Back to Provider Selection")
918
+
919
+ self.console.print()
920
+ self.console.print("━" * 70)
921
+ self.console.print()
922
+
923
+ choice = Prompt.ask("Select action", choices=["e", "r", "a", "b", "E", "R", "A", "B"], show_choices=False).lower()
924
+
925
+ if choice == "b":
926
+ break
927
+ elif choice == "e":
928
+ self._edit_provider_setting(provider, settings_list, definitions)
929
+ elif choice == "r":
930
+ self._reset_provider_setting(provider, settings_list, definitions)
931
+ elif choice == "a":
932
+ self._reset_all_provider_settings(provider, settings_list)
933
+
934
+ def _edit_provider_setting(self, provider: str, settings_list: List[str], definitions: Dict[str, Dict[str, Any]]):
935
+ """Edit a single provider setting"""
936
+ self.console.print("\n[bold]Select setting number to edit:[/bold]")
937
+
938
+ choices = [str(i) for i in range(1, len(settings_list) + 1)]
939
+ choice = IntPrompt.ask("Setting number", choices=choices)
940
+ key = settings_list[choice - 1]
941
+ definition = definitions[key]
942
+
943
+ current = self.provider_settings_mgr.get_current_value(key, definition)
944
+ default = definition.get("default")
945
+ setting_type = definition.get("type", "str")
946
+ short_key = key.replace(f"{provider.upper()}_", "")
947
+
948
+ self.console.print(f"\n[bold]Editing: {short_key}[/bold]")
949
+ self.console.print(f"Current value: [cyan]{current}[/cyan]")
950
+ self.console.print(f"Default value: [dim]{default}[/dim]")
951
+ self.console.print(f"Type: {setting_type}")
952
+
953
+ if setting_type == "bool":
954
+ new_value = Confirm.ask("\nEnable this setting?", default=current)
955
+ self.provider_settings_mgr.set_value(key, new_value, definition)
956
+ status = "enabled" if new_value else "disabled"
957
+ self.console.print(f"\n[green]✅ {short_key} {status}![/green]")
958
+ elif setting_type == "int":
959
+ new_value = IntPrompt.ask("\nNew value", default=current)
960
+ self.provider_settings_mgr.set_value(key, new_value, definition)
961
+ self.console.print(f"\n[green]✅ {short_key} set to {new_value}![/green]")
962
+ else:
963
+ new_value = Prompt.ask("\nNew value", default=str(current) if current else "").strip()
964
+ if new_value:
965
+ self.provider_settings_mgr.set_value(key, new_value, definition)
966
+ self.console.print(f"\n[green]✅ {short_key} updated![/green]")
967
+ else:
968
+ self.console.print("\n[yellow]No changes made[/yellow]")
969
+
970
+ input("\nPress Enter to continue...")
971
+
972
+ def _reset_provider_setting(self, provider: str, settings_list: List[str], definitions: Dict[str, Dict[str, Any]]):
973
+ """Reset a single provider setting to default"""
974
+ self.console.print("\n[bold]Select setting number to reset:[/bold]")
975
+
976
+ choices = [str(i) for i in range(1, len(settings_list) + 1)]
977
+ choice = IntPrompt.ask("Setting number", choices=choices)
978
+ key = settings_list[choice - 1]
979
+ definition = definitions[key]
980
+
981
+ default = definition.get("default")
982
+ short_key = key.replace(f"{provider.upper()}_", "")
983
+
984
+ if Confirm.ask(f"\nReset {short_key} to default ({default})?"):
985
+ self.provider_settings_mgr.reset_to_default(key)
986
+ self.console.print(f"\n[green]✅ {short_key} reset to default![/green]")
987
+ else:
988
+ self.console.print("\n[yellow]No changes made[/yellow]")
989
+
990
+ input("\nPress Enter to continue...")
991
+
992
+ def _reset_all_provider_settings(self, provider: str, settings_list: List[str]):
993
+ """Reset all provider settings to defaults"""
994
+ display_name = provider.replace("_", " ").title()
995
+
996
+ if Confirm.ask(f"\n[bold red]Reset ALL {display_name} settings to defaults?[/bold red]"):
997
+ for key in settings_list:
998
+ self.provider_settings_mgr.reset_to_default(key)
999
+ self.console.print(f"\n[green]✅ All {display_name} settings reset to defaults![/green]")
1000
+ else:
1001
+ self.console.print("\n[yellow]No changes made[/yellow]")
1002
+
1003
+ input("\nPress Enter to continue...")
1004
+
1005
  def manage_concurrency_limits(self):
1006
  """Manage concurrency limits"""
1007
  while True: