feat: add /delete endpoint support for gemini

This commit is contained in:
Sameer Kankute
2026-01-30 16:06:07 +05:30
parent 8fcdf6105f
commit 78dd4b2b2f
2 changed files with 94 additions and 10 deletions
+7 -6
View File
@@ -9,6 +9,7 @@ import asyncio
import contextvars
import os
import time
import uuid
from functools import partial
from typing import Any, Coroutine, Dict, Literal, Optional, Union, cast
@@ -61,7 +62,7 @@ async def acreate_file(
file: FileTypes,
purpose: Literal["assistants", "batch", "fine-tune"],
expires_after: Optional[FileExpiresAfter] = None,
custom_llm_provider: Literal["openai", "azure", "vertex_ai", "bedrock", "hosted_vllm", "manus"] = "openai",
custom_llm_provider: Literal["openai", "azure", "gemini", "vertex_ai", "bedrock", "hosted_vllm", "manus"] = "openai",
extra_headers: Optional[Dict[str, str]] = None,
extra_body: Optional[Dict[str, str]] = None,
**kwargs,
@@ -106,7 +107,7 @@ def create_file(
file: FileTypes,
purpose: Literal["assistants", "batch", "fine-tune"],
expires_after: Optional[FileExpiresAfter] = None,
custom_llm_provider: Optional[Literal["openai", "azure", "vertex_ai", "bedrock", "hosted_vllm", "manus"]] = None,
custom_llm_provider: Optional[Literal["openai", "azure", "gemini", "vertex_ai", "bedrock", "hosted_vllm", "manus"]] = None,
extra_headers: Optional[Dict[str, str]] = None,
extra_body: Optional[Dict[str, str]] = None,
**kwargs,
@@ -294,7 +295,7 @@ def create_file(
@client
async def afile_retrieve(
file_id: str,
custom_llm_provider: Literal["openai", "azure", "hosted_vllm", "manus"] = "openai",
custom_llm_provider: Literal["openai", "azure", "gemini", "hosted_vllm", "manus"] = "openai",
extra_headers: Optional[Dict[str, str]] = None,
extra_body: Optional[Dict[str, str]] = None,
**kwargs,
@@ -493,7 +494,7 @@ def file_retrieve(
@client
async def afile_delete(
file_id: str,
custom_llm_provider: Literal["openai", "azure", "manus"] = "openai",
custom_llm_provider: Literal["openai", "azure", "gemini", "manus"] = "openai",
extra_headers: Optional[Dict[str, str]] = None,
extra_body: Optional[Dict[str, str]] = None,
**kwargs,
@@ -537,7 +538,7 @@ async def afile_delete(
def file_delete(
file_id: str,
model: Optional[str] = None,
custom_llm_provider: Union[Literal["openai", "azure", "manus"], str] = "openai",
custom_llm_provider: Union[Literal["openai", "azure", "gemini", "manus"], str] = "openai",
extra_headers: Optional[Dict[str, str]] = None,
extra_body: Optional[Dict[str, str]] = None,
**kwargs,
@@ -680,7 +681,7 @@ def file_delete(
)
else:
raise litellm.exceptions.BadRequestError(
message="LiteLLM doesn't support {} for 'file_delete'. Only 'openai', 'azure', and 'manus' are supported.".format(
message="LiteLLM doesn't support {} for 'file_delete'. Only 'openai', 'azure', 'gemini', and 'manus' are supported.".format(
custom_llm_provider
),
model="n/a",
+87 -4
View File
@@ -35,6 +35,26 @@ class GoogleAIStudioFilesHandler(GeminiModelInfo, BaseFilesConfig):
def custom_llm_provider(self) -> LlmProviders:
return LlmProviders.GEMINI
def validate_environment(
self,
api_key: Optional[str],
headers: dict,
model: str,
messages: list,
optional_params: dict,
litellm_params: dict,
) -> dict:
"""
Validate environment and add Gemini API key to headers.
Google AI Studio uses x-goog-api-key header for authentication.
"""
api_key = self.get_api_key(api_key)
if not api_key:
raise ValueError("GEMINI_API_KEY is required for Google AI Studio file operations")
headers["x-goog-api-key"] = api_key
return headers
def get_complete_url(
self,
api_base: Optional[str],
@@ -56,10 +76,12 @@ class GoogleAIStudioFilesHandler(GeminiModelInfo, BaseFilesConfig):
if not api_base:
raise ValueError("api_base is required")
if not api_key:
# Get API key from multiple sources
final_api_key = api_key or litellm_params.get("api_key") or self.get_api_key()
if not final_api_key:
raise ValueError("api_key is required")
url = "{}/{}?key={}".format(api_base, endpoint, api_key)
url = "{}/{}?key={}".format(api_base, endpoint, final_api_key)
return url
def get_supported_openai_params(
@@ -196,7 +218,41 @@ class GoogleAIStudioFilesHandler(GeminiModelInfo, BaseFilesConfig):
optional_params: dict,
litellm_params: dict,
) -> tuple[str, dict]:
raise NotImplementedError("GoogleAIStudioFilesHandler does not support file deletion")
"""
Transform delete file request for Google AI Studio.
Args:
file_id: The file URI (e.g., "files/abc123" or full URI)
optional_params: Optional parameters
litellm_params: LiteLLM parameters containing api_key
Returns:
tuple[str, dict]: (url, params) for the DELETE request
"""
api_base = self.get_api_base(litellm_params.get("api_base"))
if not api_base:
raise ValueError("api_base is required")
# Get API key from multiple sources (same pattern as get_complete_url)
api_key = litellm_params.get("api_key") or self.get_api_key()
if not api_key:
raise ValueError("api_key is required")
# Extract file name from URI if full URI is provided
# file_id could be "files/abc123" or "https://generativelanguage.googleapis.com/v1beta/files/abc123"
if file_id.startswith("http"):
# Extract the file path from full URI
file_name = file_id.split("/v1beta/")[-1]
else:
file_name = file_id
# Construct the delete URL
url = f"{api_base}/v1beta/{file_name}"
# Add API key as header (Google AI Studio uses x-goog-api-key header)
params = {}
return url, params
def transform_delete_file_response(
self,
@@ -204,7 +260,34 @@ class GoogleAIStudioFilesHandler(GeminiModelInfo, BaseFilesConfig):
logging_obj: LiteLLMLoggingObj,
litellm_params: dict,
) -> FileDeleted:
raise NotImplementedError("GoogleAIStudioFilesHandler does not support file deletion")
"""
Transform Gemini's file delete response into OpenAI-style FileDeleted.
Google AI Studio returns an empty JSON object {} on successful deletion.
"""
try:
# Google AI Studio returns {} on successful deletion
if raw_response.status_code == 200:
# Extract file ID from the request URL if possible
file_id = "deleted"
if hasattr(raw_response, "request") and raw_response.request:
url = str(raw_response.request.url)
if "/files/" in url:
file_id = url.split("/files/")[-1].split("?")[0]
# Add the files/ prefix if not present
if not file_id.startswith("files/"):
file_id = f"files/{file_id}"
return FileDeleted(
id=file_id,
deleted=True,
object="file"
)
else:
raise ValueError(f"Failed to delete file: {raw_response.text}")
except Exception as e:
verbose_logger.exception(f"Error parsing file delete response: {str(e)}")
raise ValueError(f"Error parsing file delete response: {str(e)}")
def transform_list_files_request(
self,