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,617 @@
1
+ """
2
+ Google Vertex AI provider implementation for Omnimancer.
3
+
4
+ This module provides the Google Vertex AI provider implementation using Vertex AI's API
5
+ with support for Google Cloud project configuration, location settings, and service account authentication.
6
+ """
7
+
8
+ import os
9
+ from datetime import datetime
10
+ from typing import Dict, List
11
+
12
+ import httpx
13
+
14
+ from ..core.models import (
15
+ ChatContext,
16
+ ChatResponse,
17
+ EnhancedModelInfo,
18
+ ToolCall,
19
+ ToolDefinition,
20
+ )
21
+ from ..utils.errors import (
22
+ AuthenticationError,
23
+ ModelNotFoundError,
24
+ NetworkError,
25
+ ProviderError,
26
+ RateLimitError,
27
+ )
28
+ from .base import BaseProvider
29
+
30
+
31
+ class VertexAIProvider(BaseProvider):
32
+ """
33
+ Google Vertex AI provider implementation using Vertex AI's API.
34
+
35
+ Supports Google Cloud project and location configuration,
36
+ service account authentication, and Gemini models.
37
+ """
38
+
39
+ def __init__(self, api_key: str, model: str = "", **kwargs):
40
+ """
41
+ Initialize Vertex AI provider.
42
+
43
+ Args:
44
+ api_key: Google Cloud API key or service account key
45
+ model: Vertex AI model to use (e.g., 'gemini-1.5-pro', 'gemini-1.5-flash', 'gemini-pro')
46
+ **kwargs: Additional configuration including Vertex AI-specific settings
47
+ """
48
+ super().__init__(api_key, model or "gemini-1.5-pro", **kwargs)
49
+
50
+ # Vertex AI-specific configuration
51
+ self.vertex_project = kwargs.get("vertex_project")
52
+ self.vertex_location = kwargs.get("vertex_location", "us-central1")
53
+ self.service_account_path = kwargs.get("service_account_path")
54
+
55
+ # Standard parameters
56
+ self.max_tokens = kwargs.get("max_tokens", 8192)
57
+ self.temperature = kwargs.get("temperature", 0.7)
58
+ self.top_p = kwargs.get("top_p", 0.95)
59
+ self.top_k = kwargs.get("top_k", 40)
60
+
61
+ # Safety settings
62
+ self.safety_settings = kwargs.get("safety_settings", {})
63
+
64
+ # Validate required configuration
65
+ if not self.vertex_project:
66
+ raise ValueError("vertex_project is required for Vertex AI provider")
67
+
68
+ # Set up authentication
69
+ self._setup_authentication()
70
+
71
+ # Build base URL
72
+ self.base_url = f"https://{self.vertex_location}-aiplatform.googleapis.com/v1/projects/{self.vertex_project}/locations/{self.vertex_location}/publishers/google/models"
73
+
74
+ def _setup_authentication(self):
75
+ """Set up Google Cloud authentication."""
76
+ if self.service_account_path:
77
+ # Use service account file
78
+ if os.path.exists(self.service_account_path):
79
+ os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = self.service_account_path
80
+ else:
81
+ raise ValueError(
82
+ f"Service account file not found: {self.service_account_path}"
83
+ )
84
+ elif self.api_key and self.api_key != "":
85
+ # Use API key (for testing or specific configurations)
86
+ self.auth_token = self.api_key
87
+ else:
88
+ # Try to use default credentials
89
+ try:
90
+ # This would normally use Google's auth libraries
91
+ # For now, we'll require explicit configuration
92
+ raise ValueError(
93
+ "Either service_account_path or api_key must be provided for Vertex AI"
94
+ )
95
+ except Exception as e:
96
+ raise ValueError(f"Failed to set up Vertex AI authentication: {e}")
97
+
98
+ async def send_message(self, message: str, context: ChatContext) -> ChatResponse:
99
+ """
100
+ Send a message to Vertex AI API.
101
+
102
+ Args:
103
+ message: User message
104
+ context: Conversation context
105
+
106
+ Returns:
107
+ ChatResponse with Vertex AI's reply
108
+ """
109
+ try:
110
+ # Prepare request for Vertex AI
111
+ request_data = self._prepare_vertex_request(message, context)
112
+
113
+ # Build URL for the specific model
114
+ url = f"{self.base_url}/{self.model}:generateContent"
115
+
116
+ # Make API request
117
+ async with httpx.AsyncClient() as client:
118
+ response = await client.post(
119
+ url,
120
+ headers=self._get_auth_headers(),
121
+ json=request_data,
122
+ timeout=30.0,
123
+ )
124
+
125
+ # Handle response
126
+ return self._handle_response(response)
127
+
128
+ except httpx.TimeoutException:
129
+ raise NetworkError("Request to Vertex AI API timed out")
130
+ except httpx.RequestError as e:
131
+ raise NetworkError(f"Network error: {e}")
132
+ except Exception as e:
133
+ raise ProviderError(f"Unexpected error: {e}")
134
+
135
+ async def send_message_with_tools(
136
+ self,
137
+ message: str,
138
+ context: ChatContext,
139
+ available_tools: List[ToolDefinition],
140
+ ) -> ChatResponse:
141
+ """
142
+ Send a message with available tools for Vertex AI to use.
143
+
144
+ Args:
145
+ message: User message
146
+ context: Conversation context
147
+ available_tools: List of tools available for the model to use
148
+
149
+ Returns:
150
+ ChatResponse with Vertex AI's reply and any tool calls
151
+ """
152
+ try:
153
+ # Prepare request for Vertex AI with tools
154
+ request_data = self._prepare_vertex_request_with_tools(
155
+ message, context, available_tools
156
+ )
157
+
158
+ # Build URL for the specific model
159
+ url = f"{self.base_url}/{self.model}:generateContent"
160
+
161
+ # Make API request
162
+ async with httpx.AsyncClient() as client:
163
+ response = await client.post(
164
+ url,
165
+ headers=self._get_auth_headers(),
166
+ json=request_data,
167
+ timeout=30.0,
168
+ )
169
+
170
+ # Handle response with tool calls
171
+ return self._handle_response_with_tools(response)
172
+
173
+ except httpx.TimeoutException:
174
+ raise NetworkError("Request to Vertex AI API timed out")
175
+ except httpx.RequestError as e:
176
+ raise NetworkError(f"Network error: {e}")
177
+ except Exception as e:
178
+ raise ProviderError(f"Unexpected error: {e}")
179
+
180
+ async def validate_credentials(self) -> bool:
181
+ """
182
+ Validate Vertex AI credentials by making a test request.
183
+
184
+ Returns:
185
+ True if credentials are valid
186
+ """
187
+ try:
188
+ url = f"{self.base_url}/{self.model}:generateContent"
189
+
190
+ test_request = {
191
+ "contents": [{"role": "user", "parts": [{"text": "Hi"}]}],
192
+ "generationConfig": {
193
+ "maxOutputTokens": 10,
194
+ "temperature": 0.1,
195
+ },
196
+ }
197
+
198
+ async with httpx.AsyncClient() as client:
199
+ response = await client.post(
200
+ url,
201
+ headers=self._get_auth_headers(),
202
+ json=test_request,
203
+ timeout=10.0,
204
+ )
205
+
206
+ return response.status_code == 200
207
+
208
+ except Exception:
209
+ return False
210
+
211
+ def _get_auth_headers(self) -> Dict[str, str]:
212
+ """
213
+ Get authentication headers for Vertex AI API.
214
+
215
+ Returns:
216
+ Dictionary of headers
217
+ """
218
+ headers = {"Content-Type": "application/json"}
219
+
220
+ if hasattr(self, "auth_token") and self.auth_token:
221
+ headers["Authorization"] = f"Bearer {self.auth_token}"
222
+
223
+ return headers
224
+
225
+ def _prepare_vertex_request(self, message: str, context: ChatContext) -> Dict:
226
+ """
227
+ Prepare request data for Vertex AI API.
228
+
229
+ Args:
230
+ message: Current user message
231
+ context: Conversation context
232
+
233
+ Returns:
234
+ Request data formatted for Vertex AI API
235
+ """
236
+ contents = []
237
+
238
+ # Add context messages
239
+ for msg in context.messages:
240
+ role = "user" if msg.role.value == "user" else "model"
241
+ contents.append({"role": role, "parts": [{"text": msg.content}]})
242
+
243
+ # Add current message
244
+ contents.append({"role": "user", "parts": [{"text": message}]})
245
+
246
+ request_data = {
247
+ "contents": contents,
248
+ "generationConfig": {
249
+ "maxOutputTokens": self.max_tokens,
250
+ "temperature": self.temperature,
251
+ "topP": self.top_p,
252
+ "topK": self.top_k,
253
+ },
254
+ }
255
+
256
+ # Add safety settings if configured
257
+ if self.safety_settings:
258
+ request_data["safetySettings"] = self._format_safety_settings()
259
+
260
+ return request_data
261
+
262
+ def _prepare_vertex_request_with_tools(
263
+ self, message: str, context: ChatContext, tools: List[ToolDefinition]
264
+ ) -> Dict:
265
+ """
266
+ Prepare request data for Vertex AI API with tools.
267
+
268
+ Args:
269
+ message: Current user message
270
+ context: Conversation context
271
+ tools: List of available tools
272
+
273
+ Returns:
274
+ Request data formatted for Vertex AI API with tools
275
+ """
276
+ request_data = self._prepare_vertex_request(message, context)
277
+
278
+ # Add tools in Vertex AI format
279
+ if tools:
280
+ request_data["tools"] = [
281
+ self._convert_tool_to_vertex_format(tool) for tool in tools
282
+ ]
283
+
284
+ return request_data
285
+
286
+ def _convert_tool_to_vertex_format(self, tool: ToolDefinition) -> Dict:
287
+ """
288
+ Convert tool definition to Vertex AI format.
289
+
290
+ Args:
291
+ tool: Tool definition
292
+
293
+ Returns:
294
+ Tool formatted for Vertex AI API
295
+ """
296
+ return {
297
+ "functionDeclarations": [
298
+ {
299
+ "name": tool.name,
300
+ "description": tool.description,
301
+ "parameters": tool.parameters,
302
+ }
303
+ ]
304
+ }
305
+
306
+ def _format_safety_settings(self) -> List[Dict]:
307
+ """
308
+ Format safety settings for Vertex AI API.
309
+
310
+ Returns:
311
+ List of safety settings
312
+ """
313
+ safety_settings = []
314
+
315
+ # Default safety categories
316
+ categories = [
317
+ "HARM_CATEGORY_HARASSMENT",
318
+ "HARM_CATEGORY_HATE_SPEECH",
319
+ "HARM_CATEGORY_SEXUALLY_EXPLICIT",
320
+ "HARM_CATEGORY_DANGEROUS_CONTENT",
321
+ ]
322
+
323
+ for category in categories:
324
+ threshold = self.safety_settings.get(category, "BLOCK_MEDIUM_AND_ABOVE")
325
+ safety_settings.append({"category": category, "threshold": threshold})
326
+
327
+ return safety_settings
328
+
329
+ def _handle_response(self, response: httpx.Response) -> ChatResponse:
330
+ """
331
+ Handle Vertex AI API response.
332
+
333
+ Args:
334
+ response: HTTP response from Vertex AI API
335
+
336
+ Returns:
337
+ ChatResponse object
338
+
339
+ Raises:
340
+ Various provider errors based on response status
341
+ """
342
+ if response.status_code == 200:
343
+ data = response.json()
344
+ candidates = data.get("candidates", [])
345
+
346
+ if candidates and len(candidates) > 0:
347
+ candidate = candidates[0]
348
+ content = candidate.get("content", {})
349
+ parts = content.get("parts", [])
350
+
351
+ if parts and len(parts) > 0:
352
+ text = parts[0].get("text", "")
353
+
354
+ # Extract usage information
355
+ usage_metadata = data.get("usageMetadata", {})
356
+ total_tokens = usage_metadata.get("totalTokenCount", 0)
357
+
358
+ return ChatResponse(
359
+ content=text,
360
+ model_used=self.model,
361
+ tokens_used=total_tokens,
362
+ timestamp=datetime.now(),
363
+ )
364
+ else:
365
+ raise ProviderError("Empty parts in Vertex AI response")
366
+ else:
367
+ raise ProviderError("Empty candidates in Vertex AI response")
368
+
369
+ elif response.status_code == 401:
370
+ raise AuthenticationError("Invalid Vertex AI credentials")
371
+ elif response.status_code == 429:
372
+ raise RateLimitError("Vertex AI API rate limit exceeded")
373
+ elif response.status_code == 404:
374
+ raise ModelNotFoundError(f"Vertex AI model '{self.model}' not found")
375
+ else:
376
+ try:
377
+ error_data = response.json()
378
+ error_msg = error_data.get("error", {}).get("message", "Unknown error")
379
+ except:
380
+ error_msg = f"HTTP {response.status_code}"
381
+
382
+ raise ProviderError(f"Vertex AI API error: {error_msg}")
383
+
384
+ def _handle_response_with_tools(self, response: httpx.Response) -> ChatResponse:
385
+ """
386
+ Handle Vertex AI API response with tool calls.
387
+
388
+ Args:
389
+ response: HTTP response from Vertex AI API
390
+
391
+ Returns:
392
+ ChatResponse object with tool calls
393
+ """
394
+ if response.status_code == 200:
395
+ data = response.json()
396
+ candidates = data.get("candidates", [])
397
+
398
+ if candidates and len(candidates) > 0:
399
+ candidate = candidates[0]
400
+ content = candidate.get("content", {})
401
+ parts = content.get("parts", [])
402
+
403
+ text_content = ""
404
+ tool_calls = []
405
+
406
+ for part in parts:
407
+ if "text" in part:
408
+ text_content += part["text"]
409
+ elif "functionCall" in part:
410
+ func_call = part["functionCall"]
411
+ tool_calls.append(
412
+ ToolCall(
413
+ name=func_call.get("name", ""),
414
+ arguments=func_call.get("args", {}),
415
+ )
416
+ )
417
+
418
+ # Extract usage information
419
+ usage_metadata = data.get("usageMetadata", {})
420
+ total_tokens = usage_metadata.get("totalTokenCount", 0)
421
+
422
+ return ChatResponse(
423
+ content=text_content,
424
+ model_used=self.model,
425
+ tokens_used=total_tokens,
426
+ tool_calls=tool_calls if tool_calls else None,
427
+ timestamp=datetime.now(),
428
+ )
429
+ else:
430
+ raise ProviderError("Empty candidates in Vertex AI response")
431
+ else:
432
+ return self._handle_response(response)
433
+
434
+ def get_model_info(self) -> EnhancedModelInfo:
435
+ """
436
+ Get information about the current Vertex AI model.
437
+ """
438
+ model_configs = {
439
+ "gemini-1.5-pro": {
440
+ "description": "Gemini 1.5 Pro - Advanced reasoning and long context",
441
+ "max_tokens": 2097152,
442
+ "cost_per_million_input": 1.25,
443
+ "cost_per_million_output": 5.0,
444
+ "swe_score": 71.9,
445
+ "supports_tools": True,
446
+ "supports_multimodal": True,
447
+ },
448
+ "gemini-1.5-flash": {
449
+ "description": "Gemini 1.5 Flash - Fast and efficient",
450
+ "max_tokens": 1048576,
451
+ "cost_per_million_input": 0.075,
452
+ "cost_per_million_output": 0.3,
453
+ "swe_score": 61.5,
454
+ "supports_tools": True,
455
+ "supports_multimodal": True,
456
+ },
457
+ "gemini-1.0-pro": {
458
+ "description": "Gemini 1.0 Pro - Balanced performance",
459
+ "max_tokens": 32768,
460
+ "cost_per_million_input": 0.5,
461
+ "cost_per_million_output": 1.5,
462
+ "swe_score": 53.2,
463
+ "supports_tools": True,
464
+ "supports_multimodal": False,
465
+ },
466
+ "gemini-1.0-pro-vision": {
467
+ "description": "Gemini 1.0 Pro Vision - Multimodal capabilities",
468
+ "max_tokens": 16384,
469
+ "cost_per_million_input": 0.25,
470
+ "cost_per_million_output": 0.5,
471
+ "swe_score": 53.2,
472
+ "supports_tools": False,
473
+ "supports_multimodal": True,
474
+ },
475
+ }
476
+
477
+ config = model_configs.get(
478
+ self.model,
479
+ {
480
+ "description": f"Vertex AI model {self.model}",
481
+ "max_tokens": 32768,
482
+ "cost_per_million_input": 1.0,
483
+ "cost_per_million_output": 3.0,
484
+ "swe_score": 55.0,
485
+ "supports_tools": True,
486
+ "supports_multimodal": True,
487
+ },
488
+ )
489
+
490
+ enhanced_info = EnhancedModelInfo(
491
+ name=self.model,
492
+ provider="vertex",
493
+ description=config["description"],
494
+ max_tokens=config["max_tokens"],
495
+ cost_per_million_input=config["cost_per_million_input"],
496
+ cost_per_million_output=config["cost_per_million_output"],
497
+ swe_score=config["swe_score"],
498
+ available=True,
499
+ supports_tools=config["supports_tools"],
500
+ supports_multimodal=config["supports_multimodal"],
501
+ latest_version=self.model == "gemini-1.5-pro",
502
+ context_window=config["max_tokens"],
503
+ is_free=False,
504
+ release_date=datetime(2024, 5, 1), # Approximate release date
505
+ )
506
+
507
+ # Update SWE rating based on score
508
+ enhanced_info.update_swe_rating()
509
+
510
+ return enhanced_info
511
+
512
+ def get_available_models(self) -> List[EnhancedModelInfo]:
513
+ """
514
+ Get list of available Vertex AI models.
515
+ """
516
+ models = [
517
+ EnhancedModelInfo(
518
+ name="gemini-1.5-pro",
519
+ provider="vertex",
520
+ description="Gemini 1.5 Pro - Advanced reasoning and long context",
521
+ max_tokens=2097152,
522
+ cost_per_million_input=1.25,
523
+ cost_per_million_output=5.0,
524
+ swe_score=71.9,
525
+ available=True,
526
+ supports_tools=True,
527
+ supports_multimodal=True,
528
+ latest_version=True,
529
+ context_window=2097152,
530
+ is_free=False,
531
+ release_date=datetime(2024, 5, 1),
532
+ ),
533
+ EnhancedModelInfo(
534
+ name="gemini-1.5-flash",
535
+ provider="vertex",
536
+ description="Gemini 1.5 Flash - Fast and efficient",
537
+ max_tokens=1048576,
538
+ cost_per_million_input=0.075,
539
+ cost_per_million_output=0.3,
540
+ swe_score=61.5,
541
+ available=True,
542
+ supports_tools=True,
543
+ supports_multimodal=True,
544
+ context_window=1048576,
545
+ is_free=False,
546
+ release_date=datetime(2024, 5, 1),
547
+ ),
548
+ EnhancedModelInfo(
549
+ name="gemini-1.0-pro",
550
+ provider="vertex",
551
+ description="Gemini 1.0 Pro - Balanced performance",
552
+ max_tokens=32768,
553
+ cost_per_million_input=0.5,
554
+ cost_per_million_output=1.5,
555
+ swe_score=53.2,
556
+ available=True,
557
+ supports_tools=True,
558
+ supports_multimodal=False,
559
+ context_window=32768,
560
+ is_free=False,
561
+ release_date=datetime(2024, 2, 1),
562
+ ),
563
+ EnhancedModelInfo(
564
+ name="gemini-1.0-pro-vision",
565
+ provider="vertex",
566
+ description="Gemini 1.0 Pro Vision - Multimodal capabilities",
567
+ max_tokens=16384,
568
+ cost_per_million_input=0.25,
569
+ cost_per_million_output=0.5,
570
+ swe_score=53.2,
571
+ available=True,
572
+ supports_tools=False,
573
+ supports_multimodal=True,
574
+ context_window=16384,
575
+ is_free=False,
576
+ release_date=datetime(2024, 2, 1),
577
+ ),
578
+ ]
579
+
580
+ # Update SWE ratings for all models
581
+ for model in models:
582
+ model.update_swe_rating()
583
+
584
+ return models
585
+
586
+ def supports_tools(self) -> bool:
587
+ """
588
+ Check if Vertex AI provider supports tool calling.
589
+
590
+ Returns:
591
+ True for most models except vision-only variants
592
+ """
593
+ non_tool_models = ["gemini-1.0-pro-vision"]
594
+ return self.model not in non_tool_models
595
+
596
+ def supports_multimodal(self) -> bool:
597
+ """
598
+ Check if Vertex AI provider supports multimodal inputs.
599
+
600
+ Returns:
601
+ True for Gemini 1.5 models and vision variants
602
+ """
603
+ multimodal_models = [
604
+ "gemini-1.5-pro",
605
+ "gemini-1.5-flash",
606
+ "gemini-1.0-pro-vision",
607
+ ]
608
+ return any(model in self.model for model in multimodal_models)
609
+
610
+ def supports_streaming(self) -> bool:
611
+ """
612
+ Check if Vertex AI provider supports streaming responses.
613
+
614
+ Returns:
615
+ True - Vertex AI supports streaming
616
+ """
617
+ return True