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,412 @@
1
+ """
2
+ Cohere provider implementation for Omnimancer.
3
+
4
+ This module provides the Cohere AI provider implementation using Cohere's API.
5
+ """
6
+
7
+ from datetime import datetime
8
+ from typing import Dict, List
9
+
10
+ import httpx
11
+
12
+ from ..core.models import ChatContext, ChatResponse, ModelInfo
13
+ from ..utils.errors import (
14
+ AuthenticationError,
15
+ ModelNotFoundError,
16
+ NetworkError,
17
+ ProviderConfigurationError,
18
+ ProviderError,
19
+ ProviderUnavailableError,
20
+ QuotaExceededError,
21
+ RateLimitError,
22
+ )
23
+ from .base import BaseProvider
24
+
25
+
26
+ class CohereProvider(BaseProvider):
27
+ """
28
+ Cohere AI provider implementation using Cohere's API.
29
+ """
30
+
31
+ BASE_URL = "https://api.cohere.ai/v1"
32
+
33
+ def __init__(self, api_key: str, model: str = "", **kwargs):
34
+ """
35
+ Initialize Cohere provider.
36
+
37
+ Args:
38
+ api_key: Cohere API key
39
+ model: Cohere model to use (e.g., 'command-r', 'command-r-plus', 'command')
40
+ **kwargs: Additional configuration
41
+ """
42
+ super().__init__(api_key, model or "command-r", **kwargs)
43
+ self.max_tokens = kwargs.get("max_tokens", 4096)
44
+ self.temperature = kwargs.get("temperature", 0.7)
45
+
46
+ async def send_message(self, message: str, context: ChatContext) -> ChatResponse:
47
+ """
48
+ Send a message to Cohere API.
49
+
50
+ Args:
51
+ message: User message
52
+ context: Conversation context
53
+
54
+ Returns:
55
+ ChatResponse with Cohere's reply
56
+ """
57
+ try:
58
+ # Prepare chat history for Cohere API
59
+ chat_history = self._prepare_chat_history(context)
60
+
61
+ # Make API request
62
+ async with httpx.AsyncClient() as client:
63
+ response = await client.post(
64
+ f"{self.BASE_URL}/chat",
65
+ headers={
66
+ "Content-Type": "application/json",
67
+ "Authorization": f"Bearer {self.api_key}",
68
+ },
69
+ json={
70
+ "model": self.model,
71
+ "message": message,
72
+ "chat_history": chat_history,
73
+ "max_tokens": self.max_tokens,
74
+ "temperature": self.temperature,
75
+ },
76
+ timeout=30.0,
77
+ )
78
+
79
+ # Handle response
80
+ return self._handle_response(response)
81
+
82
+ except httpx.TimeoutException:
83
+ raise NetworkError("Request to Cohere API timed out")
84
+ except httpx.RequestError as e:
85
+ raise NetworkError(f"Network error: {e}")
86
+ except (
87
+ AuthenticationError,
88
+ RateLimitError,
89
+ ModelNotFoundError,
90
+ ProviderError,
91
+ ):
92
+ # Re-raise provider-specific errors without wrapping
93
+ raise
94
+ except Exception as e:
95
+ raise ProviderError(f"Unexpected error: {e}")
96
+
97
+ async def validate_credentials(self) -> bool:
98
+ """
99
+ Validate Cohere API credentials by making a test request.
100
+
101
+ Returns:
102
+ True if credentials are valid
103
+ """
104
+ try:
105
+ async with httpx.AsyncClient() as client:
106
+ response = await client.post(
107
+ f"{self.BASE_URL}/chat",
108
+ headers={
109
+ "Content-Type": "application/json",
110
+ "Authorization": f"Bearer {self.api_key}",
111
+ },
112
+ json={
113
+ "model": self.model,
114
+ "message": "Hi",
115
+ "max_tokens": 10,
116
+ },
117
+ timeout=10.0,
118
+ )
119
+
120
+ return response.status_code == 200
121
+
122
+ except Exception:
123
+ return False
124
+
125
+ def _prepare_chat_history(self, context: ChatContext) -> List[Dict[str, str]]:
126
+ """
127
+ Prepare chat history for Cohere API format.
128
+
129
+ Args:
130
+ context: Conversation context
131
+
132
+ Returns:
133
+ List of messages formatted for Cohere API
134
+ """
135
+ chat_history = []
136
+
137
+ # Convert context messages to Cohere format
138
+ # Cohere uses "USER" and "CHATBOT" roles, and excludes system messages
139
+ for msg in context.messages:
140
+ if msg.role.value == "user":
141
+ chat_history.append({"role": "USER", "message": msg.content})
142
+ elif msg.role.value == "assistant":
143
+ chat_history.append({"role": "CHATBOT", "message": msg.content})
144
+ # Skip system messages as Cohere doesn't support them in chat history
145
+
146
+ return chat_history
147
+
148
+ def _handle_response(self, response: httpx.Response) -> ChatResponse:
149
+ """
150
+ Handle Cohere API response with comprehensive error handling.
151
+
152
+ Args:
153
+ response: HTTP response from Cohere API
154
+
155
+ Returns:
156
+ ChatResponse object
157
+
158
+ Raises:
159
+ Various provider errors based on response status
160
+ """
161
+ if response.status_code == 200:
162
+ try:
163
+ data = response.json()
164
+ text = data.get("text", "")
165
+
166
+ if text:
167
+ # Extract token usage if available
168
+ meta = data.get("meta", {})
169
+ billed_units = meta.get("billed_units", {})
170
+ tokens_used = billed_units.get("output_tokens", 0)
171
+
172
+ return ChatResponse(
173
+ content=text,
174
+ model_used=self.model,
175
+ tokens_used=tokens_used,
176
+ timestamp=datetime.now(),
177
+ )
178
+ else:
179
+ raise ProviderError(
180
+ "Empty response from Cohere API", provider="cohere"
181
+ )
182
+ except (ValueError, KeyError) as e:
183
+ raise ProviderError(
184
+ f"Invalid JSON response from Cohere API: {e}",
185
+ provider="cohere",
186
+ )
187
+
188
+ elif response.status_code == 400:
189
+ try:
190
+ error_data = response.json()
191
+ error_msg = error_data.get("message", "Bad request")
192
+
193
+ if "model" in error_msg.lower() or "not found" in error_msg.lower():
194
+ available_models = [m.name for m in self.get_available_models()]
195
+ raise ModelNotFoundError(
196
+ f"Cohere model '{self.model}' not found or not accessible",
197
+ provider="cohere",
198
+ model_name=self.model,
199
+ available_models=available_models,
200
+ )
201
+ elif "token" in error_msg.lower() and "limit" in error_msg.lower():
202
+ raise ProviderError(
203
+ f"Token limit exceeded: {error_msg}. Try reducing message length.",
204
+ provider="cohere",
205
+ )
206
+ elif (
207
+ "invalid" in error_msg.lower() and "parameter" in error_msg.lower()
208
+ ):
209
+ raise ProviderConfigurationError(
210
+ f"Invalid parameter in request: {error_msg}",
211
+ provider="cohere",
212
+ suggested_fix="Check model parameters and configuration",
213
+ )
214
+ else:
215
+ raise ProviderError(
216
+ f"Cohere API error: {error_msg}", provider="cohere"
217
+ )
218
+ except (ValueError, KeyError):
219
+ raise ProviderError("Bad request to Cohere API", provider="cohere")
220
+
221
+ elif response.status_code == 401:
222
+ raise AuthenticationError(
223
+ "Invalid Cohere API key. Check your API key configuration.",
224
+ provider="cohere",
225
+ )
226
+
227
+ elif response.status_code == 403:
228
+ try:
229
+ error_data = response.json()
230
+ error_msg = error_data.get("message", "")
231
+ if "billing" in error_msg.lower() or "payment" in error_msg.lower():
232
+ raise QuotaExceededError(
233
+ "Cohere API access restricted due to billing issues",
234
+ provider="cohere",
235
+ )
236
+ else:
237
+ raise AuthenticationError(
238
+ "Cohere API access forbidden. Check API key permissions.",
239
+ provider="cohere",
240
+ )
241
+ except (ValueError, KeyError):
242
+ raise AuthenticationError(
243
+ "Cohere API access forbidden. Check API key permissions.",
244
+ provider="cohere",
245
+ )
246
+
247
+ elif response.status_code == 429:
248
+ # Extract rate limit information if available
249
+ retry_after = response.headers.get("Retry-After")
250
+ if retry_after:
251
+ try:
252
+ retry_seconds = int(retry_after)
253
+ raise RateLimitError(
254
+ "Cohere API rate limit exceeded",
255
+ provider="cohere",
256
+ retry_after=retry_seconds,
257
+ )
258
+ except ValueError:
259
+ pass
260
+
261
+ # Check if it's a quota issue
262
+ try:
263
+ error_data = response.json()
264
+ error_msg = error_data.get("message", "")
265
+ if "quota" in error_msg.lower() or "usage" in error_msg.lower():
266
+ raise QuotaExceededError(
267
+ "Cohere API usage quota exceeded", provider="cohere"
268
+ )
269
+ except (ValueError, KeyError):
270
+ pass
271
+
272
+ raise RateLimitError(
273
+ "Cohere API rate limit exceeded. Wait before retrying.",
274
+ provider="cohere",
275
+ )
276
+
277
+ elif response.status_code == 500:
278
+ raise ProviderUnavailableError(
279
+ "Cohere API server error. Try again later.",
280
+ provider="cohere",
281
+ estimated_recovery="a few minutes",
282
+ )
283
+
284
+ elif response.status_code == 502 or response.status_code == 503:
285
+ raise ProviderUnavailableError(
286
+ "Cohere API temporarily unavailable. Service may be under maintenance.",
287
+ provider="cohere",
288
+ estimated_recovery="10-30 minutes",
289
+ )
290
+
291
+ else:
292
+ try:
293
+ error_data = response.json()
294
+ error_msg = error_data.get("message", "Unknown error")
295
+ except:
296
+ error_msg = f"HTTP {response.status_code}"
297
+
298
+ raise ProviderError(f"Cohere API error: {error_msg}", provider="cohere")
299
+
300
+ def get_model_info(self) -> ModelInfo:
301
+ """
302
+ Get information about the current Cohere model.
303
+ """
304
+ model_configs = {
305
+ "command-r": {
306
+ "description": "Command R - Cohere's flagship conversational model",
307
+ "max_tokens": 128000,
308
+ "cost_per_token": 0.0000005,
309
+ },
310
+ "command-r-plus": {
311
+ "description": "Command R+ - Enhanced version with improved capabilities",
312
+ "max_tokens": 128000,
313
+ "cost_per_token": 0.000003,
314
+ },
315
+ "command-light": {
316
+ "description": "Command Light - Fast and efficient model for simple tasks",
317
+ "max_tokens": 4096,
318
+ "cost_per_token": 0.0000003,
319
+ },
320
+ "command": {
321
+ "description": "Command - Previous generation conversational model",
322
+ "max_tokens": 4096,
323
+ "cost_per_token": 0.000001,
324
+ },
325
+ }
326
+
327
+ config = model_configs.get(
328
+ self.model,
329
+ {
330
+ "description": f"Cohere model {self.model}",
331
+ "max_tokens": 4096,
332
+ "cost_per_token": 0.000001,
333
+ },
334
+ )
335
+
336
+ return ModelInfo(
337
+ name=self.model,
338
+ provider="cohere",
339
+ description=config["description"],
340
+ max_tokens=config["max_tokens"],
341
+ cost_per_token=config["cost_per_token"],
342
+ available=True,
343
+ supports_tools=False, # Cohere doesn't support tool calling in the same way
344
+ supports_multimodal=False, # Cohere is primarily text-based
345
+ latest_version=self.model == "command-r-plus",
346
+ )
347
+
348
+ def get_available_models(self) -> List[ModelInfo]:
349
+ """
350
+ Get list of available Cohere models.
351
+ """
352
+ return [
353
+ ModelInfo(
354
+ name="command-r",
355
+ provider="cohere",
356
+ description="Command R - Cohere's flagship conversational model",
357
+ max_tokens=128000,
358
+ cost_per_token=0.0000005,
359
+ available=True,
360
+ supports_tools=False,
361
+ supports_multimodal=False,
362
+ ),
363
+ ModelInfo(
364
+ name="command-r-plus",
365
+ provider="cohere",
366
+ description="Command R+ - Enhanced version with improved capabilities",
367
+ max_tokens=128000,
368
+ cost_per_token=0.000003,
369
+ available=True,
370
+ supports_tools=False,
371
+ supports_multimodal=False,
372
+ latest_version=True,
373
+ ),
374
+ ModelInfo(
375
+ name="command-light",
376
+ provider="cohere",
377
+ description="Command Light - Fast and efficient model for simple tasks",
378
+ max_tokens=4096,
379
+ cost_per_token=0.0000003,
380
+ available=True,
381
+ supports_tools=False,
382
+ supports_multimodal=False,
383
+ ),
384
+ ModelInfo(
385
+ name="command",
386
+ provider="cohere",
387
+ description="Command - Previous generation conversational model",
388
+ max_tokens=4096,
389
+ cost_per_token=0.000001,
390
+ available=True,
391
+ supports_tools=False,
392
+ supports_multimodal=False,
393
+ ),
394
+ ]
395
+
396
+ def supports_tools(self) -> bool:
397
+ """
398
+ Check if Cohere provider supports tool calling.
399
+
400
+ Returns:
401
+ False - Cohere doesn't support tool calling in the same way as OpenAI/Claude
402
+ """
403
+ return False
404
+
405
+ def supports_multimodal(self) -> bool:
406
+ """
407
+ Check if Cohere provider supports multimodal inputs.
408
+
409
+ Returns:
410
+ False - Cohere is primarily text-based
411
+ """
412
+ return False