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,608 @@
1
+ """
2
+ Azure OpenAI provider implementation for Omnimancer.
3
+
4
+ This module provides the Azure OpenAI provider implementation using Azure's OpenAI Service
5
+ with support for deployment names, API versioning, and Azure-specific authentication.
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 AzureProvider(BaseProvider):
31
+ """
32
+ Azure OpenAI provider implementation using Azure OpenAI Service.
33
+
34
+ Supports Azure-specific endpoint configuration, deployment names,
35
+ API versioning, and Azure authentication methods.
36
+ """
37
+
38
+ def __init__(self, api_key: str, model: str = "", **kwargs):
39
+ """
40
+ Initialize Azure OpenAI provider.
41
+
42
+ Args:
43
+ api_key: Azure OpenAI API key
44
+ model: Model deployment name in Azure (e.g., 'gpt-4', 'gpt-35-turbo', custom deployment name)
45
+ **kwargs: Additional configuration including Azure-specific settings
46
+ """
47
+ super().__init__(api_key, model or "gpt-4", **kwargs)
48
+
49
+ # Azure-specific configuration
50
+ self.azure_endpoint = kwargs.get("azure_endpoint")
51
+ self.azure_deployment = kwargs.get("azure_deployment", model)
52
+ self.api_version = kwargs.get("api_version", "2024-02-15-preview")
53
+
54
+ # Standard OpenAI parameters
55
+ self.max_tokens = kwargs.get("max_tokens", 4096)
56
+ self.temperature = kwargs.get("temperature", 0.7)
57
+ self.top_p = kwargs.get("top_p", 1.0)
58
+ self.frequency_penalty = kwargs.get("frequency_penalty", 0.0)
59
+ self.presence_penalty = kwargs.get("presence_penalty", 0.0)
60
+
61
+ # Validate required Azure configuration
62
+ if not self.azure_endpoint:
63
+ raise ValueError("azure_endpoint is required for Azure OpenAI provider")
64
+
65
+ # Ensure endpoint format is correct
66
+ if not self.azure_endpoint.startswith("https://"):
67
+ self.azure_endpoint = f"https://{self.azure_endpoint}"
68
+
69
+ if not self.azure_endpoint.endswith(".openai.azure.com"):
70
+ if not self.azure_endpoint.endswith(".openai.azure.com/"):
71
+ self.azure_endpoint = (
72
+ f"{self.azure_endpoint.rstrip('/')}.openai.azure.com"
73
+ )
74
+
75
+ async def send_message(self, message: str, context: ChatContext) -> ChatResponse:
76
+ """
77
+ Send a message to Azure OpenAI API.
78
+
79
+ Args:
80
+ message: User message
81
+ context: Conversation context
82
+
83
+ Returns:
84
+ ChatResponse with Azure OpenAI's reply
85
+ """
86
+ try:
87
+ # Prepare messages for Azure OpenAI API
88
+ messages = self._prepare_messages(message, context)
89
+
90
+ # Build request payload
91
+ payload = {
92
+ "messages": messages,
93
+ "max_tokens": self.max_tokens,
94
+ "temperature": self.temperature,
95
+ "top_p": self.top_p,
96
+ "frequency_penalty": self.frequency_penalty,
97
+ "presence_penalty": self.presence_penalty,
98
+ "stream": False,
99
+ }
100
+
101
+ # Build URL for Azure OpenAI
102
+ url = self._build_azure_url("chat/completions")
103
+
104
+ # Make API request
105
+ async with httpx.AsyncClient() as client:
106
+ response = await client.post(
107
+ url,
108
+ headers={
109
+ "Content-Type": "application/json",
110
+ "api-key": self.api_key,
111
+ },
112
+ json=payload,
113
+ timeout=30.0,
114
+ )
115
+
116
+ # Handle response
117
+ return self._handle_response(response)
118
+
119
+ except httpx.TimeoutException:
120
+ raise NetworkError("Request to Azure OpenAI API timed out")
121
+ except httpx.RequestError as e:
122
+ raise NetworkError(f"Network error: {e}")
123
+ except (
124
+ AuthenticationError,
125
+ RateLimitError,
126
+ ModelNotFoundError,
127
+ ProviderError,
128
+ ):
129
+ # Re-raise provider-specific errors without wrapping
130
+ raise
131
+ except Exception as e:
132
+ raise ProviderError(f"Unexpected error: {e}")
133
+
134
+ async def send_message_with_tools(
135
+ self,
136
+ message: str,
137
+ context: ChatContext,
138
+ available_tools: List[ToolDefinition],
139
+ ) -> ChatResponse:
140
+ """
141
+ Send a message with available tools for Azure OpenAI to use.
142
+
143
+ Args:
144
+ message: User message
145
+ context: Conversation context
146
+ available_tools: List of tools available for the model to use
147
+
148
+ Returns:
149
+ ChatResponse with Azure OpenAI's reply and any tool calls
150
+ """
151
+ try:
152
+ # Prepare messages for Azure OpenAI API
153
+ messages = self._prepare_messages(message, context)
154
+
155
+ # Convert tools to Azure OpenAI format
156
+ tools = self._convert_tools_to_azure_format(available_tools)
157
+
158
+ # Build request payload
159
+ payload = {
160
+ "messages": messages,
161
+ "max_tokens": self.max_tokens,
162
+ "temperature": self.temperature,
163
+ "top_p": self.top_p,
164
+ "frequency_penalty": self.frequency_penalty,
165
+ "presence_penalty": self.presence_penalty,
166
+ "tools": tools,
167
+ "tool_choice": "auto",
168
+ "stream": False,
169
+ }
170
+
171
+ # Build URL for Azure OpenAI
172
+ url = self._build_azure_url("chat/completions")
173
+
174
+ # Make API request
175
+ async with httpx.AsyncClient() as client:
176
+ response = await client.post(
177
+ url,
178
+ headers={
179
+ "Content-Type": "application/json",
180
+ "api-key": self.api_key,
181
+ },
182
+ json=payload,
183
+ timeout=30.0,
184
+ )
185
+
186
+ # Handle response with tool calls
187
+ return self._handle_response_with_tools(response)
188
+
189
+ except httpx.TimeoutException:
190
+ raise NetworkError("Request to Azure OpenAI API timed out")
191
+ except httpx.RequestError as e:
192
+ raise NetworkError(f"Network error: {e}")
193
+ except (
194
+ AuthenticationError,
195
+ RateLimitError,
196
+ ModelNotFoundError,
197
+ ProviderError,
198
+ ):
199
+ # Re-raise provider-specific errors without wrapping
200
+ raise
201
+ except Exception as e:
202
+ raise ProviderError(f"Unexpected error: {e}")
203
+
204
+ async def validate_credentials(self) -> bool:
205
+ """
206
+ Validate Azure OpenAI API credentials by making a test request.
207
+
208
+ Returns:
209
+ True if credentials are valid
210
+ """
211
+ try:
212
+ url = self._build_azure_url("chat/completions")
213
+
214
+ async with httpx.AsyncClient() as client:
215
+ response = await client.post(
216
+ url,
217
+ headers={
218
+ "Content-Type": "application/json",
219
+ "api-key": self.api_key,
220
+ },
221
+ json={
222
+ "messages": [{"role": "user", "content": "Hi"}],
223
+ "max_tokens": 10,
224
+ },
225
+ timeout=10.0,
226
+ )
227
+
228
+ return response.status_code == 200
229
+
230
+ except Exception:
231
+ return False
232
+
233
+ def _build_azure_url(self, endpoint: str) -> str:
234
+ """
235
+ Build Azure OpenAI API URL.
236
+
237
+ Args:
238
+ endpoint: API endpoint (e.g., "chat/completions")
239
+
240
+ Returns:
241
+ Complete Azure OpenAI API URL
242
+ """
243
+ base_url = f"{self.azure_endpoint}/openai/deployments/{self.azure_deployment}"
244
+ return f"{base_url}/{endpoint}?api-version={self.api_version}"
245
+
246
+ def _prepare_messages(
247
+ self, message: str, context: ChatContext
248
+ ) -> List[Dict[str, str]]:
249
+ """
250
+ Prepare messages for Azure OpenAI API format.
251
+
252
+ Args:
253
+ message: Current user message
254
+ context: Conversation context
255
+
256
+ Returns:
257
+ List of messages formatted for Azure OpenAI API
258
+ """
259
+ messages = []
260
+
261
+ # Add context messages
262
+ for msg in context.messages:
263
+ messages.append({"role": msg.role.value, "content": msg.content})
264
+
265
+ # Add current message
266
+ messages.append({"role": "user", "content": message})
267
+
268
+ return messages
269
+
270
+ def _convert_tools_to_azure_format(self, tools: List[ToolDefinition]) -> List[Dict]:
271
+ """
272
+ Convert tool definitions to Azure OpenAI API format.
273
+
274
+ Args:
275
+ tools: List of tool definitions
276
+
277
+ Returns:
278
+ List of tools formatted for Azure OpenAI API
279
+ """
280
+ azure_tools = []
281
+
282
+ for tool in tools:
283
+ azure_tool = {
284
+ "type": "function",
285
+ "function": {
286
+ "name": tool.name,
287
+ "description": tool.description,
288
+ "parameters": tool.parameters,
289
+ },
290
+ }
291
+ azure_tools.append(azure_tool)
292
+
293
+ return azure_tools
294
+
295
+ def _handle_response(self, response: httpx.Response) -> ChatResponse:
296
+ """
297
+ Handle Azure OpenAI API response.
298
+
299
+ Args:
300
+ response: HTTP response from Azure OpenAI API
301
+
302
+ Returns:
303
+ ChatResponse object
304
+
305
+ Raises:
306
+ Various provider errors based on response status
307
+ """
308
+ if response.status_code == 200:
309
+ data = response.json()
310
+ choices = data.get("choices", [])
311
+
312
+ if choices and len(choices) > 0:
313
+ message = choices[0].get("message", {})
314
+ content = message.get("content", "")
315
+ usage = data.get("usage", {})
316
+
317
+ # Use model name instead of deployment name for consistency
318
+ model_name = self.model if self.model else self.azure_deployment
319
+ return ChatResponse(
320
+ content=content,
321
+ model_used=model_name,
322
+ tokens_used=usage.get("total_tokens", 0),
323
+ timestamp=datetime.now(),
324
+ )
325
+ else:
326
+ raise ProviderError("Empty response from Azure OpenAI API")
327
+
328
+ elif response.status_code == 401:
329
+ raise AuthenticationError(
330
+ "Invalid Azure OpenAI API key or insufficient permissions"
331
+ )
332
+ elif response.status_code == 429:
333
+ raise RateLimitError("Azure OpenAI API rate limit exceeded")
334
+ elif response.status_code == 404:
335
+ raise ModelNotFoundError(
336
+ f"Azure deployment '{self.azure_deployment}' not found"
337
+ )
338
+ else:
339
+ try:
340
+ error_data = response.json()
341
+ error_msg = error_data.get("error", {}).get("message", "Unknown error")
342
+ except:
343
+ error_msg = f"HTTP {response.status_code}"
344
+
345
+ raise ProviderError(f"Azure OpenAI API error: {error_msg}")
346
+
347
+ def _handle_response_with_tools(self, response: httpx.Response) -> ChatResponse:
348
+ """
349
+ Handle Azure OpenAI API response with tool calls.
350
+
351
+ Args:
352
+ response: HTTP response from Azure OpenAI API
353
+
354
+ Returns:
355
+ ChatResponse object with tool calls
356
+ """
357
+ if response.status_code == 200:
358
+ data = response.json()
359
+ choices = data.get("choices", [])
360
+
361
+ if choices and len(choices) > 0:
362
+ message = choices[0].get("message", {})
363
+ content = message.get("content", "")
364
+ usage = data.get("usage", {})
365
+
366
+ # Extract tool calls if present
367
+ tool_calls = []
368
+ if "tool_calls" in message:
369
+ for tool_call in message["tool_calls"]:
370
+ if tool_call.get("type") == "function":
371
+ function = tool_call.get("function", {})
372
+ tool_calls.append(
373
+ ToolCall(
374
+ name=function.get("name", ""),
375
+ arguments=function.get("arguments", {}),
376
+ )
377
+ )
378
+
379
+ # Use model name instead of deployment name for consistency
380
+ model_name = self.model if self.model else self.azure_deployment
381
+ return ChatResponse(
382
+ content=content,
383
+ model_used=model_name,
384
+ tokens_used=usage.get("total_tokens", 0),
385
+ tool_calls=tool_calls if tool_calls else None,
386
+ timestamp=datetime.now(),
387
+ )
388
+ else:
389
+ raise ProviderError("Empty response from Azure OpenAI API")
390
+ else:
391
+ return self._handle_response(response)
392
+
393
+ def get_model_info(self) -> EnhancedModelInfo:
394
+ """
395
+ Get information about the current Azure OpenAI model.
396
+ """
397
+ # Map common Azure deployment names to model info
398
+ model_configs = {
399
+ "gpt-4": {
400
+ "description": "GPT-4 via Azure OpenAI Service",
401
+ "max_tokens": 8192,
402
+ "cost_per_million_input": 30.0,
403
+ "cost_per_million_output": 60.0,
404
+ "swe_score": 67.0,
405
+ "supports_tools": True,
406
+ "supports_multimodal": False,
407
+ },
408
+ "gpt-4-turbo": {
409
+ "description": "GPT-4 Turbo on Azure - Enhanced performance",
410
+ "max_tokens": 128000,
411
+ "cost_per_million_input": 10.0,
412
+ "cost_per_million_output": 30.0,
413
+ "swe_score": 69.1,
414
+ "supports_tools": True,
415
+ "supports_multimodal": True,
416
+ },
417
+ "gpt-4o": {
418
+ "description": "GPT-4o on Azure - Optimized for speed and efficiency",
419
+ "max_tokens": 128000,
420
+ "cost_per_million_input": 5.0,
421
+ "cost_per_million_output": 15.0,
422
+ "swe_score": 71.2,
423
+ "supports_tools": True,
424
+ "supports_multimodal": True,
425
+ },
426
+ "gpt-35-turbo": {
427
+ "description": "GPT-3.5 Turbo via Azure OpenAI Service",
428
+ "max_tokens": 4096,
429
+ "cost_per_million_input": 0.5,
430
+ "cost_per_million_output": 1.5,
431
+ "swe_score": 48.1,
432
+ "supports_tools": True,
433
+ "supports_multimodal": False,
434
+ },
435
+ "gpt-35-turbo-16k": {
436
+ "description": "GPT-3.5 Turbo 16K on Azure - Extended context",
437
+ "max_tokens": 16384,
438
+ "cost_per_million_input": 3.0,
439
+ "cost_per_million_output": 4.0,
440
+ "swe_score": 48.1,
441
+ "supports_tools": True,
442
+ "supports_multimodal": False,
443
+ },
444
+ }
445
+
446
+ # Try to match deployment name to known models
447
+ config = None
448
+ for model_name, model_config in model_configs.items():
449
+ if model_name in self.azure_deployment.lower():
450
+ config = model_config
451
+ break
452
+
453
+ # Default configuration if no match found
454
+ if not config:
455
+ model_name_for_desc = self.model if self.model else self.azure_deployment
456
+ config = {
457
+ "description": f"Azure OpenAI model {model_name_for_desc}",
458
+ "max_tokens": 4096,
459
+ "cost_per_million_input": 10.0,
460
+ "cost_per_million_output": 30.0,
461
+ "swe_score": 50.0,
462
+ "supports_tools": True,
463
+ "supports_multimodal": False,
464
+ }
465
+
466
+ # Use model name instead of deployment name for consistency
467
+ model_name = self.model if self.model else self.azure_deployment
468
+ enhanced_info = EnhancedModelInfo(
469
+ name=model_name,
470
+ provider="azure",
471
+ description=config["description"],
472
+ max_tokens=config["max_tokens"],
473
+ cost_per_million_input=config["cost_per_million_input"],
474
+ cost_per_million_output=config["cost_per_million_output"],
475
+ swe_score=config["swe_score"],
476
+ available=True,
477
+ supports_tools=config["supports_tools"],
478
+ supports_multimodal=config["supports_multimodal"],
479
+ latest_version="gpt-4o" in self.azure_deployment.lower(),
480
+ context_window=config["max_tokens"],
481
+ is_free=False,
482
+ release_date=datetime(2024, 5, 1), # Approximate Azure availability
483
+ )
484
+
485
+ # Update SWE rating based on score
486
+ enhanced_info.update_swe_rating()
487
+
488
+ return enhanced_info
489
+
490
+ def get_available_models(self) -> List[EnhancedModelInfo]:
491
+ """
492
+ Get list of available Azure OpenAI models.
493
+
494
+ Note: This returns common Azure deployment configurations.
495
+ Actual availability depends on your Azure subscription and region.
496
+ """
497
+ models = [
498
+ EnhancedModelInfo(
499
+ name="gpt-4",
500
+ provider="azure",
501
+ description="GPT-4 on Azure - Most capable model",
502
+ max_tokens=8192,
503
+ cost_per_million_input=30.0,
504
+ cost_per_million_output=60.0,
505
+ swe_score=67.0,
506
+ available=True,
507
+ supports_tools=True,
508
+ supports_multimodal=False,
509
+ context_window=8192,
510
+ is_free=False,
511
+ release_date=datetime(2024, 5, 1),
512
+ ),
513
+ EnhancedModelInfo(
514
+ name="gpt-4-turbo",
515
+ provider="azure",
516
+ description="GPT-4 Turbo on Azure - Enhanced performance",
517
+ max_tokens=128000,
518
+ cost_per_million_input=10.0,
519
+ cost_per_million_output=30.0,
520
+ swe_score=69.1,
521
+ available=True,
522
+ supports_tools=True,
523
+ supports_multimodal=True,
524
+ context_window=128000,
525
+ is_free=False,
526
+ release_date=datetime(2024, 5, 1),
527
+ ),
528
+ EnhancedModelInfo(
529
+ name="gpt-4o",
530
+ provider="azure",
531
+ description="GPT-4o on Azure - Optimized for speed and efficiency",
532
+ max_tokens=128000,
533
+ cost_per_million_input=5.0,
534
+ cost_per_million_output=15.0,
535
+ swe_score=71.2,
536
+ available=True,
537
+ supports_tools=True,
538
+ supports_multimodal=True,
539
+ latest_version=True,
540
+ context_window=128000,
541
+ is_free=False,
542
+ release_date=datetime(2024, 5, 1),
543
+ ),
544
+ EnhancedModelInfo(
545
+ name="gpt-35-turbo",
546
+ provider="azure",
547
+ description="GPT-3.5 Turbo on Azure - Fast and efficient",
548
+ max_tokens=4096,
549
+ cost_per_million_input=0.5,
550
+ cost_per_million_output=1.5,
551
+ swe_score=48.1,
552
+ available=True,
553
+ supports_tools=True,
554
+ supports_multimodal=False,
555
+ context_window=4096,
556
+ is_free=False,
557
+ release_date=datetime(2024, 5, 1),
558
+ ),
559
+ EnhancedModelInfo(
560
+ name="gpt-35-turbo-16k",
561
+ provider="azure",
562
+ description="GPT-3.5 Turbo 16K on Azure - Extended context",
563
+ max_tokens=16384,
564
+ cost_per_million_input=3.0,
565
+ cost_per_million_output=4.0,
566
+ swe_score=48.1,
567
+ available=True,
568
+ supports_tools=True,
569
+ supports_multimodal=False,
570
+ context_window=16384,
571
+ is_free=False,
572
+ release_date=datetime(2024, 5, 1),
573
+ ),
574
+ ]
575
+
576
+ # Update SWE ratings for all models
577
+ for model in models:
578
+ model.update_swe_rating()
579
+
580
+ return models
581
+
582
+ def supports_tools(self) -> bool:
583
+ """
584
+ Check if Azure OpenAI provider supports tool calling.
585
+
586
+ Returns:
587
+ True - Azure OpenAI supports function calling
588
+ """
589
+ return True
590
+
591
+ def supports_multimodal(self) -> bool:
592
+ """
593
+ Check if Azure OpenAI provider supports multimodal inputs.
594
+
595
+ Returns:
596
+ True for GPT-4 models with vision support
597
+ """
598
+ vision_models = ["gpt-4-turbo", "gpt-4o", "gpt-4-vision"]
599
+ return any(model in self.azure_deployment.lower() for model in vision_models)
600
+
601
+ def supports_streaming(self) -> bool:
602
+ """
603
+ Check if Azure OpenAI provider supports streaming responses.
604
+
605
+ Returns:
606
+ True - Azure OpenAI supports streaming
607
+ """
608
+ return True