Spaces:
Paused
Paused
| """ | |
| Supports writing files to Google AI Studio Files API. | |
| For vertex ai, check out the vertex_ai/files/handler.py file. | |
| """ | |
| import time | |
| from typing import List, Optional | |
| import httpx | |
| from litellm._logging import verbose_logger | |
| from litellm.litellm_core_utils.prompt_templates.common_utils import extract_file_data | |
| from litellm.llms.base_llm.files.transformation import ( | |
| BaseFilesConfig, | |
| LiteLLMLoggingObj, | |
| ) | |
| from litellm.types.llms.gemini import GeminiCreateFilesResponseObject | |
| from litellm.types.llms.openai import ( | |
| CreateFileRequest, | |
| OpenAICreateFileRequestOptionalParams, | |
| OpenAIFileObject, | |
| ) | |
| from litellm.types.utils import LlmProviders | |
| from ..common_utils import GeminiModelInfo | |
| class GoogleAIStudioFilesHandler(GeminiModelInfo, BaseFilesConfig): | |
| def __init__(self): | |
| pass | |
| def custom_llm_provider(self) -> LlmProviders: | |
| return LlmProviders.GEMINI | |
| def get_complete_url( | |
| self, | |
| api_base: Optional[str], | |
| api_key: Optional[str], | |
| model: str, | |
| optional_params: dict, | |
| litellm_params: dict, | |
| stream: Optional[bool] = None, | |
| ) -> str: | |
| """ | |
| OPTIONAL | |
| Get the complete url for the request | |
| Some providers need `model` in `api_base` | |
| """ | |
| endpoint = "upload/v1beta/files" | |
| api_base = self.get_api_base(api_base) | |
| if not api_base: | |
| raise ValueError("api_base is required") | |
| if not api_key: | |
| raise ValueError("api_key is required") | |
| url = "{}/{}?key={}".format(api_base, endpoint, api_key) | |
| return url | |
| def get_supported_openai_params( | |
| self, model: str | |
| ) -> List[OpenAICreateFileRequestOptionalParams]: | |
| return [] | |
| def map_openai_params( | |
| self, | |
| non_default_params: dict, | |
| optional_params: dict, | |
| model: str, | |
| drop_params: bool, | |
| ) -> dict: | |
| return optional_params | |
| def transform_create_file_request( | |
| self, | |
| model: str, | |
| create_file_data: CreateFileRequest, | |
| optional_params: dict, | |
| litellm_params: dict, | |
| ) -> dict: | |
| """ | |
| Transform the OpenAI-style file creation request into Gemini's format | |
| Returns: | |
| dict: Contains both request data and headers for the two-step upload | |
| """ | |
| # Extract the file information | |
| file_data = create_file_data.get("file") | |
| if file_data is None: | |
| raise ValueError("File data is required") | |
| # Use the common utility function to extract file data | |
| extracted_data = extract_file_data(file_data) | |
| # Get file size | |
| file_size = len(extracted_data["content"]) | |
| # Step 1: Initial resumable upload request | |
| headers = { | |
| "X-Goog-Upload-Protocol": "resumable", | |
| "X-Goog-Upload-Command": "start", | |
| "X-Goog-Upload-Header-Content-Length": str(file_size), | |
| "X-Goog-Upload-Header-Content-Type": extracted_data["content_type"], | |
| "Content-Type": "application/json", | |
| } | |
| headers.update(extracted_data["headers"]) # Add any custom headers | |
| # Initial metadata request body | |
| initial_data = { | |
| "file": { | |
| "display_name": extracted_data["filename"] or str(int(time.time())) | |
| } | |
| } | |
| # Step 2: Actual file upload data | |
| upload_headers = { | |
| "Content-Length": str(file_size), | |
| "X-Goog-Upload-Offset": "0", | |
| "X-Goog-Upload-Command": "upload, finalize", | |
| } | |
| return { | |
| "initial_request": {"headers": headers, "data": initial_data}, | |
| "upload_request": { | |
| "headers": upload_headers, | |
| "data": extracted_data["content"], | |
| }, | |
| } | |
| def transform_create_file_response( | |
| self, | |
| model: Optional[str], | |
| raw_response: httpx.Response, | |
| logging_obj: LiteLLMLoggingObj, | |
| litellm_params: dict, | |
| ) -> OpenAIFileObject: | |
| """ | |
| Transform Gemini's file upload response into OpenAI-style FileObject | |
| """ | |
| try: | |
| response_json = raw_response.json() | |
| response_object = GeminiCreateFilesResponseObject( | |
| **response_json.get("file", {}) # type: ignore | |
| ) | |
| # Extract file information from Gemini response | |
| return OpenAIFileObject( | |
| id=response_object["uri"], # Gemini uses URI as identifier | |
| bytes=int( | |
| response_object["sizeBytes"] | |
| ), # Gemini doesn't return file size | |
| created_at=int( | |
| time.mktime( | |
| time.strptime( | |
| response_object["createTime"].replace("Z", "+00:00"), | |
| "%Y-%m-%dT%H:%M:%S.%f%z", | |
| ) | |
| ) | |
| ), | |
| filename=response_object["displayName"], | |
| object="file", | |
| purpose="user_data", # Default to assistants as that's the main use case | |
| status="uploaded", | |
| status_details=None, | |
| ) | |
| except Exception as e: | |
| verbose_logger.exception(f"Error parsing file upload response: {str(e)}") | |
| raise ValueError(f"Error parsing file upload response: {str(e)}") | |