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,426 @@
1
+ """
2
+ Claude provider implementation for Omnimancer.
3
+
4
+ This module provides the Claude AI provider implementation using Anthropic's API.
5
+ """
6
+
7
+ from datetime import datetime
8
+ from typing import Dict, List
9
+
10
+ import certifi
11
+ import httpx
12
+
13
+ from ..core.models import ChatContext, ChatResponse, ModelInfo
14
+ from ..utils.errors import (
15
+ AuthenticationError,
16
+ ModelNotFoundError,
17
+ NetworkError,
18
+ ProviderError,
19
+ RateLimitError,
20
+ )
21
+ from .base import BaseProvider
22
+
23
+
24
+ class ClaudeProvider(BaseProvider):
25
+ """
26
+ Claude AI provider implementation using Anthropic's API.
27
+ """
28
+
29
+ BASE_URL = "https://api.anthropic.com/v1"
30
+
31
+ def __init__(self, api_key: str, model: str = "", **kwargs):
32
+ """
33
+ Initialize Claude provider.
34
+
35
+ Args:
36
+ api_key: Anthropic API key
37
+ model: Claude model to use (e.g., 'claude-sonnet-4-20250514', 'claude-3-5-sonnet-20241022')
38
+ **kwargs: Additional configuration
39
+ """
40
+ super().__init__(api_key, model or "claude-sonnet-4-20250514", **kwargs)
41
+ self.max_tokens = kwargs.get("max_tokens") or 4096
42
+ self.temperature = kwargs.get("temperature") or 0.7
43
+
44
+ async def send_message(self, message: str, context: ChatContext) -> ChatResponse:
45
+ """
46
+ Send a message to Claude API.
47
+
48
+ Args:
49
+ message: User message
50
+ context: Conversation context
51
+
52
+ Returns:
53
+ ChatResponse with Claude's reply
54
+ """
55
+ # Prepare messages for Claude API
56
+ messages = self._prepare_messages(message, context)
57
+
58
+ # Try with SSL verification first, then fall back if needed
59
+ for ssl_verify in [True, certifi.where(), False]:
60
+ try:
61
+ async with httpx.AsyncClient(verify=ssl_verify) as client:
62
+ response = await client.post(
63
+ f"{self.BASE_URL}/messages",
64
+ headers={
65
+ "Content-Type": "application/json",
66
+ "x-api-key": self.api_key,
67
+ "anthropic-version": "2023-06-01",
68
+ },
69
+ json={
70
+ "model": self.model,
71
+ "max_tokens": self.max_tokens,
72
+ "temperature": self.temperature,
73
+ "messages": messages,
74
+ },
75
+ timeout=30.0,
76
+ )
77
+
78
+ # If we get here, the request succeeded
79
+ return self._handle_response(response)
80
+
81
+ except httpx.ConnectError as e:
82
+ if "SSL" in str(e) or "certificate" in str(e):
83
+ # SSL error, try next verification method
84
+ continue
85
+ else:
86
+ # Non-SSL connection error, don't retry
87
+ raise NetworkError(f"Connection error: {e}")
88
+ except httpx.TimeoutException:
89
+ raise NetworkError("Request to Claude API timed out")
90
+ except httpx.RequestError as e:
91
+ if "SSL" not in str(e) and "certificate" not in str(e):
92
+ # Non-SSL request error, don't retry
93
+ raise NetworkError(f"Network error: {e}")
94
+ # SSL-related error, try next verification method
95
+ continue
96
+ except (
97
+ AuthenticationError,
98
+ RateLimitError,
99
+ ModelNotFoundError,
100
+ ProviderError,
101
+ ):
102
+ # Known provider errors, don't retry
103
+ raise
104
+ except Exception as e:
105
+ # Unknown error, don't retry
106
+ raise ProviderError(f"Unexpected error: {e}")
107
+
108
+ # If we get here, all SSL methods failed
109
+ raise NetworkError("Failed to establish SSL connection to Claude API")
110
+
111
+ async def validate_credentials(self) -> bool:
112
+ """
113
+ Validate Claude API credentials by making a test request.
114
+
115
+ Returns:
116
+ True if credentials are valid
117
+ """
118
+ # Try different SSL verification methods
119
+ for ssl_verify in [True, certifi.where(), False]:
120
+ try:
121
+ async with httpx.AsyncClient(verify=ssl_verify) as client:
122
+ response = await client.post(
123
+ f"{self.BASE_URL}/messages",
124
+ headers={
125
+ "Content-Type": "application/json",
126
+ "x-api-key": self.api_key,
127
+ "anthropic-version": "2023-06-01",
128
+ },
129
+ json={
130
+ "model": self.model,
131
+ "max_tokens": 10,
132
+ "messages": [{"role": "user", "content": "Hi"}],
133
+ },
134
+ timeout=10.0,
135
+ )
136
+
137
+ # If we get here, connection worked
138
+ # Check for specific status codes
139
+ if response.status_code == 200:
140
+ return True
141
+ elif response.status_code == 401:
142
+ # Authentication failed - invalid API key
143
+ return False
144
+ elif response.status_code == 400:
145
+ # Bad request - but API key might be valid, could be model issue
146
+ try:
147
+ error_data = response.json()
148
+ error_type = error_data.get("error", {}).get("type", "")
149
+ # If it's an authentication error, API key is invalid
150
+ if error_type == "authentication_error":
151
+ return False
152
+ # If it's other errors (like invalid model), API key is probably valid
153
+ return True
154
+ except:
155
+ # Can't parse error, assume API key is valid if we got a response
156
+ return True
157
+ else:
158
+ # Other status codes - if we got a response, API key is probably valid
159
+ return True
160
+
161
+ except httpx.ConnectError as e:
162
+ if "SSL" in str(e) or "certificate" in str(e):
163
+ # SSL error, try next verification method
164
+ continue
165
+ else:
166
+ # Non-SSL connection error
167
+ return False
168
+ except httpx.TimeoutException:
169
+ # Timeout might mean network issues, try next SSL method
170
+ continue
171
+ except httpx.RequestError as e:
172
+ if "SSL" not in str(e) and "certificate" not in str(e):
173
+ # Non-SSL request error
174
+ return False
175
+ # SSL-related error, try next verification method
176
+ continue
177
+ except Exception as e:
178
+ # Other error (timeout, etc.) - if it's not SSL related, stop trying
179
+ if "SSL" not in str(e) and "certificate" not in str(e):
180
+ return False
181
+ # SSL-related error, continue to next method
182
+ continue
183
+
184
+ # If we get here, all SSL methods failed
185
+ return False
186
+
187
+ def _prepare_messages(
188
+ self, message: str, context: ChatContext
189
+ ) -> List[Dict[str, str]]:
190
+ """
191
+ Prepare messages for Claude API format.
192
+
193
+ Args:
194
+ message: Current user message
195
+ context: Conversation context
196
+
197
+ Returns:
198
+ List of messages formatted for Claude API
199
+ """
200
+ messages = []
201
+
202
+ # Add context messages (excluding system messages for Claude)
203
+ for msg in context.messages:
204
+ if msg.role.value != "system":
205
+ messages.append({"role": msg.role.value, "content": msg.content})
206
+
207
+ # Add current message
208
+ messages.append({"role": "user", "content": message})
209
+
210
+ return messages
211
+
212
+ def _handle_response(self, response: httpx.Response) -> ChatResponse:
213
+ """
214
+ Handle Claude API response.
215
+
216
+ Args:
217
+ response: HTTP response from Claude API
218
+
219
+ Returns:
220
+ ChatResponse object
221
+
222
+ Raises:
223
+ Various provider errors based on response status
224
+ """
225
+ if response.status_code == 200:
226
+ data = response.json()
227
+ content = data.get("content", [])
228
+
229
+ if content and len(content) > 0:
230
+ text_content = content[0].get("text", "")
231
+ usage = data.get("usage", {})
232
+
233
+ return ChatResponse(
234
+ content=text_content,
235
+ model_used=self.model,
236
+ tokens_used=usage.get("output_tokens", 0),
237
+ timestamp=datetime.now(),
238
+ )
239
+ else:
240
+ raise ProviderError("Empty response from Claude API")
241
+
242
+ elif response.status_code == 401:
243
+ raise AuthenticationError("Invalid Claude API key")
244
+ elif response.status_code == 429:
245
+ raise RateLimitError("Claude API rate limit exceeded")
246
+ elif response.status_code == 404:
247
+ raise ModelNotFoundError(f"Claude model '{self.model}' not found")
248
+ else:
249
+ try:
250
+ error_data = response.json()
251
+ error_msg = error_data.get("error", {}).get("message", "Unknown error")
252
+ except:
253
+ error_msg = f"HTTP {response.status_code}"
254
+
255
+ raise ProviderError(f"Claude API error: {error_msg}")
256
+
257
+ def get_model_info(self) -> ModelInfo:
258
+ """
259
+ Get information about the current Claude model.
260
+ """
261
+ model_configs = {
262
+ "claude-sonnet-4-20250514": {
263
+ "description": "Claude Sonnet 4 - Latest and most capable model",
264
+ "max_tokens": 200000,
265
+ "cost_per_token": 0.000015,
266
+ },
267
+ "claude-opus-4-20250514": {
268
+ "description": "Claude Opus 4 - Most powerful model for complex tasks",
269
+ "max_tokens": 200000,
270
+ "cost_per_token": 0.000075,
271
+ },
272
+ "claude-3-5-sonnet-20241022": {
273
+ "description": "Claude 3.5 Sonnet - Enhanced reasoning and analysis",
274
+ "max_tokens": 200000,
275
+ "cost_per_token": 0.000015,
276
+ },
277
+ # Legacy models for backward compatibility
278
+ "claude-3-sonnet-20240229": {
279
+ "description": "Claude 3 Sonnet - Balanced performance and speed",
280
+ "max_tokens": 200000,
281
+ "cost_per_token": 0.000015,
282
+ },
283
+ "claude-3-haiku-20240307": {
284
+ "description": "Claude 3 Haiku - Fast and efficient",
285
+ "max_tokens": 200000,
286
+ "cost_per_token": 0.00000025,
287
+ },
288
+ "claude-3-opus-20240229": {
289
+ "description": "Claude 3 Opus - Most capable model",
290
+ "max_tokens": 200000,
291
+ "cost_per_token": 0.000075,
292
+ },
293
+ }
294
+
295
+ config = model_configs.get(
296
+ self.model,
297
+ {
298
+ "description": f"Claude model {self.model}",
299
+ "max_tokens": 200000,
300
+ "cost_per_token": 0.000015,
301
+ },
302
+ )
303
+
304
+ return ModelInfo(
305
+ name=self.model,
306
+ provider="claude",
307
+ description=config["description"],
308
+ max_tokens=config["max_tokens"],
309
+ cost_per_token=config["cost_per_token"],
310
+ available=True,
311
+ supports_tools=True,
312
+ supports_multimodal=True,
313
+ latest_version=self.model == "claude-sonnet-4-20250514",
314
+ )
315
+
316
+ def _get_static_models(self) -> List[ModelInfo]:
317
+ """
318
+ Get static list of available Claude models.
319
+ """
320
+ return [
321
+ ModelInfo(
322
+ name="claude-sonnet-4-20250514",
323
+ provider="claude",
324
+ description="Claude Sonnet 4 - Latest and most capable model",
325
+ max_tokens=200000,
326
+ cost_per_token=0.000015,
327
+ available=True,
328
+ supports_tools=True,
329
+ supports_multimodal=True,
330
+ latest_version=True,
331
+ ),
332
+ ModelInfo(
333
+ name="claude-opus-4-20250514",
334
+ provider="claude",
335
+ description="Claude Opus 4 - Most powerful model for complex tasks",
336
+ max_tokens=200000,
337
+ cost_per_token=0.000075,
338
+ available=True,
339
+ supports_tools=True,
340
+ supports_multimodal=True,
341
+ ),
342
+ ModelInfo(
343
+ name="claude-3-5-sonnet-20241022",
344
+ provider="claude",
345
+ description="Claude 3.5 Sonnet - Enhanced reasoning and analysis",
346
+ max_tokens=200000,
347
+ cost_per_token=0.000015,
348
+ available=True,
349
+ supports_tools=True,
350
+ supports_multimodal=True,
351
+ ),
352
+ ]
353
+
354
+ async def fetch_live_models(self) -> List[ModelInfo]:
355
+ """
356
+ Fetch live model list from Anthropic API.
357
+
358
+ Note: Anthropic doesn't have a public models list endpoint,
359
+ so we return an enhanced static list with current pricing.
360
+
361
+ Returns:
362
+ List of ModelInfo objects for Claude models
363
+ """
364
+ # Anthropic doesn't provide a models list endpoint
365
+ # Return the latest known models with updated information
366
+ return [
367
+ ModelInfo(
368
+ name="claude-3-5-sonnet-20241022",
369
+ provider="claude",
370
+ description="Most intelligent model with enhanced coding capabilities",
371
+ max_tokens=8192,
372
+ cost_per_token=0.000003, # $3 per million input tokens
373
+ available=True,
374
+ supports_tools=True,
375
+ supports_multimodal=True,
376
+ latest_version=True,
377
+ ),
378
+ ModelInfo(
379
+ name="claude-3-5-haiku-20241022",
380
+ provider="claude",
381
+ description="Fast model optimized for speed and efficiency",
382
+ max_tokens=8192,
383
+ cost_per_token=0.00000025, # $0.25 per million input tokens
384
+ available=True,
385
+ supports_tools=True,
386
+ supports_multimodal=True,
387
+ ),
388
+ ModelInfo(
389
+ name="claude-3-opus-20240229",
390
+ provider="claude",
391
+ description="Previous generation powerful model for complex tasks",
392
+ max_tokens=4096,
393
+ cost_per_token=0.000015, # $15 per million input tokens
394
+ available=True,
395
+ supports_tools=True,
396
+ supports_multimodal=True,
397
+ ),
398
+ ModelInfo(
399
+ name="claude-3-haiku-20240307",
400
+ provider="claude",
401
+ description="Previous generation fast and lightweight model",
402
+ max_tokens=4096,
403
+ cost_per_token=0.00000025, # $0.25 per million input tokens
404
+ available=True,
405
+ supports_tools=True,
406
+ supports_multimodal=True,
407
+ ),
408
+ ]
409
+
410
+ def supports_tools(self) -> bool:
411
+ """
412
+ Check if Claude provider supports tool calling.
413
+
414
+ Returns:
415
+ True - Claude supports tool calling
416
+ """
417
+ return True
418
+
419
+ def supports_multimodal(self) -> bool:
420
+ """
421
+ Check if Claude provider supports multimodal inputs.
422
+
423
+ Returns:
424
+ True - Claude supports images and other multimodal inputs
425
+ """
426
+ return True