omnimancer-cli 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- omnimancer/__init__.py +42 -0
- omnimancer/__main__.py +9 -0
- omnimancer/cli/__init__.py +8 -0
- omnimancer/cli/agent_manager.py +826 -0
- omnimancer/cli/agent_wizard.py +944 -0
- omnimancer/cli/approval_formatter.py +734 -0
- omnimancer/cli/approval_integration.py +734 -0
- omnimancer/cli/approval_prompt.py +1286 -0
- omnimancer/cli/batch_approval_display.py +617 -0
- omnimancer/cli/batch_approval_filters.py +729 -0
- omnimancer/cli/batch_operation_monitor.py +775 -0
- omnimancer/cli/commands/performance_cmd.py +624 -0
- omnimancer/cli/commands.py +757 -0
- omnimancer/cli/completion_handler.py +281 -0
- omnimancer/cli/config_setup_wizard.py +783 -0
- omnimancer/cli/dynamic_commands.py +371 -0
- omnimancer/cli/handlers/__init__.py +18 -0
- omnimancer/cli/handlers/agent_handler.py +865 -0
- omnimancer/cli/handlers/agent_persona_handler.py +1048 -0
- omnimancer/cli/handlers/custom_agent_handler.py +352 -0
- omnimancer/cli/handlers/permissions_handler.py +1609 -0
- omnimancer/cli/interface.py +6695 -0
- omnimancer/cli/performance_dashboard.py +612 -0
- omnimancer/core/__init__.py +34 -0
- omnimancer/core/agent/__init__.py +256 -0
- omnimancer/core/agent/agent_config.py +715 -0
- omnimancer/core/agent/agent_switcher.py +668 -0
- omnimancer/core/agent/approval_context.py +460 -0
- omnimancer/core/agent/approval_dialog.py +902 -0
- omnimancer/core/agent/approval_interface.py +628 -0
- omnimancer/core/agent/approval_manager.py +760 -0
- omnimancer/core/agent/cli_integration.py +470 -0
- omnimancer/core/agent/config.py +845 -0
- omnimancer/core/agent/custom_agent_integration.py +654 -0
- omnimancer/core/agent/diff_renderer.py +706 -0
- omnimancer/core/agent/file_content_display.py +503 -0
- omnimancer/core/agent/file_modification_workflow.py +658 -0
- omnimancer/core/agent/file_system_manager.py +1790 -0
- omnimancer/core/agent/input_handler.py +614 -0
- omnimancer/core/agent/metrics_collector.py +771 -0
- omnimancer/core/agent/optimization_engine.py +729 -0
- omnimancer/core/agent/performance_monitor.py +717 -0
- omnimancer/core/agent/persona.py +870 -0
- omnimancer/core/agent/persona_validator.py +672 -0
- omnimancer/core/agent/program_executor.py +748 -0
- omnimancer/core/agent/proposed_changes_integration.py +663 -0
- omnimancer/core/agent/read_before_write_errors.py +492 -0
- omnimancer/core/agent/read_before_write_ui.py +647 -0
- omnimancer/core/agent/rich_renderer.py +763 -0
- omnimancer/core/agent/status_config.py +238 -0
- omnimancer/core/agent/status_core.py +446 -0
- omnimancer/core/agent/status_integrated_engine.py +540 -0
- omnimancer/core/agent/status_manager.py +878 -0
- omnimancer/core/agent/status_ui_components.py +535 -0
- omnimancer/core/agent/template_applicator.py +548 -0
- omnimancer/core/agent/terminal_status_listener.py +394 -0
- omnimancer/core/agent/token_tracker.py +679 -0
- omnimancer/core/agent/types.py +60 -0
- omnimancer/core/agent/web_client.py +745 -0
- omnimancer/core/agent/workflow_orchestrator.py +826 -0
- omnimancer/core/agent_engine.py +1625 -0
- omnimancer/core/agent_mode_manager.py +706 -0
- omnimancer/core/agent_progress_ui.py +545 -0
- omnimancer/core/chat_manager.py +141 -0
- omnimancer/core/config_manager.py +2199 -0
- omnimancer/core/config_migration.py +772 -0
- omnimancer/core/config_provider.py +1974 -0
- omnimancer/core/config_security_validator.py +393 -0
- omnimancer/core/config_validator.py +768 -0
- omnimancer/core/conversation_manager.py +352 -0
- omnimancer/core/engine.py +787 -0
- omnimancer/core/enhanced_input.py +523 -0
- omnimancer/core/env_loader.py +83 -0
- omnimancer/core/fallback_manager.py +568 -0
- omnimancer/core/health_monitor.py +462 -0
- omnimancer/core/history_manager.py +462 -0
- omnimancer/core/mcp_integration_layer.py +878 -0
- omnimancer/core/models.py +1889 -0
- omnimancer/core/provider_capabilities.py +403 -0
- omnimancer/core/provider_initializer.py +450 -0
- omnimancer/core/provider_registry.py +432 -0
- omnimancer/core/security/__init__.py +16 -0
- omnimancer/core/security/approval_workflow.py +406 -0
- omnimancer/core/security/audit_logger.py +511 -0
- omnimancer/core/security/permission_controller.py +511 -0
- omnimancer/core/security/sandbox_manager.py +379 -0
- omnimancer/core/security/security_manager.py +530 -0
- omnimancer/core/setup_wizard.py +345 -0
- omnimancer/core/setup_wizard_core.py +449 -0
- omnimancer/core/setup_wizard_guide_generator.py +376 -0
- omnimancer/core/setup_wizard_provider_setup.py +454 -0
- omnimancer/core/setup_wizard_ui.py +317 -0
- omnimancer/core/setup_wizard_validation.py +371 -0
- omnimancer/core/signal_handler.py +174 -0
- omnimancer/mcp/__init__.py +11 -0
- omnimancer/mcp/client.py +725 -0
- omnimancer/mcp/manager.py +649 -0
- omnimancer/providers/__init__.py +24 -0
- omnimancer/providers/azure.py +608 -0
- omnimancer/providers/base.py +262 -0
- omnimancer/providers/bedrock.py +711 -0
- omnimancer/providers/claude.py +426 -0
- omnimancer/providers/claude_code.py +406 -0
- omnimancer/providers/cohere.py +412 -0
- omnimancer/providers/factory.py +988 -0
- omnimancer/providers/gemini.py +708 -0
- omnimancer/providers/mistral.py +554 -0
- omnimancer/providers/ollama.py +510 -0
- omnimancer/providers/openai.py +362 -0
- omnimancer/providers/openrouter.py +709 -0
- omnimancer/providers/perplexity.py +476 -0
- omnimancer/providers/vertex.py +617 -0
- omnimancer/providers/xai.py +484 -0
- omnimancer/ui/__init__.py +6 -0
- omnimancer/ui/cancellation_handler.py +235 -0
- omnimancer/ui/progress_indicator.py +318 -0
- omnimancer/utils/__init__.py +22 -0
- omnimancer/utils/error_handler.py +589 -0
- omnimancer/utils/errors.py +667 -0
- omnimancer/utils/retry.py +238 -0
- omnimancer_cli-0.1.0.dist-info/METADATA +413 -0
- omnimancer_cli-0.1.0.dist-info/RECORD +126 -0
- omnimancer_cli-0.1.0.dist-info/WHEEL +5 -0
- omnimancer_cli-0.1.0.dist-info/entry_points.txt +4 -0
- omnimancer_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
- omnimancer_cli-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1974 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Comprehensive Configuration Provider for Omnimancer.
|
|
3
|
+
|
|
4
|
+
This module consolidates configuration management, validation, compatibility,
|
|
5
|
+
and user interfaces into a single cohesive system that replaces the functionality
|
|
6
|
+
of multiple overlapping configuration modules.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import logging
|
|
11
|
+
from dataclasses import asdict, dataclass
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
from enum import Enum
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
16
|
+
|
|
17
|
+
from .config_manager import ConfigManager
|
|
18
|
+
from .config_validator import ConfigValidator
|
|
19
|
+
|
|
20
|
+
# from .config_migration_helpers import ConfigMigrationHelper # Removed - over-engineered
|
|
21
|
+
from .models import (
|
|
22
|
+
Config,
|
|
23
|
+
ConfigTemplate,
|
|
24
|
+
ConfigTemplateManager,
|
|
25
|
+
ProviderConfig,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ConfigurationMode(Enum):
|
|
32
|
+
"""Configuration modes for different user experience levels."""
|
|
33
|
+
|
|
34
|
+
SIMPLE = "simple"
|
|
35
|
+
ADVANCED = "advanced"
|
|
36
|
+
GUIDED = "guided"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ConfigurationComplexity(Enum):
|
|
40
|
+
"""Complexity levels for configuration options."""
|
|
41
|
+
|
|
42
|
+
BASIC = "basic"
|
|
43
|
+
INTERMEDIATE = "intermediate"
|
|
44
|
+
ADVANCED = "advanced"
|
|
45
|
+
EXPERT = "expert"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class ConfigurationContext:
|
|
50
|
+
"""Context information for configuration operations."""
|
|
51
|
+
|
|
52
|
+
user_level: str = "beginner"
|
|
53
|
+
use_case: str = "general"
|
|
54
|
+
primary_providers: List[str] = None
|
|
55
|
+
preferred_mode: ConfigurationMode = ConfigurationMode.SIMPLE
|
|
56
|
+
show_experimental: bool = False
|
|
57
|
+
|
|
58
|
+
def __post_init__(self):
|
|
59
|
+
if self.primary_providers is None:
|
|
60
|
+
self.primary_providers = []
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class ValidationReport:
|
|
65
|
+
"""Comprehensive validation and compatibility report."""
|
|
66
|
+
|
|
67
|
+
is_valid: bool
|
|
68
|
+
is_compatible: bool
|
|
69
|
+
compatibility_level: str
|
|
70
|
+
total_issues: int
|
|
71
|
+
critical_errors: List[str]
|
|
72
|
+
warnings: List[str]
|
|
73
|
+
suggestions: List[str]
|
|
74
|
+
health_score: float # 0-100
|
|
75
|
+
format_info: Dict[str, Any]
|
|
76
|
+
provider_status: Dict[str, Dict[str, Any]]
|
|
77
|
+
performance_issues: List[str]
|
|
78
|
+
security_issues: List[str]
|
|
79
|
+
upgrade_recommendations: List[str]
|
|
80
|
+
migration_required: bool
|
|
81
|
+
backup_available: bool
|
|
82
|
+
timestamp: str
|
|
83
|
+
|
|
84
|
+
def __post_init__(self):
|
|
85
|
+
if not self.timestamp:
|
|
86
|
+
self.timestamp = datetime.now().isoformat()
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@dataclass
|
|
90
|
+
class SimplifiedProviderConfig:
|
|
91
|
+
"""Simplified provider configuration for simple mode."""
|
|
92
|
+
|
|
93
|
+
name: str
|
|
94
|
+
display_name: str
|
|
95
|
+
description: str
|
|
96
|
+
enabled: bool = True
|
|
97
|
+
api_key: Optional[str] = None
|
|
98
|
+
model: Optional[str] = None
|
|
99
|
+
use_case_fit: List[str] = None
|
|
100
|
+
|
|
101
|
+
def __post_init__(self):
|
|
102
|
+
if self.use_case_fit is None:
|
|
103
|
+
self.use_case_fit = ["general"]
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@dataclass
|
|
107
|
+
class ProviderHealthCheck:
|
|
108
|
+
"""Provider health check result."""
|
|
109
|
+
|
|
110
|
+
name: str
|
|
111
|
+
status: str # "healthy", "warning", "error", "unknown"
|
|
112
|
+
api_key_present: bool
|
|
113
|
+
model_valid: bool
|
|
114
|
+
connectivity_ok: bool
|
|
115
|
+
issues: List[str]
|
|
116
|
+
recommendations: List[str]
|
|
117
|
+
last_tested: str
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@dataclass
|
|
121
|
+
class ConfigurationRecommendation:
|
|
122
|
+
"""Recommendation for configuration based on user context."""
|
|
123
|
+
|
|
124
|
+
template_name: str
|
|
125
|
+
confidence: float
|
|
126
|
+
reasons: List[str]
|
|
127
|
+
providers: List[str]
|
|
128
|
+
estimated_cost: str
|
|
129
|
+
complexity: ConfigurationComplexity
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@dataclass
|
|
133
|
+
class SimpleConfigOption:
|
|
134
|
+
"""Represents a simple configuration option for users."""
|
|
135
|
+
|
|
136
|
+
key: str
|
|
137
|
+
display_name: str
|
|
138
|
+
description: str
|
|
139
|
+
option_type: str # "choice", "boolean", "text", "password"
|
|
140
|
+
default_value: Any = None
|
|
141
|
+
choices: Optional[List[Dict[str, str]]] = None
|
|
142
|
+
required: bool = True
|
|
143
|
+
complexity: ConfigurationComplexity = ConfigurationComplexity.BASIC
|
|
144
|
+
category: str = "General"
|
|
145
|
+
|
|
146
|
+
def __post_init__(self):
|
|
147
|
+
if self.choices is None:
|
|
148
|
+
self.choices = []
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@dataclass
|
|
152
|
+
class SimpleConfigSection:
|
|
153
|
+
"""A section of related configuration options."""
|
|
154
|
+
|
|
155
|
+
name: str
|
|
156
|
+
description: str
|
|
157
|
+
icon: str
|
|
158
|
+
options: List[SimpleConfigOption]
|
|
159
|
+
required: bool = True
|
|
160
|
+
expanded: bool = True
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class ConfigurationProvider:
|
|
164
|
+
"""
|
|
165
|
+
Comprehensive configuration provider that consolidates all configuration functionality.
|
|
166
|
+
|
|
167
|
+
This class provides:
|
|
168
|
+
- Configuration management and validation
|
|
169
|
+
- Compatibility checking and migration
|
|
170
|
+
- Simple and advanced user interfaces
|
|
171
|
+
- Provider health monitoring
|
|
172
|
+
- Template-based configuration
|
|
173
|
+
"""
|
|
174
|
+
|
|
175
|
+
def __init__(self, config_manager: Optional[ConfigManager] = None):
|
|
176
|
+
"""
|
|
177
|
+
Initialize the configuration provider.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
config_manager: Optional ConfigManager instance
|
|
181
|
+
"""
|
|
182
|
+
self.config_manager = config_manager or ConfigManager()
|
|
183
|
+
self.template_manager = ConfigTemplateManager()
|
|
184
|
+
self.base_validator = ConfigValidator()
|
|
185
|
+
# self.migration_helper = ConfigMigrationHelper(self.config_manager) # Removed - over-engineered
|
|
186
|
+
|
|
187
|
+
# Configuration state
|
|
188
|
+
self.current_mode = ConfigurationMode.SIMPLE
|
|
189
|
+
self.context = ConfigurationContext()
|
|
190
|
+
self.current_version = "2.0"
|
|
191
|
+
self.legacy_version = "1.0"
|
|
192
|
+
|
|
193
|
+
# Load mode preferences
|
|
194
|
+
self._load_mode_preferences()
|
|
195
|
+
|
|
196
|
+
# Initialize simple options
|
|
197
|
+
self.simple_sections = self._create_simple_sections()
|
|
198
|
+
|
|
199
|
+
# =============================================================================
|
|
200
|
+
# Mode Management
|
|
201
|
+
# =============================================================================
|
|
202
|
+
|
|
203
|
+
def set_configuration_mode(self, mode: ConfigurationMode) -> None:
|
|
204
|
+
"""Set the configuration mode."""
|
|
205
|
+
self.current_mode = mode
|
|
206
|
+
self._save_mode_preferences()
|
|
207
|
+
logger.info(f"Configuration mode set to: {mode.value}")
|
|
208
|
+
|
|
209
|
+
def set_configuration_context(self, context: ConfigurationContext) -> None:
|
|
210
|
+
"""Set the configuration context for personalized experience."""
|
|
211
|
+
self.context = context
|
|
212
|
+
self._save_mode_preferences()
|
|
213
|
+
logger.info(
|
|
214
|
+
f"Configuration context updated: use_case={context.use_case}, level={context.user_level}"
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
def get_available_modes(self) -> Dict[str, Dict[str, Any]]:
|
|
218
|
+
"""Get information about available configuration modes."""
|
|
219
|
+
return {
|
|
220
|
+
ConfigurationMode.SIMPLE.value: {
|
|
221
|
+
"name": "Simple",
|
|
222
|
+
"description": "Template-based configuration with minimal technical details",
|
|
223
|
+
"target_audience": "Beginners and users who want quick setup",
|
|
224
|
+
"features": [
|
|
225
|
+
"Pre-configured templates for common use cases",
|
|
226
|
+
"Automatic provider selection and optimization",
|
|
227
|
+
"Simplified settings with explanations",
|
|
228
|
+
"One-click setup for popular configurations",
|
|
229
|
+
],
|
|
230
|
+
},
|
|
231
|
+
ConfigurationMode.GUIDED.value: {
|
|
232
|
+
"name": "Guided",
|
|
233
|
+
"description": "Step-by-step configuration with recommendations",
|
|
234
|
+
"target_audience": "Users who want some control with guidance",
|
|
235
|
+
"features": [
|
|
236
|
+
"Interactive configuration wizard",
|
|
237
|
+
"Smart recommendations based on usage patterns",
|
|
238
|
+
"Explanations for configuration choices",
|
|
239
|
+
"Ability to customize templates",
|
|
240
|
+
],
|
|
241
|
+
},
|
|
242
|
+
ConfigurationMode.ADVANCED.value: {
|
|
243
|
+
"name": "Advanced",
|
|
244
|
+
"description": "Full access to all configuration options",
|
|
245
|
+
"target_audience": "Power users and developers",
|
|
246
|
+
"features": [
|
|
247
|
+
"Complete configuration control",
|
|
248
|
+
"Access to all provider settings",
|
|
249
|
+
"Custom MCP server configuration",
|
|
250
|
+
"Advanced debugging options",
|
|
251
|
+
],
|
|
252
|
+
},
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
# =============================================================================
|
|
256
|
+
# Validation & Compatibility
|
|
257
|
+
# =============================================================================
|
|
258
|
+
|
|
259
|
+
async def comprehensive_validation(
|
|
260
|
+
self, config_path: Optional[Path] = None
|
|
261
|
+
) -> ValidationReport:
|
|
262
|
+
"""Perform comprehensive configuration validation and compatibility checking."""
|
|
263
|
+
try:
|
|
264
|
+
# Detect configuration format and compatibility
|
|
265
|
+
format_info = self._detect_configuration_format(config_path)
|
|
266
|
+
is_compatible, comp_errors, comp_warnings = self._validate_compatibility(
|
|
267
|
+
config_path
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
# Load configuration safely
|
|
271
|
+
config = await self._load_config_safely(config_path)
|
|
272
|
+
|
|
273
|
+
# Perform base validation
|
|
274
|
+
base_errors = []
|
|
275
|
+
if config:
|
|
276
|
+
base_errors = self.base_validator.validate_config(config)
|
|
277
|
+
|
|
278
|
+
# Combine all errors and warnings
|
|
279
|
+
all_errors = comp_errors + base_errors
|
|
280
|
+
critical_errors = [e for e in all_errors if self._is_critical_error(e)]
|
|
281
|
+
all_warnings = comp_warnings
|
|
282
|
+
|
|
283
|
+
# Perform provider health checks
|
|
284
|
+
provider_status = (
|
|
285
|
+
await self._check_provider_health(config) if config else {}
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
# Check for performance and security issues
|
|
289
|
+
performance_issues = (
|
|
290
|
+
self._check_performance_issues(config, format_info) if config else []
|
|
291
|
+
)
|
|
292
|
+
security_issues = self._check_security_issues(config) if config else []
|
|
293
|
+
|
|
294
|
+
# Generate suggestions and upgrade recommendations
|
|
295
|
+
suggestions = self._generate_suggestions(
|
|
296
|
+
config, format_info, all_errors, all_warnings
|
|
297
|
+
)
|
|
298
|
+
upgrade_recommendations = self._generate_upgrade_recommendations(
|
|
299
|
+
format_info
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
# Calculate health score
|
|
303
|
+
health_score = self._calculate_health_score(
|
|
304
|
+
critical_errors,
|
|
305
|
+
all_warnings,
|
|
306
|
+
provider_status,
|
|
307
|
+
performance_issues,
|
|
308
|
+
security_issues,
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
# Check for migration and backup
|
|
312
|
+
migration_required = (
|
|
313
|
+
len(all_errors) > 3
|
|
314
|
+
or format_info.get("version", self.current_version)
|
|
315
|
+
< self.current_version
|
|
316
|
+
)
|
|
317
|
+
backup_available = self._check_backup_availability()
|
|
318
|
+
|
|
319
|
+
return ValidationReport(
|
|
320
|
+
is_valid=len(critical_errors) == 0,
|
|
321
|
+
is_compatible=is_compatible,
|
|
322
|
+
compatibility_level=format_info.get("compatibility_level", "unknown"),
|
|
323
|
+
total_issues=len(all_errors) + len(all_warnings),
|
|
324
|
+
critical_errors=critical_errors,
|
|
325
|
+
warnings=all_warnings,
|
|
326
|
+
suggestions=suggestions,
|
|
327
|
+
health_score=health_score,
|
|
328
|
+
format_info=format_info,
|
|
329
|
+
provider_status=provider_status,
|
|
330
|
+
performance_issues=performance_issues,
|
|
331
|
+
security_issues=security_issues,
|
|
332
|
+
upgrade_recommendations=upgrade_recommendations,
|
|
333
|
+
migration_required=migration_required,
|
|
334
|
+
backup_available=backup_available,
|
|
335
|
+
timestamp=datetime.now().isoformat(),
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
except Exception as e:
|
|
339
|
+
logger.error(f"Comprehensive validation failed: {e}")
|
|
340
|
+
return ValidationReport(
|
|
341
|
+
is_valid=False,
|
|
342
|
+
is_compatible=False,
|
|
343
|
+
compatibility_level="unknown",
|
|
344
|
+
total_issues=1,
|
|
345
|
+
critical_errors=[f"Validation failed: {str(e)}"],
|
|
346
|
+
warnings=[],
|
|
347
|
+
suggestions=["Check configuration file format and permissions"],
|
|
348
|
+
health_score=0.0,
|
|
349
|
+
format_info={},
|
|
350
|
+
provider_status={},
|
|
351
|
+
performance_issues=[],
|
|
352
|
+
security_issues=[],
|
|
353
|
+
upgrade_recommendations=[],
|
|
354
|
+
migration_required=True,
|
|
355
|
+
backup_available=False,
|
|
356
|
+
timestamp=datetime.now().isoformat(),
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
def _detect_configuration_format(
|
|
360
|
+
self, config_path: Optional[Path] = None
|
|
361
|
+
) -> Dict[str, Any]:
|
|
362
|
+
"""Detect the format and version of an existing configuration."""
|
|
363
|
+
if config_path is None:
|
|
364
|
+
config_path = self.config_manager.get_storage_path() / "config.json"
|
|
365
|
+
|
|
366
|
+
try:
|
|
367
|
+
if not config_path.exists():
|
|
368
|
+
return {
|
|
369
|
+
"version": "none",
|
|
370
|
+
"format_type": "none",
|
|
371
|
+
"detected_features": [],
|
|
372
|
+
"compatibility_level": "full",
|
|
373
|
+
"upgrade_available": False,
|
|
374
|
+
"issues": ["No configuration file found"],
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
with open(config_path, "r") as f:
|
|
378
|
+
config_data = json.load(f)
|
|
379
|
+
|
|
380
|
+
return self._analyze_config_format(config_data)
|
|
381
|
+
|
|
382
|
+
except Exception as e:
|
|
383
|
+
logger.error(f"Failed to detect configuration format: {e}")
|
|
384
|
+
return {
|
|
385
|
+
"version": "unknown",
|
|
386
|
+
"format_type": "unknown",
|
|
387
|
+
"detected_features": [],
|
|
388
|
+
"compatibility_level": "incompatible",
|
|
389
|
+
"upgrade_available": False,
|
|
390
|
+
"issues": [f"Failed to read configuration: {str(e)}"],
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
def _analyze_config_format(self, config_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
394
|
+
"""Analyze configuration data to determine format."""
|
|
395
|
+
detected_features = []
|
|
396
|
+
format_type = "legacy"
|
|
397
|
+
version = "1.0"
|
|
398
|
+
compatibility_level = "full"
|
|
399
|
+
upgrade_available = True
|
|
400
|
+
issues = []
|
|
401
|
+
|
|
402
|
+
# Check for version information
|
|
403
|
+
if "config_version" in config_data:
|
|
404
|
+
version = config_data["config_version"]
|
|
405
|
+
elif "_schema_version" in config_data:
|
|
406
|
+
version = config_data["_schema_version"]
|
|
407
|
+
|
|
408
|
+
# Detect features based on configuration structure
|
|
409
|
+
feature_checks = {
|
|
410
|
+
"providers": "providers",
|
|
411
|
+
"default_provider": "default_provider",
|
|
412
|
+
"mcp": "mcp",
|
|
413
|
+
"chat_settings": "chat_settings",
|
|
414
|
+
"configuration_mode": "configuration_mode",
|
|
415
|
+
"user_preferences": "user_preferences",
|
|
416
|
+
"template_source": "template_source",
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
for feature, key in feature_checks.items():
|
|
420
|
+
if key in config_data:
|
|
421
|
+
detected_features.append(feature)
|
|
422
|
+
|
|
423
|
+
# Determine format type
|
|
424
|
+
if "configuration_mode" in detected_features:
|
|
425
|
+
format_type = "advanced"
|
|
426
|
+
elif (
|
|
427
|
+
"user_preferences" in detected_features
|
|
428
|
+
or "template_source" in detected_features
|
|
429
|
+
):
|
|
430
|
+
format_type = "simple"
|
|
431
|
+
|
|
432
|
+
# Check for issues
|
|
433
|
+
if "providers" in config_data and len(config_data["providers"]) > 5:
|
|
434
|
+
issues.append(
|
|
435
|
+
f"Large number of providers ({len(config_data['providers'])}) might benefit from simplification"
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
# Check for deprecated fields
|
|
439
|
+
deprecated_fields = [
|
|
440
|
+
"legacy_auth",
|
|
441
|
+
"old_mcp_format",
|
|
442
|
+
"deprecated_settings",
|
|
443
|
+
]
|
|
444
|
+
for field in deprecated_fields:
|
|
445
|
+
if field in config_data:
|
|
446
|
+
issues.append(f"Deprecated field '{field}' found")
|
|
447
|
+
compatibility_level = "partial"
|
|
448
|
+
|
|
449
|
+
return {
|
|
450
|
+
"version": version,
|
|
451
|
+
"format_type": format_type,
|
|
452
|
+
"detected_features": detected_features,
|
|
453
|
+
"compatibility_level": compatibility_level,
|
|
454
|
+
"upgrade_available": upgrade_available and version < self.current_version,
|
|
455
|
+
"issues": issues,
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
def _validate_compatibility(
|
|
459
|
+
self, config_path: Optional[Path] = None
|
|
460
|
+
) -> Tuple[bool, List[str], List[str]]:
|
|
461
|
+
"""Validate configuration compatibility."""
|
|
462
|
+
errors = []
|
|
463
|
+
warnings = []
|
|
464
|
+
|
|
465
|
+
try:
|
|
466
|
+
format_info = self._detect_configuration_format(config_path)
|
|
467
|
+
|
|
468
|
+
if format_info["compatibility_level"] == "incompatible":
|
|
469
|
+
errors.append(
|
|
470
|
+
"Configuration format is incompatible and requires manual migration"
|
|
471
|
+
)
|
|
472
|
+
errors.extend(format_info["issues"])
|
|
473
|
+
return False, errors, warnings
|
|
474
|
+
|
|
475
|
+
elif format_info["compatibility_level"] == "partial":
|
|
476
|
+
warnings.append(
|
|
477
|
+
"Configuration has compatibility issues that may need attention"
|
|
478
|
+
)
|
|
479
|
+
warnings.extend(format_info["issues"])
|
|
480
|
+
|
|
481
|
+
# Load and validate configuration
|
|
482
|
+
if config_path is None:
|
|
483
|
+
config_path = self.config_manager.get_storage_path() / "config.json"
|
|
484
|
+
|
|
485
|
+
if not config_path.exists():
|
|
486
|
+
errors.append("Configuration file not found")
|
|
487
|
+
return False, errors, warnings
|
|
488
|
+
|
|
489
|
+
try:
|
|
490
|
+
config = self.config_manager.load_config()
|
|
491
|
+
validation_errors = self.base_validator.validate_config(config)
|
|
492
|
+
|
|
493
|
+
if validation_errors:
|
|
494
|
+
for error in validation_errors:
|
|
495
|
+
if any(
|
|
496
|
+
critical in error.lower()
|
|
497
|
+
for critical in ["missing", "required", "invalid"]
|
|
498
|
+
):
|
|
499
|
+
errors.append(error)
|
|
500
|
+
else:
|
|
501
|
+
warnings.append(error)
|
|
502
|
+
|
|
503
|
+
except Exception as e:
|
|
504
|
+
errors.append(f"Configuration loading failed: {str(e)}")
|
|
505
|
+
return False, errors, warnings
|
|
506
|
+
|
|
507
|
+
if format_info["upgrade_available"]:
|
|
508
|
+
warnings.append(
|
|
509
|
+
f"Configuration upgrade available: v{format_info['version']} → v{self.current_version}"
|
|
510
|
+
)
|
|
511
|
+
|
|
512
|
+
is_compatible = len(errors) == 0
|
|
513
|
+
return is_compatible, errors, warnings
|
|
514
|
+
|
|
515
|
+
except Exception as e:
|
|
516
|
+
logger.error(f"Compatibility validation failed: {e}")
|
|
517
|
+
errors.append(f"Validation failed: {str(e)}")
|
|
518
|
+
return False, errors, warnings
|
|
519
|
+
|
|
520
|
+
async def _load_config_safely(
|
|
521
|
+
self, config_path: Optional[Path] = None
|
|
522
|
+
) -> Optional[Config]:
|
|
523
|
+
"""Safely load configuration without failing validation."""
|
|
524
|
+
try:
|
|
525
|
+
if config_path:
|
|
526
|
+
with open(config_path, "r") as f:
|
|
527
|
+
config_data = json.load(f)
|
|
528
|
+
return self.config_manager.load_config_from_dict(config_data)
|
|
529
|
+
else:
|
|
530
|
+
return self.config_manager.load_config()
|
|
531
|
+
except Exception as e:
|
|
532
|
+
logger.debug(f"Failed to load configuration: {e}")
|
|
533
|
+
return None
|
|
534
|
+
|
|
535
|
+
def _is_critical_error(self, error: str) -> bool:
|
|
536
|
+
"""Determine if an error is critical."""
|
|
537
|
+
critical_keywords = [
|
|
538
|
+
"missing api key",
|
|
539
|
+
"no providers configured",
|
|
540
|
+
"default provider not configured",
|
|
541
|
+
"invalid storage path",
|
|
542
|
+
"configuration loading failed",
|
|
543
|
+
"incompatible",
|
|
544
|
+
]
|
|
545
|
+
return any(keyword in error.lower() for keyword in critical_keywords)
|
|
546
|
+
|
|
547
|
+
async def _check_provider_health(self, config: Config) -> Dict[str, Dict[str, Any]]:
|
|
548
|
+
"""Check health of all configured providers."""
|
|
549
|
+
provider_status = {}
|
|
550
|
+
|
|
551
|
+
for provider_name, provider_config in config.providers.items():
|
|
552
|
+
health_check = await self._check_single_provider_health(
|
|
553
|
+
provider_name, provider_config
|
|
554
|
+
)
|
|
555
|
+
provider_status[provider_name] = asdict(health_check)
|
|
556
|
+
|
|
557
|
+
return provider_status
|
|
558
|
+
|
|
559
|
+
async def _check_single_provider_health(
|
|
560
|
+
self, provider_name: str, provider_config: ProviderConfig
|
|
561
|
+
) -> ProviderHealthCheck:
|
|
562
|
+
"""Check health of a single provider."""
|
|
563
|
+
issues = []
|
|
564
|
+
recommendations = []
|
|
565
|
+
status = "healthy"
|
|
566
|
+
|
|
567
|
+
# Check API key
|
|
568
|
+
api_key_present = bool(provider_config.api_key)
|
|
569
|
+
if not api_key_present and provider_name not in [
|
|
570
|
+
"claude-code",
|
|
571
|
+
"ollama",
|
|
572
|
+
]:
|
|
573
|
+
issues.append("API key missing")
|
|
574
|
+
recommendations.append("Add API key for this provider")
|
|
575
|
+
status = "error"
|
|
576
|
+
|
|
577
|
+
# Check model validity
|
|
578
|
+
model_valid = bool(provider_config.model)
|
|
579
|
+
if not model_valid:
|
|
580
|
+
issues.append("No model specified")
|
|
581
|
+
recommendations.append("Specify a valid model for this provider")
|
|
582
|
+
status = "error"
|
|
583
|
+
|
|
584
|
+
# Check model name validity
|
|
585
|
+
if model_valid and provider_config.model:
|
|
586
|
+
model_issues = self._validate_model_name(
|
|
587
|
+
provider_name, provider_config.model
|
|
588
|
+
)
|
|
589
|
+
if model_issues:
|
|
590
|
+
issues.extend(model_issues)
|
|
591
|
+
recommendations.append("Update to a supported model name")
|
|
592
|
+
if status == "healthy":
|
|
593
|
+
status = "warning"
|
|
594
|
+
|
|
595
|
+
# Check connectivity
|
|
596
|
+
connectivity_ok = api_key_present and model_valid
|
|
597
|
+
if not connectivity_ok and status == "healthy":
|
|
598
|
+
status = "warning"
|
|
599
|
+
|
|
600
|
+
# Check provider-specific issues
|
|
601
|
+
provider_specific_issues = self._check_provider_specific_issues(
|
|
602
|
+
provider_name, provider_config
|
|
603
|
+
)
|
|
604
|
+
if provider_specific_issues:
|
|
605
|
+
issues.extend(provider_specific_issues)
|
|
606
|
+
if status == "healthy":
|
|
607
|
+
status = "warning"
|
|
608
|
+
|
|
609
|
+
return ProviderHealthCheck(
|
|
610
|
+
name=provider_name,
|
|
611
|
+
status=status,
|
|
612
|
+
api_key_present=api_key_present,
|
|
613
|
+
model_valid=model_valid,
|
|
614
|
+
connectivity_ok=connectivity_ok,
|
|
615
|
+
issues=issues,
|
|
616
|
+
recommendations=recommendations,
|
|
617
|
+
last_tested=datetime.now().isoformat(),
|
|
618
|
+
)
|
|
619
|
+
|
|
620
|
+
def _validate_model_name(self, provider_name: str, model_name: str) -> List[str]:
|
|
621
|
+
"""Validate model name for a provider."""
|
|
622
|
+
issues = []
|
|
623
|
+
|
|
624
|
+
known_models = {
|
|
625
|
+
"claude": [
|
|
626
|
+
"claude-3-sonnet-20240229",
|
|
627
|
+
"claude-3-haiku-20240307",
|
|
628
|
+
"claude-3-opus-20240229",
|
|
629
|
+
"claude-3-5-sonnet-20241022",
|
|
630
|
+
],
|
|
631
|
+
"openai": [
|
|
632
|
+
"gpt-4",
|
|
633
|
+
"gpt-4-turbo",
|
|
634
|
+
"gpt-4o",
|
|
635
|
+
"gpt-4o-mini",
|
|
636
|
+
"gpt-3.5-turbo",
|
|
637
|
+
],
|
|
638
|
+
"gemini": ["gemini-1.5-pro", "gemini-1.5-flash", "gemini-1.0-pro"],
|
|
639
|
+
"perplexity": ["sonar-pro", "sonar", "deep-research"],
|
|
640
|
+
"mistral": ["mistral-small", "mistral-medium", "mistral-large"],
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
if provider_name in known_models:
|
|
644
|
+
valid_models = known_models[provider_name]
|
|
645
|
+
if not any(model in model_name for model in valid_models):
|
|
646
|
+
issues.append(
|
|
647
|
+
f"Model '{model_name}' may not be valid for {provider_name}"
|
|
648
|
+
)
|
|
649
|
+
|
|
650
|
+
return issues
|
|
651
|
+
|
|
652
|
+
def _check_provider_specific_issues(
|
|
653
|
+
self, provider_name: str, provider_config: ProviderConfig
|
|
654
|
+
) -> List[str]:
|
|
655
|
+
"""Check provider-specific configuration issues."""
|
|
656
|
+
issues = []
|
|
657
|
+
|
|
658
|
+
if provider_name.lower() == "azure":
|
|
659
|
+
if (
|
|
660
|
+
not hasattr(provider_config, "azure_endpoint")
|
|
661
|
+
or not provider_config.azure_endpoint
|
|
662
|
+
):
|
|
663
|
+
issues.append("Azure endpoint not specified")
|
|
664
|
+
if (
|
|
665
|
+
not hasattr(provider_config, "azure_deployment")
|
|
666
|
+
or not provider_config.azure_deployment
|
|
667
|
+
):
|
|
668
|
+
issues.append("Azure deployment not specified")
|
|
669
|
+
elif provider_name.lower() == "bedrock":
|
|
670
|
+
if (
|
|
671
|
+
not hasattr(provider_config, "aws_region")
|
|
672
|
+
or not provider_config.aws_region
|
|
673
|
+
):
|
|
674
|
+
issues.append("AWS region not specified")
|
|
675
|
+
elif provider_name.lower() == "ollama":
|
|
676
|
+
if hasattr(provider_config, "base_url") and provider_config.base_url:
|
|
677
|
+
if not provider_config.base_url.startswith(("http://", "https://")):
|
|
678
|
+
issues.append("Invalid Ollama base URL format")
|
|
679
|
+
|
|
680
|
+
return issues
|
|
681
|
+
|
|
682
|
+
def _check_performance_issues(
|
|
683
|
+
self, config: Config, format_info: Dict[str, Any]
|
|
684
|
+
) -> List[str]:
|
|
685
|
+
"""Check for performance-related configuration issues."""
|
|
686
|
+
issues = []
|
|
687
|
+
|
|
688
|
+
if len(config.providers) > 8:
|
|
689
|
+
issues.append(
|
|
690
|
+
f"Large number of providers ({len(config.providers)}) may impact performance"
|
|
691
|
+
)
|
|
692
|
+
|
|
693
|
+
timeout_issues = sum(
|
|
694
|
+
1
|
|
695
|
+
for provider_config in config.providers.values()
|
|
696
|
+
if not hasattr(provider_config, "timeout") or not provider_config.timeout
|
|
697
|
+
)
|
|
698
|
+
|
|
699
|
+
if timeout_issues > len(config.providers) / 2:
|
|
700
|
+
issues.append("Many providers missing timeout configurations")
|
|
701
|
+
|
|
702
|
+
if hasattr(config, "mcp") and config.mcp and hasattr(config.mcp, "servers"):
|
|
703
|
+
if len(config.mcp.servers) > 10:
|
|
704
|
+
issues.append(
|
|
705
|
+
f"Large number of MCP servers ({len(config.mcp.servers)}) may impact startup time"
|
|
706
|
+
)
|
|
707
|
+
|
|
708
|
+
if (
|
|
709
|
+
hasattr(config, "configuration_mode")
|
|
710
|
+
and config.configuration_mode == "simple"
|
|
711
|
+
):
|
|
712
|
+
if len(config.providers) > 3:
|
|
713
|
+
issues.append("Simple mode with many providers may cause confusion")
|
|
714
|
+
|
|
715
|
+
return issues
|
|
716
|
+
|
|
717
|
+
def _check_security_issues(self, config: Config) -> List[str]:
|
|
718
|
+
"""Check for security-related configuration issues."""
|
|
719
|
+
issues = []
|
|
720
|
+
|
|
721
|
+
for provider_name, provider_config in config.providers.items():
|
|
722
|
+
if provider_config.api_key:
|
|
723
|
+
if len(provider_config.api_key) < 10:
|
|
724
|
+
issues.append(f"Suspiciously short API key for {provider_name}")
|
|
725
|
+
elif provider_config.api_key.startswith(("demo", "test", "example")):
|
|
726
|
+
issues.append(f"Possible demo/test API key for {provider_name}")
|
|
727
|
+
|
|
728
|
+
if hasattr(config, "mcp") and config.mcp:
|
|
729
|
+
if hasattr(config.mcp, "auto_approve") and config.mcp.auto_approve:
|
|
730
|
+
issues.append("MCP auto-approve enabled - may pose security risk")
|
|
731
|
+
|
|
732
|
+
return issues
|
|
733
|
+
|
|
734
|
+
def _generate_suggestions(
|
|
735
|
+
self,
|
|
736
|
+
config: Optional[Config],
|
|
737
|
+
format_info: Dict[str, Any],
|
|
738
|
+
errors: List[str],
|
|
739
|
+
warnings: List[str],
|
|
740
|
+
) -> List[str]:
|
|
741
|
+
"""Generate helpful suggestions for configuration improvement."""
|
|
742
|
+
suggestions = []
|
|
743
|
+
|
|
744
|
+
if any("no providers" in error.lower() for error in errors):
|
|
745
|
+
suggestions.append(
|
|
746
|
+
"Run 'omnimancer config setup' to configure AI providers"
|
|
747
|
+
)
|
|
748
|
+
|
|
749
|
+
if any("api key" in error.lower() for error in errors):
|
|
750
|
+
suggestions.append("Add missing API keys for enabled providers")
|
|
751
|
+
|
|
752
|
+
if any("model" in error.lower() for error in errors):
|
|
753
|
+
suggestions.append("Specify valid models for all providers")
|
|
754
|
+
|
|
755
|
+
if format_info.get("upgrade_available", False):
|
|
756
|
+
suggestions.append(
|
|
757
|
+
f"Upgrade configuration from v{format_info.get('version')} to v{self.current_version}"
|
|
758
|
+
)
|
|
759
|
+
|
|
760
|
+
if (
|
|
761
|
+
format_info.get("format_type") == "legacy"
|
|
762
|
+
and len(format_info.get("detected_features", [])) > 5
|
|
763
|
+
):
|
|
764
|
+
suggestions.append(
|
|
765
|
+
"Consider using simplified configuration mode for easier management"
|
|
766
|
+
)
|
|
767
|
+
|
|
768
|
+
if config and len(config.providers) == 1:
|
|
769
|
+
suggestions.append("Consider adding a backup provider for redundancy")
|
|
770
|
+
elif config and len(config.providers) > 5:
|
|
771
|
+
suggestions.append(
|
|
772
|
+
"Consider simplifying provider configuration for better performance"
|
|
773
|
+
)
|
|
774
|
+
|
|
775
|
+
if len(warnings) > 3:
|
|
776
|
+
suggestions.append("Review and address configuration warnings")
|
|
777
|
+
|
|
778
|
+
return suggestions
|
|
779
|
+
|
|
780
|
+
def _generate_upgrade_recommendations(
|
|
781
|
+
self, format_info: Dict[str, Any]
|
|
782
|
+
) -> List[str]:
|
|
783
|
+
"""Generate upgrade recommendations."""
|
|
784
|
+
recommendations = []
|
|
785
|
+
|
|
786
|
+
if format_info.get("upgrade_available", False):
|
|
787
|
+
compatibility_level = format_info.get("compatibility_level", "unknown")
|
|
788
|
+
if compatibility_level == "full":
|
|
789
|
+
recommendations.append(
|
|
790
|
+
"Automatic upgrade available - run 'omnimancer config migrate'"
|
|
791
|
+
)
|
|
792
|
+
elif compatibility_level == "partial":
|
|
793
|
+
recommendations.append(
|
|
794
|
+
"Assisted upgrade recommended - review configuration issues first"
|
|
795
|
+
)
|
|
796
|
+
else:
|
|
797
|
+
recommendations.append(
|
|
798
|
+
"Manual upgrade required - backup configuration before changes"
|
|
799
|
+
)
|
|
800
|
+
|
|
801
|
+
if (
|
|
802
|
+
"mcp" in format_info.get("detected_features", [])
|
|
803
|
+
and format_info.get("version", "2.0") < "2.0"
|
|
804
|
+
):
|
|
805
|
+
recommendations.append(
|
|
806
|
+
"MCP configuration can be simplified in newer format"
|
|
807
|
+
)
|
|
808
|
+
|
|
809
|
+
if len(format_info.get("detected_features", [])) > 6:
|
|
810
|
+
recommendations.append(
|
|
811
|
+
"Configuration complexity can be reduced with template-based setup"
|
|
812
|
+
)
|
|
813
|
+
|
|
814
|
+
return recommendations
|
|
815
|
+
|
|
816
|
+
def _calculate_health_score(
|
|
817
|
+
self,
|
|
818
|
+
critical_errors: List[str],
|
|
819
|
+
warnings: List[str],
|
|
820
|
+
provider_status: Dict[str, Dict[str, Any]],
|
|
821
|
+
performance_issues: List[str],
|
|
822
|
+
security_issues: List[str],
|
|
823
|
+
) -> float:
|
|
824
|
+
"""Calculate overall configuration health score (0-100)."""
|
|
825
|
+
score = 100.0
|
|
826
|
+
|
|
827
|
+
score -= len(critical_errors) * 25
|
|
828
|
+
score -= len(warnings) * 5
|
|
829
|
+
|
|
830
|
+
for provider_name, status in provider_status.items():
|
|
831
|
+
if status["status"] == "error":
|
|
832
|
+
score -= 15
|
|
833
|
+
elif status["status"] == "warning":
|
|
834
|
+
score -= 5
|
|
835
|
+
|
|
836
|
+
score -= len(performance_issues) * 3
|
|
837
|
+
score -= len(security_issues) * 10
|
|
838
|
+
|
|
839
|
+
return max(0.0, min(100.0, score))
|
|
840
|
+
|
|
841
|
+
def generate_compatibility_report(self) -> str:
|
|
842
|
+
"""Generate a comprehensive compatibility report."""
|
|
843
|
+
try:
|
|
844
|
+
# Get comprehensive validation report
|
|
845
|
+
import asyncio
|
|
846
|
+
|
|
847
|
+
validation_report = asyncio.run(self.comprehensive_validation())
|
|
848
|
+
|
|
849
|
+
report_lines = []
|
|
850
|
+
report_lines.append("# Configuration Compatibility Report")
|
|
851
|
+
report_lines.append("")
|
|
852
|
+
|
|
853
|
+
# Basic info
|
|
854
|
+
report_lines.append("## Configuration Status")
|
|
855
|
+
report_lines.append(
|
|
856
|
+
f"- Version: {validation_report.format_info.get('version', 'unknown')}"
|
|
857
|
+
)
|
|
858
|
+
report_lines.append(
|
|
859
|
+
f"- Format Type: {validation_report.format_info.get('format_type', 'unknown').title()}"
|
|
860
|
+
)
|
|
861
|
+
report_lines.append(
|
|
862
|
+
f"- Compatible: {'Yes' if validation_report.is_compatible else 'No'}"
|
|
863
|
+
)
|
|
864
|
+
report_lines.append(
|
|
865
|
+
f"- Valid: {'Yes' if validation_report.is_valid else 'No'}"
|
|
866
|
+
)
|
|
867
|
+
report_lines.append(
|
|
868
|
+
f"- Health Score: {validation_report.health_score:.1f}/100"
|
|
869
|
+
)
|
|
870
|
+
report_lines.append("")
|
|
871
|
+
|
|
872
|
+
# Issues
|
|
873
|
+
if validation_report.critical_errors:
|
|
874
|
+
report_lines.append("## Critical Errors")
|
|
875
|
+
for error in validation_report.critical_errors:
|
|
876
|
+
report_lines.append(f"- {error}")
|
|
877
|
+
report_lines.append("")
|
|
878
|
+
|
|
879
|
+
if validation_report.warnings:
|
|
880
|
+
report_lines.append("## Warnings")
|
|
881
|
+
for warning in validation_report.warnings:
|
|
882
|
+
report_lines.append(f"- {warning}")
|
|
883
|
+
report_lines.append("")
|
|
884
|
+
|
|
885
|
+
# Recommendations
|
|
886
|
+
if (
|
|
887
|
+
validation_report.suggestions
|
|
888
|
+
or validation_report.upgrade_recommendations
|
|
889
|
+
):
|
|
890
|
+
report_lines.append("## Recommendations")
|
|
891
|
+
for suggestion in validation_report.suggestions:
|
|
892
|
+
report_lines.append(f"- {suggestion}")
|
|
893
|
+
for upgrade in validation_report.upgrade_recommendations:
|
|
894
|
+
report_lines.append(f"- {upgrade}")
|
|
895
|
+
report_lines.append("")
|
|
896
|
+
|
|
897
|
+
# Provider details
|
|
898
|
+
if validation_report.provider_status:
|
|
899
|
+
report_lines.append("## Provider Status")
|
|
900
|
+
for (
|
|
901
|
+
provider,
|
|
902
|
+
status,
|
|
903
|
+
) in validation_report.provider_status.items():
|
|
904
|
+
status_icon = (
|
|
905
|
+
"✓"
|
|
906
|
+
if status["status"] == "healthy"
|
|
907
|
+
else "⚠" if status["status"] == "warning" else "✗"
|
|
908
|
+
)
|
|
909
|
+
report_lines.append(
|
|
910
|
+
f"- **{provider}**: {status_icon} {status['status'].title()}"
|
|
911
|
+
)
|
|
912
|
+
|
|
913
|
+
if status.get("issues"):
|
|
914
|
+
for issue in status["issues"]:
|
|
915
|
+
report_lines.append(f" - Issue: {issue}")
|
|
916
|
+
|
|
917
|
+
if status.get("recommendations"):
|
|
918
|
+
for rec in status["recommendations"]:
|
|
919
|
+
report_lines.append(f" - Recommendation: {rec}")
|
|
920
|
+
|
|
921
|
+
report_lines.append("")
|
|
922
|
+
|
|
923
|
+
# Migration info
|
|
924
|
+
if validation_report.migration_required:
|
|
925
|
+
report_lines.append("## Migration Recommended")
|
|
926
|
+
report_lines.append(
|
|
927
|
+
"Consider migrating to the latest configuration format for better features."
|
|
928
|
+
)
|
|
929
|
+
if validation_report.backup_available:
|
|
930
|
+
report_lines.append(
|
|
931
|
+
"Configuration backups are available for safe migration."
|
|
932
|
+
)
|
|
933
|
+
else:
|
|
934
|
+
report_lines.append(
|
|
935
|
+
"**Warning**: No configuration backups found. Create backup before migration."
|
|
936
|
+
)
|
|
937
|
+
|
|
938
|
+
return "\n".join(report_lines)
|
|
939
|
+
|
|
940
|
+
except Exception as e:
|
|
941
|
+
return f"Failed to generate compatibility report: {str(e)}"
|
|
942
|
+
|
|
943
|
+
def ensure_backward_compatibility(self) -> Tuple[bool, List[str]]:
|
|
944
|
+
"""Ensure backward compatibility with existing configurations."""
|
|
945
|
+
messages = []
|
|
946
|
+
|
|
947
|
+
try:
|
|
948
|
+
# Get validation report
|
|
949
|
+
import asyncio
|
|
950
|
+
|
|
951
|
+
validation_report = asyncio.run(self.comprehensive_validation())
|
|
952
|
+
|
|
953
|
+
if validation_report.is_valid and validation_report.is_compatible:
|
|
954
|
+
messages.append("✓ Configuration is backward compatible")
|
|
955
|
+
return True, messages
|
|
956
|
+
|
|
957
|
+
# Attempt automatic fixes for compatibility issues
|
|
958
|
+
messages.append("Applying backward compatibility fixes...")
|
|
959
|
+
|
|
960
|
+
try:
|
|
961
|
+
config = self.config_manager.get_config()
|
|
962
|
+
fixed_issues = 0
|
|
963
|
+
|
|
964
|
+
# Fix deprecated model references
|
|
965
|
+
for name, provider_config in config.providers.items():
|
|
966
|
+
if name == "claude" and provider_config.model == "claude-v1":
|
|
967
|
+
provider_config.model = "claude-3-haiku-20240307"
|
|
968
|
+
messages.append(f"✓ Updated {name} model to current version")
|
|
969
|
+
fixed_issues += 1
|
|
970
|
+
elif name == "openai" and provider_config.model in [
|
|
971
|
+
"text-davinci-003",
|
|
972
|
+
"code-davinci-002",
|
|
973
|
+
]:
|
|
974
|
+
provider_config.model = "gpt-4o-mini"
|
|
975
|
+
messages.append(f"✓ Updated {name} model to current version")
|
|
976
|
+
fixed_issues += 1
|
|
977
|
+
|
|
978
|
+
# Ensure configuration version is set
|
|
979
|
+
if not hasattr(config, "version"):
|
|
980
|
+
config.version = self.current_version
|
|
981
|
+
messages.append("✓ Added version information to configuration")
|
|
982
|
+
fixed_issues += 1
|
|
983
|
+
|
|
984
|
+
# Save fixes
|
|
985
|
+
if fixed_issues > 0:
|
|
986
|
+
self.config_manager.save_config(config)
|
|
987
|
+
messages.append(f"✓ Applied {fixed_issues} compatibility fixes")
|
|
988
|
+
|
|
989
|
+
# Final validation
|
|
990
|
+
final_report = asyncio.run(self.comprehensive_validation())
|
|
991
|
+
if final_report.is_valid and final_report.is_compatible:
|
|
992
|
+
messages.append("✅ Backward compatibility ensured")
|
|
993
|
+
return True, messages
|
|
994
|
+
else:
|
|
995
|
+
remaining_issues = final_report.total_issues
|
|
996
|
+
messages.append(f"⚠ {remaining_issues} issues remain")
|
|
997
|
+
messages.extend(
|
|
998
|
+
final_report.suggestions[:3]
|
|
999
|
+
) # Show top 3 suggestions
|
|
1000
|
+
return False, messages
|
|
1001
|
+
|
|
1002
|
+
except Exception as e:
|
|
1003
|
+
messages.append(f"✗ Failed to apply fixes: {str(e)}")
|
|
1004
|
+
return False, messages
|
|
1005
|
+
|
|
1006
|
+
except Exception as e:
|
|
1007
|
+
logger.error(f"Backward compatibility check failed: {e}")
|
|
1008
|
+
messages.append(f"✗ Compatibility check failed: {str(e)}")
|
|
1009
|
+
return False, messages
|
|
1010
|
+
|
|
1011
|
+
def _check_backup_availability(self) -> bool:
|
|
1012
|
+
"""Check if configuration backups are available."""
|
|
1013
|
+
try:
|
|
1014
|
+
storage_path = self.config_manager.get_storage_path()
|
|
1015
|
+
backup_dir = storage_path / "backups"
|
|
1016
|
+
|
|
1017
|
+
if backup_dir.exists():
|
|
1018
|
+
backup_files = list(backup_dir.glob("*.json"))
|
|
1019
|
+
return len(backup_files) > 0
|
|
1020
|
+
|
|
1021
|
+
return False
|
|
1022
|
+
except:
|
|
1023
|
+
return False
|
|
1024
|
+
|
|
1025
|
+
# =============================================================================
|
|
1026
|
+
# Simple Configuration Interface
|
|
1027
|
+
# =============================================================================
|
|
1028
|
+
|
|
1029
|
+
def _create_simple_sections(self) -> List[SimpleConfigSection]:
|
|
1030
|
+
"""Create simplified configuration sections."""
|
|
1031
|
+
sections = []
|
|
1032
|
+
|
|
1033
|
+
# Quick Setup Section
|
|
1034
|
+
quick_setup_options = [
|
|
1035
|
+
SimpleConfigOption(
|
|
1036
|
+
key="use_case",
|
|
1037
|
+
display_name="What will you primarily use Omnimancer for?",
|
|
1038
|
+
description="Choose your main use case to get optimized settings",
|
|
1039
|
+
option_type="choice",
|
|
1040
|
+
choices=[
|
|
1041
|
+
{
|
|
1042
|
+
"value": "coding",
|
|
1043
|
+
"label": "Coding & Development",
|
|
1044
|
+
"description": "Writing code, debugging, technical documentation",
|
|
1045
|
+
},
|
|
1046
|
+
{
|
|
1047
|
+
"value": "research",
|
|
1048
|
+
"label": "Research & Analysis",
|
|
1049
|
+
"description": "Research papers, data analysis, fact-checking",
|
|
1050
|
+
},
|
|
1051
|
+
{
|
|
1052
|
+
"value": "creative",
|
|
1053
|
+
"label": "Creative Writing",
|
|
1054
|
+
"description": "Stories, articles, creative content",
|
|
1055
|
+
},
|
|
1056
|
+
{
|
|
1057
|
+
"value": "general",
|
|
1058
|
+
"label": "General Use",
|
|
1059
|
+
"description": "Mixed tasks, general assistance",
|
|
1060
|
+
},
|
|
1061
|
+
{
|
|
1062
|
+
"value": "performance",
|
|
1063
|
+
"label": "Fast & Economical",
|
|
1064
|
+
"description": "Quick responses, minimal costs",
|
|
1065
|
+
},
|
|
1066
|
+
],
|
|
1067
|
+
default_value="general",
|
|
1068
|
+
category="Quick Setup",
|
|
1069
|
+
complexity=ConfigurationComplexity.BASIC,
|
|
1070
|
+
),
|
|
1071
|
+
SimpleConfigOption(
|
|
1072
|
+
key="experience_level",
|
|
1073
|
+
display_name="How familiar are you with AI tools?",
|
|
1074
|
+
description="This helps us show the right level of detail",
|
|
1075
|
+
option_type="choice",
|
|
1076
|
+
choices=[
|
|
1077
|
+
{
|
|
1078
|
+
"value": "beginner",
|
|
1079
|
+
"label": "Beginner",
|
|
1080
|
+
"description": "New to AI tools, prefer simple options",
|
|
1081
|
+
},
|
|
1082
|
+
{
|
|
1083
|
+
"value": "intermediate",
|
|
1084
|
+
"label": "Some Experience",
|
|
1085
|
+
"description": "Used AI tools before, comfortable with basic settings",
|
|
1086
|
+
},
|
|
1087
|
+
{
|
|
1088
|
+
"value": "advanced",
|
|
1089
|
+
"label": "Experienced",
|
|
1090
|
+
"description": "Familiar with AI, want more control",
|
|
1091
|
+
},
|
|
1092
|
+
{
|
|
1093
|
+
"value": "expert",
|
|
1094
|
+
"label": "Expert",
|
|
1095
|
+
"description": "Power user, need full customization",
|
|
1096
|
+
},
|
|
1097
|
+
],
|
|
1098
|
+
default_value="intermediate",
|
|
1099
|
+
category="Quick Setup",
|
|
1100
|
+
complexity=ConfigurationComplexity.BASIC,
|
|
1101
|
+
),
|
|
1102
|
+
]
|
|
1103
|
+
|
|
1104
|
+
sections.append(
|
|
1105
|
+
SimpleConfigSection(
|
|
1106
|
+
name="Quick Setup",
|
|
1107
|
+
description="Get started quickly with recommended settings",
|
|
1108
|
+
icon="⚡",
|
|
1109
|
+
options=quick_setup_options,
|
|
1110
|
+
required=True,
|
|
1111
|
+
expanded=True,
|
|
1112
|
+
)
|
|
1113
|
+
)
|
|
1114
|
+
|
|
1115
|
+
# AI Providers Section
|
|
1116
|
+
provider_options = [
|
|
1117
|
+
SimpleConfigOption(
|
|
1118
|
+
key="enable_claude",
|
|
1119
|
+
display_name="Anthropic Claude",
|
|
1120
|
+
description="Excellent reasoning and coding. Requires API key ($).",
|
|
1121
|
+
option_type="boolean",
|
|
1122
|
+
default_value=True,
|
|
1123
|
+
category="AI Providers",
|
|
1124
|
+
complexity=ConfigurationComplexity.BASIC,
|
|
1125
|
+
),
|
|
1126
|
+
SimpleConfigOption(
|
|
1127
|
+
key="claude_api_key",
|
|
1128
|
+
display_name="Claude API Key",
|
|
1129
|
+
description="Get from https://console.anthropic.com",
|
|
1130
|
+
option_type="password",
|
|
1131
|
+
required=False,
|
|
1132
|
+
category="AI Providers",
|
|
1133
|
+
complexity=ConfigurationComplexity.BASIC,
|
|
1134
|
+
),
|
|
1135
|
+
SimpleConfigOption(
|
|
1136
|
+
key="enable_openai",
|
|
1137
|
+
display_name="OpenAI GPT",
|
|
1138
|
+
description="Strong creative abilities and problem solving. Requires API key ($).",
|
|
1139
|
+
option_type="boolean",
|
|
1140
|
+
default_value=True,
|
|
1141
|
+
category="AI Providers",
|
|
1142
|
+
complexity=ConfigurationComplexity.BASIC,
|
|
1143
|
+
),
|
|
1144
|
+
SimpleConfigOption(
|
|
1145
|
+
key="openai_api_key",
|
|
1146
|
+
display_name="OpenAI API Key",
|
|
1147
|
+
description="Get from https://platform.openai.com",
|
|
1148
|
+
option_type="password",
|
|
1149
|
+
required=False,
|
|
1150
|
+
category="AI Providers",
|
|
1151
|
+
complexity=ConfigurationComplexity.BASIC,
|
|
1152
|
+
),
|
|
1153
|
+
]
|
|
1154
|
+
|
|
1155
|
+
sections.append(
|
|
1156
|
+
SimpleConfigSection(
|
|
1157
|
+
name="AI Providers",
|
|
1158
|
+
description="Choose which AI services to use",
|
|
1159
|
+
icon="🤖",
|
|
1160
|
+
options=provider_options,
|
|
1161
|
+
required=True,
|
|
1162
|
+
expanded=False,
|
|
1163
|
+
)
|
|
1164
|
+
)
|
|
1165
|
+
|
|
1166
|
+
return sections
|
|
1167
|
+
|
|
1168
|
+
def get_simple_mode_providers(self) -> List[SimplifiedProviderConfig]:
|
|
1169
|
+
"""Get simplified provider configurations for simple mode."""
|
|
1170
|
+
simplified_providers = []
|
|
1171
|
+
|
|
1172
|
+
provider_info = {
|
|
1173
|
+
"claude": {
|
|
1174
|
+
"display_name": "Anthropic Claude",
|
|
1175
|
+
"description": "Excellent for reasoning, coding, and analysis. Very capable and safe.",
|
|
1176
|
+
"use_case_fit": ["coding", "research", "creative", "general"],
|
|
1177
|
+
},
|
|
1178
|
+
"openai": {
|
|
1179
|
+
"display_name": "OpenAI GPT",
|
|
1180
|
+
"description": "Strong general capabilities, good for creative tasks and problem solving.",
|
|
1181
|
+
"use_case_fit": ["creative", "coding", "general"],
|
|
1182
|
+
},
|
|
1183
|
+
"gemini": {
|
|
1184
|
+
"display_name": "Google Gemini",
|
|
1185
|
+
"description": "Large context window, good for document analysis and multimodal tasks.",
|
|
1186
|
+
"use_case_fit": ["research", "general"],
|
|
1187
|
+
},
|
|
1188
|
+
"claude-code": {
|
|
1189
|
+
"display_name": "Claude Code (Free)",
|
|
1190
|
+
"description": "Free local access to Claude via Claude Code. No API costs.",
|
|
1191
|
+
"use_case_fit": ["coding", "general"],
|
|
1192
|
+
},
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
try:
|
|
1196
|
+
current_config = self.config_manager.get_config()
|
|
1197
|
+
current_providers = current_config.providers
|
|
1198
|
+
except:
|
|
1199
|
+
current_providers = {}
|
|
1200
|
+
|
|
1201
|
+
for provider_name, info in provider_info.items():
|
|
1202
|
+
enabled = provider_name in current_providers
|
|
1203
|
+
api_key = None
|
|
1204
|
+
model = None
|
|
1205
|
+
|
|
1206
|
+
if enabled and provider_name in current_providers:
|
|
1207
|
+
provider_config = current_providers[provider_name]
|
|
1208
|
+
api_key = "***" if provider_config.api_key else None
|
|
1209
|
+
model = provider_config.model
|
|
1210
|
+
|
|
1211
|
+
simplified_providers.append(
|
|
1212
|
+
SimplifiedProviderConfig(
|
|
1213
|
+
name=provider_name,
|
|
1214
|
+
display_name=info["display_name"],
|
|
1215
|
+
description=info["description"],
|
|
1216
|
+
enabled=enabled,
|
|
1217
|
+
api_key=api_key,
|
|
1218
|
+
model=model,
|
|
1219
|
+
use_case_fit=info["use_case_fit"],
|
|
1220
|
+
)
|
|
1221
|
+
)
|
|
1222
|
+
|
|
1223
|
+
return simplified_providers
|
|
1224
|
+
|
|
1225
|
+
def get_quick_setup_templates(self) -> List[Dict[str, Any]]:
|
|
1226
|
+
"""Get quick setup templates with simplified descriptions."""
|
|
1227
|
+
templates = []
|
|
1228
|
+
|
|
1229
|
+
# Get all templates and simplify them
|
|
1230
|
+
for (
|
|
1231
|
+
name,
|
|
1232
|
+
template,
|
|
1233
|
+
) in self.template_manager.get_all_templates().items():
|
|
1234
|
+
simplified = {
|
|
1235
|
+
"name": name,
|
|
1236
|
+
"display_name": template.name.title(),
|
|
1237
|
+
"description": template.description,
|
|
1238
|
+
"icon": self._get_template_icon(name),
|
|
1239
|
+
"estimated_setup_time": self._get_estimated_setup_time(template),
|
|
1240
|
+
"cost_estimate": self._get_cost_estimate(template),
|
|
1241
|
+
"complexity": (
|
|
1242
|
+
"Simple" if name in ["general", "performance"] else "Moderate"
|
|
1243
|
+
),
|
|
1244
|
+
"providers_needed": len(template.recommended_providers[:3]),
|
|
1245
|
+
"main_providers": template.recommended_providers[:2],
|
|
1246
|
+
"use_case": template.use_case,
|
|
1247
|
+
"recommended_for": self._get_recommended_for(template),
|
|
1248
|
+
}
|
|
1249
|
+
templates.append(simplified)
|
|
1250
|
+
|
|
1251
|
+
# Sort by complexity (simple first) and popularity
|
|
1252
|
+
templates.sort(
|
|
1253
|
+
key=lambda x: (
|
|
1254
|
+
0 if x["complexity"] == "Simple" else 1,
|
|
1255
|
+
-x["providers_needed"],
|
|
1256
|
+
)
|
|
1257
|
+
)
|
|
1258
|
+
|
|
1259
|
+
return templates
|
|
1260
|
+
|
|
1261
|
+
def _get_template_icon(self, template_name: str) -> str:
|
|
1262
|
+
"""Get an appropriate icon for a template."""
|
|
1263
|
+
icons = {
|
|
1264
|
+
"coding": "💻",
|
|
1265
|
+
"research": "🔬",
|
|
1266
|
+
"creative": "✏️",
|
|
1267
|
+
"general": "🌟",
|
|
1268
|
+
"performance": "⚡",
|
|
1269
|
+
}
|
|
1270
|
+
return icons.get(template_name, "🤖")
|
|
1271
|
+
|
|
1272
|
+
def _get_estimated_setup_time(self, template: ConfigTemplate) -> str:
|
|
1273
|
+
"""Estimate setup time for a template."""
|
|
1274
|
+
provider_count = len(template.recommended_providers[:3])
|
|
1275
|
+
|
|
1276
|
+
if provider_count <= 1:
|
|
1277
|
+
return "2-3 minutes"
|
|
1278
|
+
elif provider_count == 2:
|
|
1279
|
+
return "5-7 minutes"
|
|
1280
|
+
else:
|
|
1281
|
+
return "8-12 minutes"
|
|
1282
|
+
|
|
1283
|
+
def _get_cost_estimate(self, template: ConfigTemplate) -> str:
|
|
1284
|
+
"""Get cost estimate for a template."""
|
|
1285
|
+
providers = template.recommended_providers[:3]
|
|
1286
|
+
|
|
1287
|
+
# Check for free options
|
|
1288
|
+
free_providers = ["claude-code", "ollama"]
|
|
1289
|
+
has_free = any(p in free_providers for p in providers)
|
|
1290
|
+
|
|
1291
|
+
if has_free and len(providers) <= 2:
|
|
1292
|
+
return "Free option available"
|
|
1293
|
+
elif "performance" in template.name.lower():
|
|
1294
|
+
return "Low cost (~$5-15/month)"
|
|
1295
|
+
else:
|
|
1296
|
+
return "Moderate cost (~$15-40/month)"
|
|
1297
|
+
|
|
1298
|
+
def _get_recommended_for(self, template: ConfigTemplate) -> List[str]:
|
|
1299
|
+
"""Get who this template is recommended for."""
|
|
1300
|
+
recommendations = {
|
|
1301
|
+
"coding": [
|
|
1302
|
+
"Software developers",
|
|
1303
|
+
"Students learning programming",
|
|
1304
|
+
"Technical writers",
|
|
1305
|
+
],
|
|
1306
|
+
"research": ["Researchers", "Students", "Analysts", "Journalists"],
|
|
1307
|
+
"creative": [
|
|
1308
|
+
"Writers",
|
|
1309
|
+
"Content creators",
|
|
1310
|
+
"Marketing professionals",
|
|
1311
|
+
],
|
|
1312
|
+
"general": [
|
|
1313
|
+
"General users",
|
|
1314
|
+
"Business professionals",
|
|
1315
|
+
"Personal use",
|
|
1316
|
+
],
|
|
1317
|
+
"performance": [
|
|
1318
|
+
"Budget-conscious users",
|
|
1319
|
+
"High-volume usage",
|
|
1320
|
+
"Teams",
|
|
1321
|
+
],
|
|
1322
|
+
}
|
|
1323
|
+
return recommendations.get(template.use_case, ["General users"])
|
|
1324
|
+
|
|
1325
|
+
def get_setup_wizard_steps(self, use_case: str = "general") -> List[Dict[str, Any]]:
|
|
1326
|
+
"""Get step-by-step wizard steps for configuration."""
|
|
1327
|
+
steps = []
|
|
1328
|
+
|
|
1329
|
+
# Step 1: Welcome
|
|
1330
|
+
steps.append(
|
|
1331
|
+
{
|
|
1332
|
+
"step": 1,
|
|
1333
|
+
"title": "Welcome to Omnimancer",
|
|
1334
|
+
"description": "Let's set up Omnimancer for your needs",
|
|
1335
|
+
"type": "welcome",
|
|
1336
|
+
"content": {
|
|
1337
|
+
"message": "This quick setup will configure Omnimancer based on how you plan to use it.",
|
|
1338
|
+
"estimated_time": "5-10 minutes",
|
|
1339
|
+
"features": [
|
|
1340
|
+
"Choose your use case for optimized settings",
|
|
1341
|
+
"Select AI providers that fit your needs",
|
|
1342
|
+
"Set up API keys for your chosen services",
|
|
1343
|
+
"Enable helpful tools and features",
|
|
1344
|
+
],
|
|
1345
|
+
},
|
|
1346
|
+
}
|
|
1347
|
+
)
|
|
1348
|
+
|
|
1349
|
+
# Step 2: Use Case Selection
|
|
1350
|
+
steps.append(
|
|
1351
|
+
{
|
|
1352
|
+
"step": 2,
|
|
1353
|
+
"title": "Choose Your Primary Use Case",
|
|
1354
|
+
"description": "This helps us recommend the best settings for you",
|
|
1355
|
+
"type": "single_choice",
|
|
1356
|
+
"option": {
|
|
1357
|
+
"key": "use_case",
|
|
1358
|
+
"display_name": "What will you primarily use Omnimancer for?",
|
|
1359
|
+
"description": "Choose your main use case to get optimized settings",
|
|
1360
|
+
"option_type": "choice",
|
|
1361
|
+
"choices": [
|
|
1362
|
+
{
|
|
1363
|
+
"value": "coding",
|
|
1364
|
+
"label": "Coding & Development",
|
|
1365
|
+
"description": "Writing code, debugging, technical documentation",
|
|
1366
|
+
},
|
|
1367
|
+
{
|
|
1368
|
+
"value": "research",
|
|
1369
|
+
"label": "Research & Analysis",
|
|
1370
|
+
"description": "Research papers, data analysis, fact-checking",
|
|
1371
|
+
},
|
|
1372
|
+
{
|
|
1373
|
+
"value": "creative",
|
|
1374
|
+
"label": "Creative Writing",
|
|
1375
|
+
"description": "Stories, articles, creative content",
|
|
1376
|
+
},
|
|
1377
|
+
{
|
|
1378
|
+
"value": "general",
|
|
1379
|
+
"label": "General Use",
|
|
1380
|
+
"description": "Mixed tasks, general assistance",
|
|
1381
|
+
},
|
|
1382
|
+
{
|
|
1383
|
+
"value": "performance",
|
|
1384
|
+
"label": "Fast & Economical",
|
|
1385
|
+
"description": "Quick responses, minimal costs",
|
|
1386
|
+
},
|
|
1387
|
+
],
|
|
1388
|
+
"default_value": "general",
|
|
1389
|
+
},
|
|
1390
|
+
"help_text": "Don't worry - you can change this later or use Omnimancer for multiple purposes.",
|
|
1391
|
+
}
|
|
1392
|
+
)
|
|
1393
|
+
|
|
1394
|
+
# Step 3: Experience Level
|
|
1395
|
+
steps.append(
|
|
1396
|
+
{
|
|
1397
|
+
"step": 3,
|
|
1398
|
+
"title": "Your Experience Level",
|
|
1399
|
+
"description": "This helps us show the right amount of detail",
|
|
1400
|
+
"type": "single_choice",
|
|
1401
|
+
"option": {
|
|
1402
|
+
"key": "experience_level",
|
|
1403
|
+
"display_name": "How familiar are you with AI tools?",
|
|
1404
|
+
"description": "This helps us show the right level of detail",
|
|
1405
|
+
"option_type": "choice",
|
|
1406
|
+
"choices": [
|
|
1407
|
+
{
|
|
1408
|
+
"value": "beginner",
|
|
1409
|
+
"label": "Beginner",
|
|
1410
|
+
"description": "New to AI tools, prefer simple options",
|
|
1411
|
+
},
|
|
1412
|
+
{
|
|
1413
|
+
"value": "intermediate",
|
|
1414
|
+
"label": "Some Experience",
|
|
1415
|
+
"description": "Used AI tools before, comfortable with basic settings",
|
|
1416
|
+
},
|
|
1417
|
+
{
|
|
1418
|
+
"value": "advanced",
|
|
1419
|
+
"label": "Experienced",
|
|
1420
|
+
"description": "Familiar with AI, want more control",
|
|
1421
|
+
},
|
|
1422
|
+
{
|
|
1423
|
+
"value": "expert",
|
|
1424
|
+
"label": "Expert",
|
|
1425
|
+
"description": "Power user, need full customization",
|
|
1426
|
+
},
|
|
1427
|
+
],
|
|
1428
|
+
"default_value": "intermediate",
|
|
1429
|
+
},
|
|
1430
|
+
}
|
|
1431
|
+
)
|
|
1432
|
+
|
|
1433
|
+
# Step 4: AI Providers
|
|
1434
|
+
steps.append(
|
|
1435
|
+
{
|
|
1436
|
+
"step": 4,
|
|
1437
|
+
"title": "Choose AI Providers",
|
|
1438
|
+
"description": "Select which AI services you'd like to use",
|
|
1439
|
+
"type": "provider_selection",
|
|
1440
|
+
"content": {
|
|
1441
|
+
"message": "We recommend starting with 1-2 providers. You can add more later.",
|
|
1442
|
+
"providers": self._get_simplified_provider_options(use_case),
|
|
1443
|
+
},
|
|
1444
|
+
}
|
|
1445
|
+
)
|
|
1446
|
+
|
|
1447
|
+
# Step 5: API Keys
|
|
1448
|
+
steps.append(
|
|
1449
|
+
{
|
|
1450
|
+
"step": 5,
|
|
1451
|
+
"title": "Add API Keys",
|
|
1452
|
+
"description": "Enter API keys for your selected providers",
|
|
1453
|
+
"type": "api_keys",
|
|
1454
|
+
"content": {
|
|
1455
|
+
"message": "API keys are encrypted and stored securely on your device.",
|
|
1456
|
+
"help_link": "Need help getting API keys?",
|
|
1457
|
+
"providers_with_keys": [
|
|
1458
|
+
"claude",
|
|
1459
|
+
"openai",
|
|
1460
|
+
"gemini",
|
|
1461
|
+
"perplexity",
|
|
1462
|
+
],
|
|
1463
|
+
},
|
|
1464
|
+
}
|
|
1465
|
+
)
|
|
1466
|
+
|
|
1467
|
+
# Step 6: Tools and Features
|
|
1468
|
+
steps.append(
|
|
1469
|
+
{
|
|
1470
|
+
"step": 6,
|
|
1471
|
+
"title": "Enable Tools",
|
|
1472
|
+
"description": "Choose which capabilities to enable",
|
|
1473
|
+
"type": "feature_selection",
|
|
1474
|
+
"content": {
|
|
1475
|
+
"message": "These tools enhance Omnimancer's capabilities. All require your approval before use.",
|
|
1476
|
+
"features": [
|
|
1477
|
+
{
|
|
1478
|
+
"key": "web_search",
|
|
1479
|
+
"name": "Web Search",
|
|
1480
|
+
"description": "Search the internet for current information",
|
|
1481
|
+
"recommended": True,
|
|
1482
|
+
},
|
|
1483
|
+
{
|
|
1484
|
+
"key": "file_operations",
|
|
1485
|
+
"name": "File Operations",
|
|
1486
|
+
"description": "Read and write files (with your approval)",
|
|
1487
|
+
"recommended": True,
|
|
1488
|
+
},
|
|
1489
|
+
{
|
|
1490
|
+
"key": "code_tools",
|
|
1491
|
+
"name": "Coding Tools",
|
|
1492
|
+
"description": "Git, code analysis, and development tools",
|
|
1493
|
+
"recommended": use_case == "coding",
|
|
1494
|
+
},
|
|
1495
|
+
],
|
|
1496
|
+
},
|
|
1497
|
+
}
|
|
1498
|
+
)
|
|
1499
|
+
|
|
1500
|
+
# Step 7: Review and Finish
|
|
1501
|
+
steps.append(
|
|
1502
|
+
{
|
|
1503
|
+
"step": 7,
|
|
1504
|
+
"title": "Review Your Configuration",
|
|
1505
|
+
"description": "Confirm your settings and complete setup",
|
|
1506
|
+
"type": "review",
|
|
1507
|
+
"content": {
|
|
1508
|
+
"message": "Review your configuration below. You can change any of these settings later.",
|
|
1509
|
+
"final_steps": [
|
|
1510
|
+
"Save your configuration",
|
|
1511
|
+
"Test your first AI conversation",
|
|
1512
|
+
"Explore additional features",
|
|
1513
|
+
],
|
|
1514
|
+
},
|
|
1515
|
+
}
|
|
1516
|
+
)
|
|
1517
|
+
|
|
1518
|
+
return steps
|
|
1519
|
+
|
|
1520
|
+
def _get_simplified_provider_options(self, use_case: str) -> List[Dict[str, Any]]:
|
|
1521
|
+
"""Get simplified provider options based on use case."""
|
|
1522
|
+
all_providers = self.get_simple_mode_providers()
|
|
1523
|
+
|
|
1524
|
+
# Filter and rank providers based on use case
|
|
1525
|
+
relevant_providers = []
|
|
1526
|
+
for provider in all_providers:
|
|
1527
|
+
if use_case in provider.use_case_fit or "general" in provider.use_case_fit:
|
|
1528
|
+
# Calculate relevance score
|
|
1529
|
+
relevance = 1.0
|
|
1530
|
+
if use_case in provider.use_case_fit:
|
|
1531
|
+
relevance += 0.5
|
|
1532
|
+
if provider.name in ["claude", "openai"]: # Popular choices
|
|
1533
|
+
relevance += 0.3
|
|
1534
|
+
if provider.name in ["claude-code", "ollama"]: # Free options
|
|
1535
|
+
relevance += 0.2
|
|
1536
|
+
|
|
1537
|
+
relevant_providers.append(
|
|
1538
|
+
{
|
|
1539
|
+
"provider": asdict(provider),
|
|
1540
|
+
"relevance": relevance,
|
|
1541
|
+
"setup_difficulty": self._get_provider_setup_difficulty(
|
|
1542
|
+
provider.name
|
|
1543
|
+
),
|
|
1544
|
+
"recommended": relevance > 1.3,
|
|
1545
|
+
}
|
|
1546
|
+
)
|
|
1547
|
+
|
|
1548
|
+
# Sort by relevance
|
|
1549
|
+
relevant_providers.sort(key=lambda x: x["relevance"], reverse=True)
|
|
1550
|
+
|
|
1551
|
+
return relevant_providers[:5] # Return top 5 most relevant
|
|
1552
|
+
|
|
1553
|
+
def _get_provider_setup_difficulty(self, provider_name: str) -> str:
|
|
1554
|
+
"""Get setup difficulty level for a provider."""
|
|
1555
|
+
difficulty_map = {
|
|
1556
|
+
"claude-code": "Easy (No API key needed)",
|
|
1557
|
+
"ollama": "Moderate (Local installation required)",
|
|
1558
|
+
"claude": "Easy (API key required)",
|
|
1559
|
+
"openai": "Easy (API key required)",
|
|
1560
|
+
"gemini": "Easy (Free tier available)",
|
|
1561
|
+
"perplexity": "Moderate (Subscription required)",
|
|
1562
|
+
}
|
|
1563
|
+
return difficulty_map.get(provider_name, "Easy")
|
|
1564
|
+
|
|
1565
|
+
def get_provider_setup_instructions(self, provider_name: str) -> Dict[str, Any]:
|
|
1566
|
+
"""Get setup instructions for a specific provider."""
|
|
1567
|
+
instructions = {
|
|
1568
|
+
"claude": {
|
|
1569
|
+
"title": "Anthropic Claude Setup",
|
|
1570
|
+
"steps": [
|
|
1571
|
+
"Visit https://console.anthropic.com",
|
|
1572
|
+
"Sign up or log in to your account",
|
|
1573
|
+
"Go to the API Keys section",
|
|
1574
|
+
"Create a new API key",
|
|
1575
|
+
"Copy the key (starts with 'sk-ant-')",
|
|
1576
|
+
"Paste it in the API key field",
|
|
1577
|
+
],
|
|
1578
|
+
"api_key_format": "sk-ant-...",
|
|
1579
|
+
"cost_info": "Pay-per-use pricing, free tier available",
|
|
1580
|
+
"recommended_models": [
|
|
1581
|
+
"claude-3-5-sonnet-20241022",
|
|
1582
|
+
"claude-3-haiku-20240307",
|
|
1583
|
+
],
|
|
1584
|
+
},
|
|
1585
|
+
"openai": {
|
|
1586
|
+
"title": "OpenAI GPT Setup",
|
|
1587
|
+
"steps": [
|
|
1588
|
+
"Visit https://platform.openai.com",
|
|
1589
|
+
"Sign up or log in to your account",
|
|
1590
|
+
"Go to the API Keys section",
|
|
1591
|
+
"Create a new API key",
|
|
1592
|
+
"Copy the key (starts with 'sk-')",
|
|
1593
|
+
"Paste it in the API key field",
|
|
1594
|
+
],
|
|
1595
|
+
"api_key_format": "sk-...",
|
|
1596
|
+
"cost_info": "Pay-per-use pricing, free tier available for new users",
|
|
1597
|
+
"recommended_models": ["gpt-4o", "gpt-4o-mini"],
|
|
1598
|
+
},
|
|
1599
|
+
"gemini": {
|
|
1600
|
+
"title": "Google Gemini Setup",
|
|
1601
|
+
"steps": [
|
|
1602
|
+
"Visit https://makersuite.google.com/app/apikey",
|
|
1603
|
+
"Sign in with your Google account",
|
|
1604
|
+
"Create a new API key",
|
|
1605
|
+
"Copy the key (starts with 'AIza')",
|
|
1606
|
+
"Paste it in the API key field",
|
|
1607
|
+
],
|
|
1608
|
+
"api_key_format": "AIza...",
|
|
1609
|
+
"cost_info": "Free tier with generous limits, pay-per-use beyond limits",
|
|
1610
|
+
"recommended_models": ["gemini-1.5-pro", "gemini-1.5-flash"],
|
|
1611
|
+
},
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
return instructions.get(
|
|
1615
|
+
provider_name,
|
|
1616
|
+
{
|
|
1617
|
+
"title": f"{provider_name} Setup",
|
|
1618
|
+
"steps": ["Refer to provider documentation for setup instructions"],
|
|
1619
|
+
"api_key_format": "Check provider documentation",
|
|
1620
|
+
"cost_info": "Varies by provider",
|
|
1621
|
+
},
|
|
1622
|
+
)
|
|
1623
|
+
|
|
1624
|
+
def get_configuration_preview(self, answers: Dict[str, Any]) -> Dict[str, Any]:
|
|
1625
|
+
"""Generate a preview of what the configuration will look like."""
|
|
1626
|
+
preview = {
|
|
1627
|
+
"use_case": answers.get("use_case", "general"),
|
|
1628
|
+
"enabled_providers": [],
|
|
1629
|
+
"estimated_monthly_cost": "$0",
|
|
1630
|
+
"features_enabled": [],
|
|
1631
|
+
"setup_complete": False,
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
# Calculate enabled providers and costs
|
|
1635
|
+
cost_estimates = {
|
|
1636
|
+
"claude": 15,
|
|
1637
|
+
"openai": 15,
|
|
1638
|
+
"gemini": 5,
|
|
1639
|
+
"perplexity": 20,
|
|
1640
|
+
}
|
|
1641
|
+
total_cost = 0
|
|
1642
|
+
|
|
1643
|
+
if answers.get("enable_claude") and answers.get("claude_api_key"):
|
|
1644
|
+
preview["enabled_providers"].append("Anthropic Claude")
|
|
1645
|
+
total_cost += cost_estimates.get("claude", 0)
|
|
1646
|
+
|
|
1647
|
+
if answers.get("enable_openai") and answers.get("openai_api_key"):
|
|
1648
|
+
preview["enabled_providers"].append("OpenAI GPT")
|
|
1649
|
+
total_cost += cost_estimates.get("openai", 0)
|
|
1650
|
+
|
|
1651
|
+
if answers.get("enable_gemini") and answers.get("gemini_api_key"):
|
|
1652
|
+
preview["enabled_providers"].append("Google Gemini")
|
|
1653
|
+
total_cost += cost_estimates.get("gemini", 0)
|
|
1654
|
+
|
|
1655
|
+
if answers.get("enable_claude_code"):
|
|
1656
|
+
preview["enabled_providers"].append("Claude Code (Free)")
|
|
1657
|
+
|
|
1658
|
+
# Update cost estimate
|
|
1659
|
+
if total_cost == 0:
|
|
1660
|
+
preview["estimated_monthly_cost"] = "Free"
|
|
1661
|
+
elif total_cost <= 10:
|
|
1662
|
+
preview["estimated_monthly_cost"] = f"~${total_cost}/month"
|
|
1663
|
+
else:
|
|
1664
|
+
preview["estimated_monthly_cost"] = (
|
|
1665
|
+
f"~${total_cost}-{total_cost + 20}/month"
|
|
1666
|
+
)
|
|
1667
|
+
|
|
1668
|
+
# Features
|
|
1669
|
+
if answers.get("enable_web_search"):
|
|
1670
|
+
preview["features_enabled"].append("Web Search")
|
|
1671
|
+
if answers.get("enable_file_operations"):
|
|
1672
|
+
preview["features_enabled"].append("File Operations")
|
|
1673
|
+
if answers.get("enable_code_tools"):
|
|
1674
|
+
preview["features_enabled"].append("Coding Tools")
|
|
1675
|
+
|
|
1676
|
+
# Check if setup would be complete
|
|
1677
|
+
is_valid, errors = self.validate_simple_answers(answers)
|
|
1678
|
+
preview["setup_complete"] = is_valid
|
|
1679
|
+
preview["missing_items"] = errors
|
|
1680
|
+
|
|
1681
|
+
return preview
|
|
1682
|
+
|
|
1683
|
+
def get_recommended_configuration(
|
|
1684
|
+
self, context: Optional[ConfigurationContext] = None
|
|
1685
|
+
) -> ConfigurationRecommendation:
|
|
1686
|
+
"""Get configuration recommendation based on user context."""
|
|
1687
|
+
ctx = context or self.context
|
|
1688
|
+
|
|
1689
|
+
if ctx.use_case == "coding":
|
|
1690
|
+
return ConfigurationRecommendation(
|
|
1691
|
+
template_name="coding",
|
|
1692
|
+
confidence=0.9,
|
|
1693
|
+
reasons=[
|
|
1694
|
+
"Optimized for software development",
|
|
1695
|
+
"Includes code-capable models",
|
|
1696
|
+
"Has essential development tools",
|
|
1697
|
+
],
|
|
1698
|
+
providers=["claude", "openai", "claude-code"],
|
|
1699
|
+
estimated_cost="low",
|
|
1700
|
+
complexity=ConfigurationComplexity.BASIC,
|
|
1701
|
+
)
|
|
1702
|
+
elif ctx.use_case == "research":
|
|
1703
|
+
return ConfigurationRecommendation(
|
|
1704
|
+
template_name="research",
|
|
1705
|
+
confidence=0.9,
|
|
1706
|
+
reasons=[
|
|
1707
|
+
"Includes web search capabilities",
|
|
1708
|
+
"Large context models for document analysis",
|
|
1709
|
+
"Citation and fact-checking support",
|
|
1710
|
+
],
|
|
1711
|
+
providers=["perplexity", "claude", "gemini"],
|
|
1712
|
+
estimated_cost="medium",
|
|
1713
|
+
complexity=ConfigurationComplexity.BASIC,
|
|
1714
|
+
)
|
|
1715
|
+
else:
|
|
1716
|
+
return ConfigurationRecommendation(
|
|
1717
|
+
template_name="general",
|
|
1718
|
+
confidence=0.7,
|
|
1719
|
+
reasons=[
|
|
1720
|
+
"Balanced configuration for general use",
|
|
1721
|
+
"Good mix of capabilities",
|
|
1722
|
+
"Suitable for various tasks",
|
|
1723
|
+
],
|
|
1724
|
+
providers=["claude", "openai", "gemini"],
|
|
1725
|
+
estimated_cost="medium",
|
|
1726
|
+
complexity=ConfigurationComplexity.BASIC,
|
|
1727
|
+
)
|
|
1728
|
+
|
|
1729
|
+
def apply_template_configuration(
|
|
1730
|
+
self, template_name: str, api_keys: Dict[str, str] = None
|
|
1731
|
+
) -> Tuple[bool, List[str]]:
|
|
1732
|
+
"""Apply a template configuration in simple mode."""
|
|
1733
|
+
messages = []
|
|
1734
|
+
|
|
1735
|
+
try:
|
|
1736
|
+
template = self.template_manager.get_template(template_name)
|
|
1737
|
+
config = self.config_manager.get_config()
|
|
1738
|
+
|
|
1739
|
+
# Apply provider configurations
|
|
1740
|
+
for provider_name in template.recommended_providers[:3]:
|
|
1741
|
+
provider_config = (
|
|
1742
|
+
self.template_manager.create_provider_config_from_template(
|
|
1743
|
+
template, provider_name
|
|
1744
|
+
)
|
|
1745
|
+
)
|
|
1746
|
+
|
|
1747
|
+
if provider_config:
|
|
1748
|
+
if api_keys and provider_name in api_keys:
|
|
1749
|
+
provider_config.api_key = api_keys[provider_name]
|
|
1750
|
+
elif provider_name in ["claude-code", "ollama"]:
|
|
1751
|
+
provider_config.api_key = None
|
|
1752
|
+
|
|
1753
|
+
config.providers[provider_name] = provider_config
|
|
1754
|
+
messages.append(
|
|
1755
|
+
f"Configured {provider_name} with {provider_config.model}"
|
|
1756
|
+
)
|
|
1757
|
+
|
|
1758
|
+
if template.recommended_providers:
|
|
1759
|
+
config.default_provider = template.recommended_providers[0]
|
|
1760
|
+
messages.append(f"Set default provider to {config.default_provider}")
|
|
1761
|
+
|
|
1762
|
+
# Apply MCP configuration (simplified)
|
|
1763
|
+
mcp_config = self.template_manager.create_mcp_config_from_template(template)
|
|
1764
|
+
enabled_count = 0
|
|
1765
|
+
for server_name, server_config in mcp_config.servers.items():
|
|
1766
|
+
if enabled_count < 2:
|
|
1767
|
+
server_config.enabled = True
|
|
1768
|
+
enabled_count += 1
|
|
1769
|
+
else:
|
|
1770
|
+
server_config.enabled = False
|
|
1771
|
+
|
|
1772
|
+
config.mcp = mcp_config
|
|
1773
|
+
messages.append(f"Enabled {enabled_count} MCP tools")
|
|
1774
|
+
|
|
1775
|
+
self.config_manager.save_config(config)
|
|
1776
|
+
messages.append("Configuration saved successfully")
|
|
1777
|
+
|
|
1778
|
+
return True, messages
|
|
1779
|
+
|
|
1780
|
+
except Exception as e:
|
|
1781
|
+
logger.error(f"Failed to apply template configuration: {e}")
|
|
1782
|
+
return False, [f"Failed to apply template: {str(e)}"]
|
|
1783
|
+
|
|
1784
|
+
def validate_simple_answers(
|
|
1785
|
+
self, answers: Dict[str, Any]
|
|
1786
|
+
) -> Tuple[bool, List[str]]:
|
|
1787
|
+
"""Validate answers from the simple configuration interface."""
|
|
1788
|
+
errors = []
|
|
1789
|
+
|
|
1790
|
+
required_fields = ["use_case", "experience_level"]
|
|
1791
|
+
for field in required_fields:
|
|
1792
|
+
if field not in answers or not answers[field]:
|
|
1793
|
+
errors.append(f"{field.replace('_', ' ').title()} is required")
|
|
1794
|
+
|
|
1795
|
+
enabled_providers = []
|
|
1796
|
+
if answers.get("enable_claude") and answers.get("claude_api_key"):
|
|
1797
|
+
enabled_providers.append("claude")
|
|
1798
|
+
if answers.get("enable_openai") and answers.get("openai_api_key"):
|
|
1799
|
+
enabled_providers.append("openai")
|
|
1800
|
+
if answers.get("enable_claude_code"):
|
|
1801
|
+
enabled_providers.append("claude-code")
|
|
1802
|
+
|
|
1803
|
+
if not enabled_providers:
|
|
1804
|
+
errors.append("At least one AI provider must be enabled and configured")
|
|
1805
|
+
|
|
1806
|
+
# Validate API key formats
|
|
1807
|
+
if answers.get("claude_api_key"):
|
|
1808
|
+
key = answers["claude_api_key"]
|
|
1809
|
+
if not key.startswith("sk-ant-"):
|
|
1810
|
+
errors.append("Claude API key should start with 'sk-ant-'")
|
|
1811
|
+
|
|
1812
|
+
if answers.get("openai_api_key"):
|
|
1813
|
+
key = answers["openai_api_key"]
|
|
1814
|
+
if not key.startswith("sk-"):
|
|
1815
|
+
errors.append("OpenAI API key should start with 'sk-'")
|
|
1816
|
+
|
|
1817
|
+
return len(errors) == 0, errors
|
|
1818
|
+
|
|
1819
|
+
def generate_simple_configuration(
|
|
1820
|
+
self, answers: Dict[str, Any]
|
|
1821
|
+
) -> Tuple[bool, Dict[str, Any], List[str]]:
|
|
1822
|
+
"""Generate a complete configuration based on simple answers."""
|
|
1823
|
+
messages = []
|
|
1824
|
+
|
|
1825
|
+
try:
|
|
1826
|
+
use_case = answers.get("use_case", "general")
|
|
1827
|
+
experience_level = answers.get("experience_level", "intermediate")
|
|
1828
|
+
|
|
1829
|
+
context = ConfigurationContext(
|
|
1830
|
+
user_level=experience_level,
|
|
1831
|
+
use_case=use_case,
|
|
1832
|
+
preferred_mode=self.current_mode,
|
|
1833
|
+
)
|
|
1834
|
+
self.set_configuration_context(context)
|
|
1835
|
+
|
|
1836
|
+
recommendation = self.get_recommended_configuration(context)
|
|
1837
|
+
messages.append(
|
|
1838
|
+
f"Recommended template: {recommendation.template_name} (confidence: {recommendation.confidence:.0%})"
|
|
1839
|
+
)
|
|
1840
|
+
|
|
1841
|
+
api_keys = {}
|
|
1842
|
+
if answers.get("enable_claude") and answers.get("claude_api_key"):
|
|
1843
|
+
api_keys["claude"] = answers["claude_api_key"]
|
|
1844
|
+
messages.append("Claude API key provided")
|
|
1845
|
+
|
|
1846
|
+
if answers.get("enable_openai") and answers.get("openai_api_key"):
|
|
1847
|
+
api_keys["openai"] = answers["openai_api_key"]
|
|
1848
|
+
messages.append("OpenAI API key provided")
|
|
1849
|
+
|
|
1850
|
+
success, template_messages = self.apply_template_configuration(
|
|
1851
|
+
recommendation.template_name, api_keys
|
|
1852
|
+
)
|
|
1853
|
+
|
|
1854
|
+
if success:
|
|
1855
|
+
messages.extend(template_messages)
|
|
1856
|
+
config_dict = self.get_configuration_summary()
|
|
1857
|
+
|
|
1858
|
+
config_dict["user_preferences"] = {
|
|
1859
|
+
"use_case": use_case,
|
|
1860
|
+
"experience_level": experience_level,
|
|
1861
|
+
"tools_enabled": {
|
|
1862
|
+
"web_search": answers.get("enable_web_search", True),
|
|
1863
|
+
"file_operations": answers.get("enable_file_operations", True),
|
|
1864
|
+
"code_tools": answers.get("enable_code_tools", True),
|
|
1865
|
+
},
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
messages.append("Configuration generated successfully")
|
|
1869
|
+
return True, config_dict, messages
|
|
1870
|
+
else:
|
|
1871
|
+
return False, {}, messages
|
|
1872
|
+
|
|
1873
|
+
except Exception as e:
|
|
1874
|
+
logger.error(f"Failed to generate simple configuration: {e}")
|
|
1875
|
+
return False, {}, [f"Configuration generation failed: {str(e)}"]
|
|
1876
|
+
|
|
1877
|
+
def get_configuration_summary(self) -> Dict[str, Any]:
|
|
1878
|
+
"""Get a summary of the current configuration appropriate for the current mode."""
|
|
1879
|
+
try:
|
|
1880
|
+
config = self.config_manager.get_config()
|
|
1881
|
+
|
|
1882
|
+
summary = {
|
|
1883
|
+
"mode": self.current_mode.value,
|
|
1884
|
+
"context": asdict(self.context),
|
|
1885
|
+
"default_provider": config.default_provider,
|
|
1886
|
+
"enabled_providers": len(config.providers),
|
|
1887
|
+
"mcp_enabled": config.mcp.enabled,
|
|
1888
|
+
"enabled_mcp_servers": len(config.mcp.get_enabled_servers()),
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
if self.current_mode == ConfigurationMode.SIMPLE:
|
|
1892
|
+
summary["providers"] = []
|
|
1893
|
+
for name, provider_config in list(config.providers.items())[:3]:
|
|
1894
|
+
summary["providers"].append(
|
|
1895
|
+
{
|
|
1896
|
+
"name": name,
|
|
1897
|
+
"model": provider_config.model,
|
|
1898
|
+
"enabled": provider_config.enabled,
|
|
1899
|
+
}
|
|
1900
|
+
)
|
|
1901
|
+
|
|
1902
|
+
summary["tools"] = list(config.mcp.get_enabled_servers().keys())[:3]
|
|
1903
|
+
|
|
1904
|
+
elif self.current_mode == ConfigurationMode.ADVANCED:
|
|
1905
|
+
summary.update(self.config_manager.get_config_summary())
|
|
1906
|
+
|
|
1907
|
+
return summary
|
|
1908
|
+
|
|
1909
|
+
except Exception as e:
|
|
1910
|
+
logger.error(f"Failed to get configuration summary: {e}")
|
|
1911
|
+
return {"error": str(e)}
|
|
1912
|
+
|
|
1913
|
+
# =============================================================================
|
|
1914
|
+
# Utility Methods
|
|
1915
|
+
# =============================================================================
|
|
1916
|
+
|
|
1917
|
+
def _load_mode_preferences(self) -> None:
|
|
1918
|
+
"""Load mode preferences from storage."""
|
|
1919
|
+
try:
|
|
1920
|
+
storage_path = self.config_manager.get_storage_path()
|
|
1921
|
+
preferences_file = storage_path / "mode_preferences.json"
|
|
1922
|
+
|
|
1923
|
+
if preferences_file.exists():
|
|
1924
|
+
with open(preferences_file, "r") as f:
|
|
1925
|
+
data = json.load(f)
|
|
1926
|
+
|
|
1927
|
+
self.current_mode = ConfigurationMode(data.get("mode", "simple"))
|
|
1928
|
+
|
|
1929
|
+
context_data = data.get("context", {})
|
|
1930
|
+
self.context = ConfigurationContext(
|
|
1931
|
+
user_level=context_data.get("user_level", "beginner"),
|
|
1932
|
+
use_case=context_data.get("use_case", "general"),
|
|
1933
|
+
primary_providers=context_data.get("primary_providers", []),
|
|
1934
|
+
preferred_mode=ConfigurationMode(
|
|
1935
|
+
context_data.get("preferred_mode", "simple")
|
|
1936
|
+
),
|
|
1937
|
+
show_experimental=context_data.get("show_experimental", False),
|
|
1938
|
+
)
|
|
1939
|
+
|
|
1940
|
+
except Exception as e:
|
|
1941
|
+
logger.debug(f"Could not load mode preferences: {e}")
|
|
1942
|
+
|
|
1943
|
+
def _save_mode_preferences(self) -> None:
|
|
1944
|
+
"""Save mode preferences to storage."""
|
|
1945
|
+
try:
|
|
1946
|
+
storage_path = self.config_manager.get_storage_path()
|
|
1947
|
+
preferences_file = storage_path / "mode_preferences.json"
|
|
1948
|
+
|
|
1949
|
+
data = {
|
|
1950
|
+
"mode": self.current_mode.value,
|
|
1951
|
+
"context": asdict(self.context),
|
|
1952
|
+
"updated_at": datetime.now().isoformat(),
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1955
|
+
with open(preferences_file, "w") as f:
|
|
1956
|
+
json.dump(data, f, indent=2)
|
|
1957
|
+
|
|
1958
|
+
except Exception as e:
|
|
1959
|
+
logger.debug(f"Could not save mode preferences: {e}")
|
|
1960
|
+
|
|
1961
|
+
|
|
1962
|
+
def create_configuration_provider(
|
|
1963
|
+
config_manager: Optional[ConfigManager] = None,
|
|
1964
|
+
) -> ConfigurationProvider:
|
|
1965
|
+
"""
|
|
1966
|
+
Create a configuration provider with default settings.
|
|
1967
|
+
|
|
1968
|
+
Args:
|
|
1969
|
+
config_manager: Optional ConfigManager instance
|
|
1970
|
+
|
|
1971
|
+
Returns:
|
|
1972
|
+
Configured ConfigurationProvider instance
|
|
1973
|
+
"""
|
|
1974
|
+
return ConfigurationProvider(config_manager)
|