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,554 @@
1
+ """
2
+ Mistral AI provider implementation for Omnimancer.
3
+
4
+ This module provides the Mistral AI provider implementation using Mistral's API
5
+ with support for safety settings and advanced parameters.
6
+ """
7
+
8
+ from datetime import datetime
9
+ from typing import Dict, List
10
+
11
+ import httpx
12
+
13
+ from ..core.models import (
14
+ ChatContext,
15
+ ChatResponse,
16
+ EnhancedModelInfo,
17
+ ToolCall,
18
+ ToolDefinition,
19
+ )
20
+ from ..utils.errors import (
21
+ AuthenticationError,
22
+ ModelNotFoundError,
23
+ NetworkError,
24
+ ProviderError,
25
+ RateLimitError,
26
+ )
27
+ from .base import BaseProvider
28
+
29
+
30
+ class MistralProvider(BaseProvider):
31
+ """
32
+ Mistral AI provider implementation using Mistral's API.
33
+
34
+ Supports Mistral models with safety settings, function calling,
35
+ and advanced conversation parameters.
36
+ """
37
+
38
+ BASE_URL = "https://api.mistral.ai/v1"
39
+
40
+ def __init__(self, api_key: str, model: str = "", **kwargs):
41
+ """
42
+ Initialize Mistral provider.
43
+
44
+ Args:
45
+ api_key: Mistral API key
46
+ model: Mistral model to use (e.g., 'mistral-large-latest', 'mistral-small-latest', 'open-mistral-7b')
47
+ **kwargs: Additional configuration including Mistral-specific settings
48
+ """
49
+ super().__init__(api_key, model or "mistral-large-latest", **kwargs)
50
+ self.max_tokens = kwargs.get("max_tokens", 4096)
51
+ self.temperature = kwargs.get("temperature", 0.7)
52
+ self.top_p = kwargs.get("top_p", 1.0)
53
+ self.safe_prompt = kwargs.get("safe_prompt", False)
54
+ self.random_seed = kwargs.get("random_seed", None)
55
+ self.response_format = kwargs.get("response_format", None) # For JSON mode
56
+
57
+ async def send_message(self, message: str, context: ChatContext) -> ChatResponse:
58
+ """
59
+ Send a message to Mistral API.
60
+
61
+ Args:
62
+ message: User message
63
+ context: Conversation context
64
+
65
+ Returns:
66
+ ChatResponse with Mistral's reply
67
+ """
68
+ try:
69
+ # Prepare messages for Mistral API
70
+ messages = self._prepare_messages(message, context)
71
+
72
+ # Build request payload
73
+ payload = {
74
+ "model": self.model,
75
+ "messages": messages,
76
+ "max_tokens": self.max_tokens,
77
+ "temperature": self.temperature,
78
+ "top_p": self.top_p,
79
+ "safe_prompt": self.safe_prompt,
80
+ "stream": False,
81
+ }
82
+
83
+ # Add optional parameters
84
+ if self.random_seed is not None:
85
+ payload["random_seed"] = self.random_seed
86
+
87
+ if self.response_format:
88
+ payload["response_format"] = self.response_format
89
+
90
+ # Make API request
91
+ async with httpx.AsyncClient() as client:
92
+ response = await client.post(
93
+ f"{self.BASE_URL}/chat/completions",
94
+ headers={
95
+ "Content-Type": "application/json",
96
+ "Authorization": f"Bearer {self.api_key}",
97
+ },
98
+ json=payload,
99
+ timeout=30.0,
100
+ )
101
+
102
+ # Handle response
103
+ return self._handle_response(response)
104
+
105
+ except httpx.TimeoutException:
106
+ raise NetworkError("Request to Mistral API timed out")
107
+ except httpx.RequestError as e:
108
+ raise NetworkError(f"Network error: {e}")
109
+ except (
110
+ AuthenticationError,
111
+ RateLimitError,
112
+ ModelNotFoundError,
113
+ ProviderError,
114
+ ):
115
+ # Re-raise provider-specific errors without wrapping
116
+ raise
117
+ except Exception as e:
118
+ raise ProviderError(f"Unexpected error: {e}")
119
+
120
+ async def send_message_with_tools(
121
+ self,
122
+ message: str,
123
+ context: ChatContext,
124
+ available_tools: List[ToolDefinition],
125
+ ) -> ChatResponse:
126
+ """
127
+ Send a message with available tools for Mistral to use.
128
+
129
+ Args:
130
+ message: User message
131
+ context: Conversation context
132
+ available_tools: List of tools available for Mistral to use
133
+
134
+ Returns:
135
+ ChatResponse with Mistral's reply and any tool calls
136
+ """
137
+ try:
138
+ # Prepare messages for Mistral API
139
+ messages = self._prepare_messages(message, context)
140
+
141
+ # Convert tools to Mistral format
142
+ tools = self._convert_tools_to_mistral_format(available_tools)
143
+
144
+ # Build request payload
145
+ payload = {
146
+ "model": self.model,
147
+ "messages": messages,
148
+ "max_tokens": self.max_tokens,
149
+ "temperature": self.temperature,
150
+ "top_p": self.top_p,
151
+ "safe_prompt": self.safe_prompt,
152
+ "tools": tools,
153
+ "tool_choice": "auto",
154
+ "stream": False,
155
+ }
156
+
157
+ # Add optional parameters
158
+ if self.random_seed is not None:
159
+ payload["random_seed"] = self.random_seed
160
+
161
+ # Make API request
162
+ async with httpx.AsyncClient() as client:
163
+ response = await client.post(
164
+ f"{self.BASE_URL}/chat/completions",
165
+ headers={
166
+ "Content-Type": "application/json",
167
+ "Authorization": f"Bearer {self.api_key}",
168
+ },
169
+ json=payload,
170
+ timeout=30.0,
171
+ )
172
+
173
+ # Handle response with tool calls
174
+ return self._handle_response_with_tools(response)
175
+
176
+ except httpx.TimeoutException:
177
+ raise NetworkError("Request to Mistral API timed out")
178
+ except httpx.RequestError as e:
179
+ raise NetworkError(f"Network error: {e}")
180
+ except Exception as e:
181
+ raise ProviderError(f"Unexpected error: {e}")
182
+
183
+ async def validate_credentials(self) -> bool:
184
+ """
185
+ Validate Mistral API credentials by making a test request.
186
+
187
+ Returns:
188
+ True if credentials are valid
189
+ """
190
+ try:
191
+ async with httpx.AsyncClient() as client:
192
+ response = await client.post(
193
+ f"{self.BASE_URL}/chat/completions",
194
+ headers={
195
+ "Content-Type": "application/json",
196
+ "Authorization": f"Bearer {self.api_key}",
197
+ },
198
+ json={
199
+ "model": self.model,
200
+ "messages": [{"role": "user", "content": "Hi"}],
201
+ "max_tokens": 10,
202
+ },
203
+ timeout=10.0,
204
+ )
205
+
206
+ return response.status_code == 200
207
+
208
+ except Exception:
209
+ return False
210
+
211
+ def _prepare_messages(
212
+ self, message: str, context: ChatContext
213
+ ) -> List[Dict[str, str]]:
214
+ """
215
+ Prepare messages for Mistral API format.
216
+
217
+ Args:
218
+ message: Current user message
219
+ context: Conversation context
220
+
221
+ Returns:
222
+ List of messages formatted for Mistral API
223
+ """
224
+ messages = []
225
+
226
+ # Add context messages
227
+ for msg in context.messages:
228
+ messages.append({"role": msg.role.value, "content": msg.content})
229
+
230
+ # Add current message
231
+ messages.append({"role": "user", "content": message})
232
+
233
+ return messages
234
+
235
+ def _convert_tools_to_mistral_format(
236
+ self, tools: List[ToolDefinition]
237
+ ) -> List[Dict]:
238
+ """
239
+ Convert tool definitions to Mistral API format.
240
+
241
+ Args:
242
+ tools: List of tool definitions
243
+
244
+ Returns:
245
+ List of tools formatted for Mistral API
246
+ """
247
+ mistral_tools = []
248
+
249
+ for tool in tools:
250
+ mistral_tool = {
251
+ "type": "function",
252
+ "function": {
253
+ "name": tool.name,
254
+ "description": tool.description,
255
+ "parameters": tool.parameters,
256
+ },
257
+ }
258
+ mistral_tools.append(mistral_tool)
259
+
260
+ return mistral_tools
261
+
262
+ def _handle_response(self, response: httpx.Response) -> ChatResponse:
263
+ """
264
+ Handle Mistral API response.
265
+
266
+ Args:
267
+ response: HTTP response from Mistral API
268
+
269
+ Returns:
270
+ ChatResponse object
271
+
272
+ Raises:
273
+ Various provider errors based on response status
274
+ """
275
+ if response.status_code == 200:
276
+ data = response.json()
277
+ choices = data.get("choices", [])
278
+
279
+ if choices and len(choices) > 0:
280
+ message = choices[0].get("message", {})
281
+ content = message.get("content", "")
282
+ usage = data.get("usage", {})
283
+
284
+ return ChatResponse(
285
+ content=content,
286
+ model_used=self.model,
287
+ tokens_used=usage.get("total_tokens", 0),
288
+ timestamp=datetime.now(),
289
+ )
290
+ else:
291
+ raise ProviderError("Empty response from Mistral API")
292
+
293
+ elif response.status_code == 401:
294
+ raise AuthenticationError("Invalid Mistral API key")
295
+ elif response.status_code == 429:
296
+ raise RateLimitError("Mistral API rate limit exceeded")
297
+ elif response.status_code == 404:
298
+ raise ModelNotFoundError(f"Mistral model '{self.model}' not found")
299
+ else:
300
+ try:
301
+ error_data = response.json()
302
+ error_msg = error_data.get("error", {}).get("message", "Unknown error")
303
+ except:
304
+ error_msg = f"HTTP {response.status_code}"
305
+
306
+ raise ProviderError(f"Mistral API error: {error_msg}")
307
+
308
+ def _handle_response_with_tools(self, response: httpx.Response) -> ChatResponse:
309
+ """
310
+ Handle Mistral API response with tool calls.
311
+
312
+ Args:
313
+ response: HTTP response from Mistral API
314
+
315
+ Returns:
316
+ ChatResponse object with tool calls
317
+ """
318
+ if response.status_code == 200:
319
+ data = response.json()
320
+ choices = data.get("choices", [])
321
+
322
+ if choices and len(choices) > 0:
323
+ message = choices[0].get("message", {})
324
+ content = message.get("content", "")
325
+ usage = data.get("usage", {})
326
+
327
+ # Extract tool calls if present
328
+ tool_calls = []
329
+ if "tool_calls" in message:
330
+ for tool_call in message["tool_calls"]:
331
+ if tool_call.get("type") == "function":
332
+ function = tool_call.get("function", {})
333
+ tool_calls.append(
334
+ ToolCall(
335
+ name=function.get("name", ""),
336
+ arguments=function.get("arguments", {}),
337
+ )
338
+ )
339
+
340
+ return ChatResponse(
341
+ content=content,
342
+ model_used=self.model,
343
+ tokens_used=usage.get("total_tokens", 0),
344
+ tool_calls=tool_calls if tool_calls else None,
345
+ timestamp=datetime.now(),
346
+ )
347
+ else:
348
+ raise ProviderError("Empty response from Mistral API")
349
+ else:
350
+ return self._handle_response(response)
351
+
352
+ def get_model_info(self) -> EnhancedModelInfo:
353
+ """
354
+ Get information about the current Mistral model.
355
+ """
356
+ model_configs = {
357
+ "mistral-large-latest": {
358
+ "description": "Mistral Large - Most capable model for complex tasks",
359
+ "max_tokens": 128000,
360
+ "cost_per_million_input": 2.0,
361
+ "cost_per_million_output": 6.0,
362
+ "swe_score": 64.6,
363
+ "supports_tools": True,
364
+ "supports_multimodal": False,
365
+ },
366
+ "mistral-medium-latest": {
367
+ "description": "Mistral Medium - Balanced performance and efficiency",
368
+ "max_tokens": 32000,
369
+ "cost_per_million_input": 2.7,
370
+ "cost_per_million_output": 8.1,
371
+ "swe_score": 55.8,
372
+ "supports_tools": True,
373
+ "supports_multimodal": False,
374
+ },
375
+ "mistral-small-latest": {
376
+ "description": "Mistral Small - Fast and cost-effective",
377
+ "max_tokens": 32000,
378
+ "cost_per_million_input": 0.2,
379
+ "cost_per_million_output": 0.6,
380
+ "swe_score": 48.2,
381
+ "supports_tools": True,
382
+ "supports_multimodal": False,
383
+ },
384
+ "mistral-tiny": {
385
+ "description": "Mistral Tiny - Ultra-fast for simple tasks",
386
+ "max_tokens": 32000,
387
+ "cost_per_million_input": 0.14,
388
+ "cost_per_million_output": 0.42,
389
+ "swe_score": 35.1,
390
+ "supports_tools": False,
391
+ "supports_multimodal": False,
392
+ },
393
+ "codestral-latest": {
394
+ "description": "Codestral - Specialized for code generation and analysis",
395
+ "max_tokens": 32000,
396
+ "cost_per_million_input": 0.2,
397
+ "cost_per_million_output": 0.6,
398
+ "swe_score": 78.2,
399
+ "supports_tools": True,
400
+ "supports_multimodal": False,
401
+ },
402
+ }
403
+
404
+ config = model_configs.get(
405
+ self.model,
406
+ {
407
+ "description": f"Mistral model {self.model}",
408
+ "max_tokens": 32000,
409
+ "cost_per_million_input": 2.0,
410
+ "cost_per_million_output": 6.0,
411
+ "swe_score": 50.0,
412
+ "supports_tools": True,
413
+ "supports_multimodal": False,
414
+ },
415
+ )
416
+
417
+ enhanced_info = EnhancedModelInfo(
418
+ name=self.model,
419
+ provider="mistral",
420
+ description=config["description"],
421
+ max_tokens=config["max_tokens"],
422
+ cost_per_million_input=config["cost_per_million_input"],
423
+ cost_per_million_output=config["cost_per_million_output"],
424
+ swe_score=config["swe_score"],
425
+ available=True,
426
+ supports_tools=config["supports_tools"],
427
+ supports_multimodal=config["supports_multimodal"],
428
+ latest_version=self.model == "mistral-large-latest",
429
+ context_window=config["max_tokens"],
430
+ is_free=False,
431
+ release_date=datetime(2024, 9, 1), # Approximate release date
432
+ )
433
+
434
+ # Update SWE rating based on score
435
+ enhanced_info.update_swe_rating()
436
+
437
+ return enhanced_info
438
+
439
+ def get_available_models(self) -> List[EnhancedModelInfo]:
440
+ """
441
+ Get list of available Mistral models.
442
+ """
443
+ models = [
444
+ EnhancedModelInfo(
445
+ name="mistral-large-latest",
446
+ provider="mistral",
447
+ description="Mistral Large - Most capable model for complex tasks",
448
+ max_tokens=128000,
449
+ cost_per_million_input=2.0,
450
+ cost_per_million_output=6.0,
451
+ swe_score=64.6,
452
+ available=True,
453
+ supports_tools=True,
454
+ supports_multimodal=False,
455
+ latest_version=True,
456
+ context_window=128000,
457
+ is_free=False,
458
+ release_date=datetime(2024, 9, 1),
459
+ ),
460
+ EnhancedModelInfo(
461
+ name="mistral-medium-latest",
462
+ provider="mistral",
463
+ description="Mistral Medium - Balanced performance and efficiency",
464
+ max_tokens=32000,
465
+ cost_per_million_input=2.7,
466
+ cost_per_million_output=8.1,
467
+ swe_score=55.8,
468
+ available=True,
469
+ supports_tools=True,
470
+ supports_multimodal=False,
471
+ context_window=32000,
472
+ is_free=False,
473
+ release_date=datetime(2024, 9, 1),
474
+ ),
475
+ EnhancedModelInfo(
476
+ name="mistral-small-latest",
477
+ provider="mistral",
478
+ description="Mistral Small - Fast and cost-effective",
479
+ max_tokens=32000,
480
+ cost_per_million_input=0.2,
481
+ cost_per_million_output=0.6,
482
+ swe_score=48.2,
483
+ available=True,
484
+ supports_tools=True,
485
+ supports_multimodal=False,
486
+ context_window=32000,
487
+ is_free=False,
488
+ release_date=datetime(2024, 9, 1),
489
+ ),
490
+ EnhancedModelInfo(
491
+ name="mistral-tiny",
492
+ provider="mistral",
493
+ description="Mistral Tiny - Ultra-fast for simple tasks",
494
+ max_tokens=32000,
495
+ cost_per_million_input=0.14,
496
+ cost_per_million_output=0.42,
497
+ swe_score=35.1,
498
+ available=True,
499
+ supports_tools=False,
500
+ supports_multimodal=False,
501
+ context_window=32000,
502
+ is_free=False,
503
+ release_date=datetime(2024, 9, 1),
504
+ ),
505
+ EnhancedModelInfo(
506
+ name="codestral-latest",
507
+ provider="mistral",
508
+ description="Codestral - Specialized for code generation and analysis",
509
+ max_tokens=32000,
510
+ cost_per_million_input=0.2,
511
+ cost_per_million_output=0.6,
512
+ swe_score=78.2,
513
+ available=True,
514
+ supports_tools=True,
515
+ supports_multimodal=False,
516
+ context_window=32000,
517
+ is_free=False,
518
+ release_date=datetime(2024, 9, 1),
519
+ ),
520
+ ]
521
+
522
+ # Update SWE ratings for all models
523
+ for model in models:
524
+ model.update_swe_rating()
525
+
526
+ return models
527
+
528
+ def supports_tools(self) -> bool:
529
+ """
530
+ Check if Mistral provider supports tool calling.
531
+
532
+ Returns:
533
+ True for most models except tiny variants
534
+ """
535
+ non_tool_models = ["mistral-tiny"]
536
+ return self.model not in non_tool_models
537
+
538
+ def supports_multimodal(self) -> bool:
539
+ """
540
+ Check if Mistral provider supports multimodal inputs.
541
+
542
+ Returns:
543
+ False - Current Mistral models don't support multimodal inputs
544
+ """
545
+ return False
546
+
547
+ def supports_streaming(self) -> bool:
548
+ """
549
+ Check if Mistral provider supports streaming responses.
550
+
551
+ Returns:
552
+ True - Mistral supports streaming
553
+ """
554
+ return True