-
-
Notifications
You must be signed in to change notification settings - Fork 11.7k
Managed batches fixes for vertex #22464
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -108,11 +108,18 @@ async def _async_create_batch( | |||||||||||||||||||
| client = get_async_httpx_client( | ||||||||||||||||||||
| llm_provider=litellm.LlmProviders.VERTEX_AI, | ||||||||||||||||||||
| ) | ||||||||||||||||||||
| response = await client.post( | ||||||||||||||||||||
| url=api_base, | ||||||||||||||||||||
| headers=headers, | ||||||||||||||||||||
| data=json.dumps(vertex_batch_request), | ||||||||||||||||||||
| ) | ||||||||||||||||||||
| try: | ||||||||||||||||||||
| response = await client.post( | ||||||||||||||||||||
| url=api_base, | ||||||||||||||||||||
| headers=headers, | ||||||||||||||||||||
| data=json.dumps(vertex_batch_request), | ||||||||||||||||||||
| ) | ||||||||||||||||||||
| except httpx.HTTPStatusError as e: | ||||||||||||||||||||
| error_body = e.response.text if hasattr(e, 'response') else "N/A" | ||||||||||||||||||||
| litellm.verbose_logger.error( | ||||||||||||||||||||
| f"Vertex AI batch create failed: status={e.response.status_code}, body={error_body[:1000]}" | ||||||||||||||||||||
| ) | ||||||||||||||||||||
|
Comment on lines
+118
to
+121
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Redundant
Suggested change
|
||||||||||||||||||||
| raise | ||||||||||||||||||||
| if response.status_code != 200: | ||||||||||||||||||||
| raise Exception(f"Error: {response.status_code} {response.text}") | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -335,37 +335,84 @@ def get_error_class( | |||||||||||||||||||||||||||||||
| status_code=status_code, message=error_message, headers=headers | ||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| def _parse_gcs_uri(self, file_id: str) -> Tuple[str, str]: | ||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||
| Parse a GCS URI (gs://bucket/path/to/object) into (bucket, url-encoded-object-path). | ||||||||||||||||||||||||||||||||
| Handles both raw and URL-encoded input. | ||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||
| import urllib.parse | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| decoded = urllib.parse.unquote(file_id) | ||||||||||||||||||||||||||||||||
| if decoded.startswith("gs://"): | ||||||||||||||||||||||||||||||||
| full_path = decoded[5:] | ||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||
| full_path = decoded | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if "/" in full_path: | ||||||||||||||||||||||||||||||||
| bucket_name, object_path = full_path.split("/", 1) | ||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||
| bucket_name = full_path | ||||||||||||||||||||||||||||||||
| object_path = "" | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| encoded_object = urllib.parse.quote(object_path, safe="") | ||||||||||||||||||||||||||||||||
| return bucket_name, encoded_object | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| def transform_retrieve_file_request( | ||||||||||||||||||||||||||||||||
| self, | ||||||||||||||||||||||||||||||||
| file_id: str, | ||||||||||||||||||||||||||||||||
| optional_params: dict, | ||||||||||||||||||||||||||||||||
| litellm_params: dict, | ||||||||||||||||||||||||||||||||
| ) -> tuple[str, dict]: | ||||||||||||||||||||||||||||||||
| raise NotImplementedError("VertexAIFilesConfig does not support file retrieval") | ||||||||||||||||||||||||||||||||
| bucket, encoded_object = self._parse_gcs_uri(file_id) | ||||||||||||||||||||||||||||||||
| url = f"https://storage.googleapis.com/storage/v1/b/{bucket}/o/{encoded_object}" | ||||||||||||||||||||||||||||||||
| return url, {} | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| def transform_retrieve_file_response( | ||||||||||||||||||||||||||||||||
| self, | ||||||||||||||||||||||||||||||||
| raw_response: Response, | ||||||||||||||||||||||||||||||||
| logging_obj: LiteLLMLoggingObj, | ||||||||||||||||||||||||||||||||
| litellm_params: dict, | ||||||||||||||||||||||||||||||||
| ) -> OpenAIFileObject: | ||||||||||||||||||||||||||||||||
| raise NotImplementedError("VertexAIFilesConfig does not support file retrieval") | ||||||||||||||||||||||||||||||||
| response_json = raw_response.json() | ||||||||||||||||||||||||||||||||
| gcs_id = response_json.get("id", "") | ||||||||||||||||||||||||||||||||
| gcs_id = "/".join(gcs_id.split("/")[:-1]) if gcs_id else "" | ||||||||||||||||||||||||||||||||
| return OpenAIFileObject( | ||||||||||||||||||||||||||||||||
| id=f"gs://{gcs_id}", | ||||||||||||||||||||||||||||||||
| bytes=int(response_json.get("size", 0)), | ||||||||||||||||||||||||||||||||
| created_at=_convert_vertex_datetime_to_openai_datetime( | ||||||||||||||||||||||||||||||||
| vertex_datetime=response_json.get("timeCreated", "") | ||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||
| filename=response_json.get("name", ""), | ||||||||||||||||||||||||||||||||
| object="file", | ||||||||||||||||||||||||||||||||
| purpose=response_json.get("metadata", {}).get("purpose", "batch"), | ||||||||||||||||||||||||||||||||
| status="processed", | ||||||||||||||||||||||||||||||||
| status_details=None, | ||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| def transform_delete_file_request( | ||||||||||||||||||||||||||||||||
| self, | ||||||||||||||||||||||||||||||||
| file_id: str, | ||||||||||||||||||||||||||||||||
| optional_params: dict, | ||||||||||||||||||||||||||||||||
| litellm_params: dict, | ||||||||||||||||||||||||||||||||
| ) -> tuple[str, dict]: | ||||||||||||||||||||||||||||||||
| raise NotImplementedError("VertexAIFilesConfig does not support file deletion") | ||||||||||||||||||||||||||||||||
| bucket, encoded_object = self._parse_gcs_uri(file_id) | ||||||||||||||||||||||||||||||||
| url = f"https://storage.googleapis.com/storage/v1/b/{bucket}/o/{encoded_object}" | ||||||||||||||||||||||||||||||||
| return url, {} | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| def transform_delete_file_response( | ||||||||||||||||||||||||||||||||
| self, | ||||||||||||||||||||||||||||||||
| raw_response: Response, | ||||||||||||||||||||||||||||||||
| logging_obj: LiteLLMLoggingObj, | ||||||||||||||||||||||||||||||||
| litellm_params: dict, | ||||||||||||||||||||||||||||||||
| ) -> FileDeleted: | ||||||||||||||||||||||||||||||||
| raise NotImplementedError("VertexAIFilesConfig does not support file deletion") | ||||||||||||||||||||||||||||||||
| file_id = "deleted" | ||||||||||||||||||||||||||||||||
| if hasattr(raw_response, "request") and raw_response.request: | ||||||||||||||||||||||||||||||||
| url = str(raw_response.request.url) | ||||||||||||||||||||||||||||||||
| if "/o/" in url: | ||||||||||||||||||||||||||||||||
| import urllib.parse | ||||||||||||||||||||||||||||||||
| encoded_name = url.split("/o/")[-1].split("?")[0] | ||||||||||||||||||||||||||||||||
| file_id = f"gs://{urllib.parse.unquote(encoded_name)}" | ||||||||||||||||||||||||||||||||
|
Comment on lines
+408
to
+414
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reconstructed delete file ID is missing the bucket name The URL format is The bucket name should also be extracted from the URL (between
Suggested change
|
||||||||||||||||||||||||||||||||
| return FileDeleted(id=file_id, deleted=True, object="file") | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| def transform_list_files_request( | ||||||||||||||||||||||||||||||||
| self, | ||||||||||||||||||||||||||||||||
|
|
@@ -389,15 +436,18 @@ def transform_file_content_request( | |||||||||||||||||||||||||||||||
| optional_params: dict, | ||||||||||||||||||||||||||||||||
| litellm_params: dict, | ||||||||||||||||||||||||||||||||
| ) -> tuple[str, dict]: | ||||||||||||||||||||||||||||||||
| raise NotImplementedError("VertexAIFilesConfig does not support file content retrieval") | ||||||||||||||||||||||||||||||||
| file_id = file_content_request.get("file_id", "") | ||||||||||||||||||||||||||||||||
| bucket, encoded_object = self._parse_gcs_uri(file_id) | ||||||||||||||||||||||||||||||||
| url = f"https://storage.googleapis.com/storage/v1/b/{bucket}/o/{encoded_object}?alt=media" | ||||||||||||||||||||||||||||||||
| return url, {} | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| def transform_file_content_response( | ||||||||||||||||||||||||||||||||
| self, | ||||||||||||||||||||||||||||||||
| raw_response: Response, | ||||||||||||||||||||||||||||||||
| logging_obj: LiteLLMLoggingObj, | ||||||||||||||||||||||||||||||||
| litellm_params: dict, | ||||||||||||||||||||||||||||||||
| ) -> HttpxBinaryResponseContent: | ||||||||||||||||||||||||||||||||
| raise NotImplementedError("VertexAIFilesConfig does not support file content retrieval") | ||||||||||||||||||||||||||||||||
| return HttpxBinaryResponseContent(response=raw_response) | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| class VertexAIJsonlFilesTransformation(VertexGeminiConfig): | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sync
file_retrievemissingvertex_aiproviderafile_retrievenow accepts"vertex_ai"(and"gemini"), but it delegates to the synchronousfile_retrieveat line 337 which still only acceptsLiteral["openai", "azure", "hosted_vllm", "manus"]. Callingafile_retrieve(file_id, custom_llm_provider="vertex_ai")will hit the sync function with a provider value it doesn't recognise, causing a type mismatch and likely a routing failure at runtime.The
Literaltype onfile_retrieve(line 339) needs to be updated to include"gemini"and"vertex_ai"as well.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ephrimstanley Can you fix this one