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.
Files changed (126) hide show
  1. omnimancer/__init__.py +42 -0
  2. omnimancer/__main__.py +9 -0
  3. omnimancer/cli/__init__.py +8 -0
  4. omnimancer/cli/agent_manager.py +826 -0
  5. omnimancer/cli/agent_wizard.py +944 -0
  6. omnimancer/cli/approval_formatter.py +734 -0
  7. omnimancer/cli/approval_integration.py +734 -0
  8. omnimancer/cli/approval_prompt.py +1286 -0
  9. omnimancer/cli/batch_approval_display.py +617 -0
  10. omnimancer/cli/batch_approval_filters.py +729 -0
  11. omnimancer/cli/batch_operation_monitor.py +775 -0
  12. omnimancer/cli/commands/performance_cmd.py +624 -0
  13. omnimancer/cli/commands.py +757 -0
  14. omnimancer/cli/completion_handler.py +281 -0
  15. omnimancer/cli/config_setup_wizard.py +783 -0
  16. omnimancer/cli/dynamic_commands.py +371 -0
  17. omnimancer/cli/handlers/__init__.py +18 -0
  18. omnimancer/cli/handlers/agent_handler.py +865 -0
  19. omnimancer/cli/handlers/agent_persona_handler.py +1048 -0
  20. omnimancer/cli/handlers/custom_agent_handler.py +352 -0
  21. omnimancer/cli/handlers/permissions_handler.py +1609 -0
  22. omnimancer/cli/interface.py +6695 -0
  23. omnimancer/cli/performance_dashboard.py +612 -0
  24. omnimancer/core/__init__.py +34 -0
  25. omnimancer/core/agent/__init__.py +256 -0
  26. omnimancer/core/agent/agent_config.py +715 -0
  27. omnimancer/core/agent/agent_switcher.py +668 -0
  28. omnimancer/core/agent/approval_context.py +460 -0
  29. omnimancer/core/agent/approval_dialog.py +902 -0
  30. omnimancer/core/agent/approval_interface.py +628 -0
  31. omnimancer/core/agent/approval_manager.py +760 -0
  32. omnimancer/core/agent/cli_integration.py +470 -0
  33. omnimancer/core/agent/config.py +845 -0
  34. omnimancer/core/agent/custom_agent_integration.py +654 -0
  35. omnimancer/core/agent/diff_renderer.py +706 -0
  36. omnimancer/core/agent/file_content_display.py +503 -0
  37. omnimancer/core/agent/file_modification_workflow.py +658 -0
  38. omnimancer/core/agent/file_system_manager.py +1790 -0
  39. omnimancer/core/agent/input_handler.py +614 -0
  40. omnimancer/core/agent/metrics_collector.py +771 -0
  41. omnimancer/core/agent/optimization_engine.py +729 -0
  42. omnimancer/core/agent/performance_monitor.py +717 -0
  43. omnimancer/core/agent/persona.py +870 -0
  44. omnimancer/core/agent/persona_validator.py +672 -0
  45. omnimancer/core/agent/program_executor.py +748 -0
  46. omnimancer/core/agent/proposed_changes_integration.py +663 -0
  47. omnimancer/core/agent/read_before_write_errors.py +492 -0
  48. omnimancer/core/agent/read_before_write_ui.py +647 -0
  49. omnimancer/core/agent/rich_renderer.py +763 -0
  50. omnimancer/core/agent/status_config.py +238 -0
  51. omnimancer/core/agent/status_core.py +446 -0
  52. omnimancer/core/agent/status_integrated_engine.py +540 -0
  53. omnimancer/core/agent/status_manager.py +878 -0
  54. omnimancer/core/agent/status_ui_components.py +535 -0
  55. omnimancer/core/agent/template_applicator.py +548 -0
  56. omnimancer/core/agent/terminal_status_listener.py +394 -0
  57. omnimancer/core/agent/token_tracker.py +679 -0
  58. omnimancer/core/agent/types.py +60 -0
  59. omnimancer/core/agent/web_client.py +745 -0
  60. omnimancer/core/agent/workflow_orchestrator.py +826 -0
  61. omnimancer/core/agent_engine.py +1625 -0
  62. omnimancer/core/agent_mode_manager.py +706 -0
  63. omnimancer/core/agent_progress_ui.py +545 -0
  64. omnimancer/core/chat_manager.py +141 -0
  65. omnimancer/core/config_manager.py +2199 -0
  66. omnimancer/core/config_migration.py +772 -0
  67. omnimancer/core/config_provider.py +1974 -0
  68. omnimancer/core/config_security_validator.py +393 -0
  69. omnimancer/core/config_validator.py +768 -0
  70. omnimancer/core/conversation_manager.py +352 -0
  71. omnimancer/core/engine.py +787 -0
  72. omnimancer/core/enhanced_input.py +523 -0
  73. omnimancer/core/env_loader.py +83 -0
  74. omnimancer/core/fallback_manager.py +568 -0
  75. omnimancer/core/health_monitor.py +462 -0
  76. omnimancer/core/history_manager.py +462 -0
  77. omnimancer/core/mcp_integration_layer.py +878 -0
  78. omnimancer/core/models.py +1889 -0
  79. omnimancer/core/provider_capabilities.py +403 -0
  80. omnimancer/core/provider_initializer.py +450 -0
  81. omnimancer/core/provider_registry.py +432 -0
  82. omnimancer/core/security/__init__.py +16 -0
  83. omnimancer/core/security/approval_workflow.py +406 -0
  84. omnimancer/core/security/audit_logger.py +511 -0
  85. omnimancer/core/security/permission_controller.py +511 -0
  86. omnimancer/core/security/sandbox_manager.py +379 -0
  87. omnimancer/core/security/security_manager.py +530 -0
  88. omnimancer/core/setup_wizard.py +345 -0
  89. omnimancer/core/setup_wizard_core.py +449 -0
  90. omnimancer/core/setup_wizard_guide_generator.py +376 -0
  91. omnimancer/core/setup_wizard_provider_setup.py +454 -0
  92. omnimancer/core/setup_wizard_ui.py +317 -0
  93. omnimancer/core/setup_wizard_validation.py +371 -0
  94. omnimancer/core/signal_handler.py +174 -0
  95. omnimancer/mcp/__init__.py +11 -0
  96. omnimancer/mcp/client.py +725 -0
  97. omnimancer/mcp/manager.py +649 -0
  98. omnimancer/providers/__init__.py +24 -0
  99. omnimancer/providers/azure.py +608 -0
  100. omnimancer/providers/base.py +262 -0
  101. omnimancer/providers/bedrock.py +711 -0
  102. omnimancer/providers/claude.py +426 -0
  103. omnimancer/providers/claude_code.py +406 -0
  104. omnimancer/providers/cohere.py +412 -0
  105. omnimancer/providers/factory.py +988 -0
  106. omnimancer/providers/gemini.py +708 -0
  107. omnimancer/providers/mistral.py +554 -0
  108. omnimancer/providers/ollama.py +510 -0
  109. omnimancer/providers/openai.py +362 -0
  110. omnimancer/providers/openrouter.py +709 -0
  111. omnimancer/providers/perplexity.py +476 -0
  112. omnimancer/providers/vertex.py +617 -0
  113. omnimancer/providers/xai.py +484 -0
  114. omnimancer/ui/__init__.py +6 -0
  115. omnimancer/ui/cancellation_handler.py +235 -0
  116. omnimancer/ui/progress_indicator.py +318 -0
  117. omnimancer/utils/__init__.py +22 -0
  118. omnimancer/utils/error_handler.py +589 -0
  119. omnimancer/utils/errors.py +667 -0
  120. omnimancer/utils/retry.py +238 -0
  121. omnimancer_cli-0.1.0.dist-info/METADATA +413 -0
  122. omnimancer_cli-0.1.0.dist-info/RECORD +126 -0
  123. omnimancer_cli-0.1.0.dist-info/WHEEL +5 -0
  124. omnimancer_cli-0.1.0.dist-info/entry_points.txt +4 -0
  125. omnimancer_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
  126. omnimancer_cli-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,450 @@
1
+ """
2
+ Provider initialization optimization module.
3
+
4
+ This module provides optimized initialization for providers with
5
+ lazy loading and caching mechanisms to improve performance.
6
+ """
7
+
8
+ import importlib
9
+ import inspect
10
+ import logging
11
+ import threading
12
+ import time
13
+ from typing import Dict, List, Type
14
+
15
+ from ..providers.base import BaseProvider
16
+ from .models import EnhancedModelInfo, ProviderConfig
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ class ProviderInitializer:
22
+ """
23
+ Optimized provider initialization with lazy loading and caching.
24
+
25
+ This class handles lazy loading of provider classes and caching
26
+ of provider instances and model information to improve performance.
27
+ """
28
+
29
+ # Class-level caches
30
+ _provider_classes: Dict[str, Type[BaseProvider]] = {}
31
+ _provider_instances: Dict[str, BaseProvider] = {}
32
+ _model_cache: Dict[str, List[EnhancedModelInfo]] = {}
33
+ _cache_timestamps: Dict[str, float] = {}
34
+ _cache_ttl: float = 3600.0 # 1 hour cache TTL by default
35
+
36
+ # Locks for thread safety
37
+ _class_lock = threading.RLock()
38
+ _instance_lock = threading.RLock()
39
+ _model_lock = threading.RLock()
40
+
41
+ # Configuration
42
+ _lazy_loading_enabled: bool = True
43
+ _cache_enabled: bool = True
44
+
45
+ @classmethod
46
+ def get_provider_class(cls, provider_name: str) -> Type[BaseProvider]:
47
+ """
48
+ Get provider class with lazy loading.
49
+
50
+ Args:
51
+ provider_name: Name of the provider
52
+
53
+ Returns:
54
+ Provider class
55
+
56
+ Raises:
57
+ ImportError: If provider module cannot be imported
58
+ ValueError: If provider class cannot be found
59
+ """
60
+ with cls._class_lock:
61
+ # Check if class is already loaded
62
+ if provider_name in cls._provider_classes:
63
+ return cls._provider_classes[provider_name]
64
+
65
+ # Lazy load provider class
66
+ try:
67
+ # Map provider name to module path
68
+ module_path = cls._get_module_path(provider_name)
69
+
70
+ # Import module
71
+ module = importlib.import_module(module_path)
72
+
73
+ # Find provider class in module
74
+ provider_class = None
75
+ for name, obj in inspect.getmembers(module):
76
+ if (
77
+ inspect.isclass(obj)
78
+ and issubclass(obj, BaseProvider)
79
+ and obj != BaseProvider
80
+ and name.lower().endswith("provider")
81
+ ):
82
+ provider_class = obj
83
+ break
84
+
85
+ if not provider_class:
86
+ raise ValueError(f"Could not find provider class in {module_path}")
87
+
88
+ # Cache provider class
89
+ cls._provider_classes[provider_name] = provider_class
90
+ logger.debug(f"Lazy loaded provider class for {provider_name}")
91
+
92
+ return provider_class
93
+
94
+ except ImportError as e:
95
+ logger.error(
96
+ f"Failed to import provider module for {provider_name}: {e}"
97
+ )
98
+ raise
99
+ except Exception as e:
100
+ logger.error(f"Error loading provider class for {provider_name}: {e}")
101
+ raise ValueError(
102
+ f"Failed to load provider class for {provider_name}: {e}"
103
+ )
104
+
105
+ @classmethod
106
+ def _get_module_path(cls, provider_name: str) -> str:
107
+ """
108
+ Map provider name to module path.
109
+
110
+ Args:
111
+ provider_name: Name of the provider
112
+
113
+ Returns:
114
+ Module path
115
+ """
116
+ # Special case for claude-code provider
117
+ if provider_name == "claude-code":
118
+ return "omnimancer.providers.claude_code"
119
+
120
+ # Standard mapping
121
+ return f"omnimancer.providers.{provider_name}"
122
+
123
+ @classmethod
124
+ def get_provider_instance(
125
+ cls,
126
+ provider_name: str,
127
+ config: ProviderConfig,
128
+ config_manager: "ConfigManager" = None,
129
+ ) -> BaseProvider:
130
+ """
131
+ Get or create cached provider instance.
132
+
133
+ Args:
134
+ provider_name: Name of the provider
135
+ config: Provider configuration
136
+ config_manager: Optional config manager for API key decryption
137
+
138
+ Returns:
139
+ Provider instance
140
+ """
141
+ if not cls._cache_enabled:
142
+ # If caching is disabled, create new instance every time
143
+ return cls._create_provider_instance(provider_name, config, config_manager)
144
+
145
+ # Generate cache key based on provider name and config
146
+ cache_key = cls._generate_cache_key(provider_name, config)
147
+
148
+ with cls._instance_lock:
149
+ # Check if instance is already cached and valid
150
+ if cache_key in cls._provider_instances and cls._is_cache_valid(cache_key):
151
+ logger.debug(f"Using cached provider instance for {provider_name}")
152
+ return cls._provider_instances[cache_key]
153
+
154
+ # Create new instance
155
+ instance = cls._create_provider_instance(
156
+ provider_name, config, config_manager
157
+ )
158
+
159
+ # Cache instance
160
+ cls._provider_instances[cache_key] = instance
161
+ cls._cache_timestamps[cache_key] = time.time()
162
+
163
+ return instance
164
+
165
+ @classmethod
166
+ def _create_provider_instance(
167
+ cls,
168
+ provider_name: str,
169
+ config: ProviderConfig,
170
+ config_manager: "ConfigManager" = None,
171
+ ) -> BaseProvider:
172
+ """
173
+ Create a new provider instance.
174
+
175
+ Args:
176
+ provider_name: Name of the provider
177
+ config: Provider configuration
178
+ config_manager: Optional config manager for API key decryption
179
+
180
+ Returns:
181
+ Provider instance
182
+ """
183
+ # Get provider class
184
+ provider_class = cls.get_provider_class(provider_name)
185
+
186
+ # Get decrypted API key
187
+ api_key = config.api_key
188
+ if config_manager and api_key:
189
+ try:
190
+ # Use config manager to get properly decrypted API key
191
+ decrypted_key = config_manager.get_api_key(provider_name)
192
+ if decrypted_key:
193
+ api_key = decrypted_key
194
+ except Exception as e:
195
+ logger.warning(f"Failed to decrypt API key for {provider_name}: {e}")
196
+ # Fall back to using the key as-is
197
+ pass
198
+
199
+ # If still no valid API key, try environment variables
200
+ if not api_key or api_key.startswith("your-") or api_key.startswith("sk-your"):
201
+ from .env_loader import load_api_key_from_env
202
+
203
+ env_key = load_api_key_from_env(provider_name)
204
+ if env_key:
205
+ api_key = env_key
206
+ logger.info(f"Using API key from environment for {provider_name}")
207
+
208
+ # Create instance
209
+ # Filter out None values to allow provider defaults to work
210
+ kwargs = {
211
+ k: v
212
+ for k, v in config.model_dump(exclude={"api_key", "model"}).items()
213
+ if v is not None
214
+ }
215
+ instance = provider_class(api_key=api_key, model=config.model, **kwargs)
216
+
217
+ return instance
218
+
219
+ @classmethod
220
+ def _generate_cache_key(cls, provider_name: str, config: ProviderConfig) -> str:
221
+ """
222
+ Generate cache key for provider instance.
223
+
224
+ Args:
225
+ provider_name: Name of the provider
226
+ config: Provider configuration
227
+
228
+ Returns:
229
+ Cache key
230
+ """
231
+ # Include key configuration parameters in cache key
232
+ key_params = {
233
+ "model": config.model,
234
+ "base_url": getattr(config, "base_url", None),
235
+ "organization": getattr(config, "organization", None),
236
+ "project_id": getattr(config, "project_id", None),
237
+ "azure_endpoint": getattr(config, "azure_endpoint", None),
238
+ "azure_deployment": getattr(config, "azure_deployment", None),
239
+ }
240
+
241
+ # Filter out None values
242
+ key_params = {k: v for k, v in key_params.items() if v is not None}
243
+
244
+ # Generate key
245
+ key_str = f"{provider_name}:{config.model}:{hash(str(key_params))}"
246
+ return key_str
247
+
248
+ @classmethod
249
+ def _is_cache_valid(cls, cache_key: str) -> bool:
250
+ """
251
+ Check if cached data is still valid.
252
+
253
+ Args:
254
+ cache_key: Cache key
255
+
256
+ Returns:
257
+ True if cache is valid
258
+ """
259
+ if cache_key not in cls._cache_timestamps:
260
+ return False
261
+
262
+ return (time.time() - cls._cache_timestamps[cache_key]) < cls._cache_ttl
263
+
264
+ @classmethod
265
+ def get_model_info(
266
+ cls, provider_name: str, enhanced: bool = True
267
+ ) -> List[EnhancedModelInfo]:
268
+ """
269
+ Get cached model information for a provider.
270
+
271
+ Args:
272
+ provider_name: Name of the provider
273
+ enhanced: Whether to return enhanced model info
274
+
275
+ Returns:
276
+ List of model info objects
277
+ """
278
+ if not cls._cache_enabled:
279
+ # If caching is disabled, fetch models every time
280
+ return cls._fetch_model_info(provider_name, enhanced)
281
+
282
+ with cls._model_lock:
283
+ cache_key = f"{provider_name}:{'enhanced' if enhanced else 'basic'}"
284
+
285
+ # Check if models are already cached and valid
286
+ if cache_key in cls._model_cache and cls._is_cache_valid(cache_key):
287
+ logger.debug(f"Using cached model info for {provider_name}")
288
+ return cls._model_cache[cache_key]
289
+
290
+ # Fetch models
291
+ models = cls._fetch_model_info(provider_name, enhanced)
292
+
293
+ # Cache models
294
+ cls._model_cache[cache_key] = models
295
+ cls._cache_timestamps[cache_key] = time.time()
296
+
297
+ return models
298
+
299
+ @classmethod
300
+ def _fetch_model_info(
301
+ cls, provider_name: str, enhanced: bool
302
+ ) -> List[EnhancedModelInfo]:
303
+ """
304
+ Fetch model information from provider.
305
+
306
+ Args:
307
+ provider_name: Name of the provider
308
+ enhanced: Whether to return enhanced model info
309
+
310
+ Returns:
311
+ List of model info objects
312
+ """
313
+ try:
314
+ # Get provider class
315
+ provider_class = cls.get_provider_class(provider_name)
316
+
317
+ # Create temporary instance with dummy credentials
318
+ temp_provider = provider_class(api_key="dummy", model="dummy")
319
+
320
+ # Get models
321
+ models = temp_provider.get_available_models()
322
+
323
+ # Convert to EnhancedModelInfo if needed
324
+ from .models import ModelInfo
325
+
326
+ if enhanced and models and isinstance(models[0], ModelInfo):
327
+ models = [EnhancedModelInfo.from_model_info(model) for model in models]
328
+
329
+ return models
330
+
331
+ except Exception as e:
332
+ logger.error(f"Error fetching model info for {provider_name}: {e}")
333
+ return []
334
+
335
+ @classmethod
336
+ def clear_caches(cls) -> None:
337
+ """Clear all caches."""
338
+ with cls._class_lock:
339
+ cls._provider_classes.clear()
340
+
341
+ with cls._instance_lock:
342
+ cls._provider_instances.clear()
343
+
344
+ with cls._model_lock:
345
+ cls._model_cache.clear()
346
+
347
+ cls._cache_timestamps.clear()
348
+ logger.info("Cleared all provider caches")
349
+
350
+ @classmethod
351
+ def set_cache_ttl(cls, ttl_seconds: float) -> None:
352
+ """
353
+ Set cache TTL (time to live).
354
+
355
+ Args:
356
+ ttl_seconds: TTL in seconds
357
+ """
358
+ cls._cache_ttl = ttl_seconds
359
+ logger.info(f"Set provider cache TTL to {ttl_seconds} seconds")
360
+
361
+ @classmethod
362
+ def enable_lazy_loading(cls, enabled: bool = True) -> None:
363
+ """
364
+ Enable or disable lazy loading.
365
+
366
+ Args:
367
+ enabled: Whether to enable lazy loading
368
+ """
369
+ cls._lazy_loading_enabled = enabled
370
+ logger.info(f"{'Enabled' if enabled else 'Disabled'} provider lazy loading")
371
+
372
+ @classmethod
373
+ def enable_caching(cls, enabled: bool = True) -> None:
374
+ """
375
+ Enable or disable caching.
376
+
377
+ Args:
378
+ enabled: Whether to enable caching
379
+ """
380
+ cls._cache_enabled = enabled
381
+ if not enabled:
382
+ cls.clear_caches()
383
+ logger.info(f"{'Enabled' if enabled else 'Disabled'} provider caching")
384
+
385
+ @classmethod
386
+ def preload_provider_classes(cls, provider_names: List[str]) -> None:
387
+ """
388
+ Preload provider classes.
389
+
390
+ Args:
391
+ provider_names: List of provider names to preload
392
+ """
393
+ for provider_name in provider_names:
394
+ try:
395
+ cls.get_provider_class(provider_name)
396
+ logger.debug(f"Preloaded provider class for {provider_name}")
397
+ except Exception as e:
398
+ logger.warning(
399
+ f"Failed to preload provider class for {provider_name}: {e}"
400
+ )
401
+
402
+ @classmethod
403
+ async def initialize_providers(
404
+ cls,
405
+ provider_configs: Dict[str, ProviderConfig],
406
+ config_manager: "ConfigManager" = None,
407
+ ) -> Dict[str, BaseProvider]:
408
+ """
409
+ Initialize all providers from configuration.
410
+
411
+ Args:
412
+ provider_configs: Dictionary of provider configurations
413
+ config_manager: Optional config manager for API key decryption
414
+
415
+ Returns:
416
+ Dictionary of initialized provider instances
417
+ """
418
+ from ..providers.factory import ProviderFactory
419
+
420
+ providers = {}
421
+
422
+ for provider_name, config in provider_configs.items():
423
+ try:
424
+ # Use ProviderFactory instead of direct class loading
425
+ provider = ProviderFactory.create_provider(
426
+ provider_name, config, config_manager
427
+ )
428
+ providers[provider_name] = provider
429
+ logger.info(f"Initialized provider: {provider_name}")
430
+ except Exception as e:
431
+ logger.error(f"Failed to initialize provider {provider_name}: {e}")
432
+ # Continue with other providers instead of failing completely
433
+ continue
434
+
435
+ return providers
436
+
437
+ @classmethod
438
+ def preload_model_info(cls, provider_names: List[str]) -> None:
439
+ """
440
+ Preload model information.
441
+
442
+ Args:
443
+ provider_names: List of provider names to preload
444
+ """
445
+ for provider_name in provider_names:
446
+ try:
447
+ cls.get_model_info(provider_name)
448
+ logger.debug(f"Preloaded model info for {provider_name}")
449
+ except Exception as e:
450
+ logger.warning(f"Failed to preload model info for {provider_name}: {e}")