Litellm dev 04 10 2025 p3 (#9903)

* feat(managed_files.py): encode file type in unified file id

simplify calling gemini models

* fix(common_utils.py): fix extracting file type from unified file id

* fix(litellm_logging.py): create standard logging payload for create file call

* fix: fix linting error
This commit is contained in:
Krish Dholakia 2025-04-11 09:29:42 -07:00 committed by GitHub
parent 8ecd9ede81
commit 0415f1205e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 99 additions and 22 deletions

View file

@ -56,6 +56,7 @@ from litellm.types.llms.openai import (
Batch,
FineTuningJob,
HttpxBinaryResponseContent,
OpenAIFileObject,
ResponseCompletedEvent,
ResponsesAPIResponse,
)
@ -902,6 +903,7 @@ class Logging(LiteLLMLoggingBaseClass):
FineTuningJob,
ResponsesAPIResponse,
ResponseCompletedEvent,
OpenAIFileObject,
LiteLLMRealtimeStreamLoggingObject,
],
cache_hit: Optional[bool] = None,
@ -1095,6 +1097,7 @@ class Logging(LiteLLMLoggingBaseClass):
or isinstance(logging_result, FineTuningJob)
or isinstance(logging_result, LiteLLMBatch)
or isinstance(logging_result, ResponsesAPIResponse)
or isinstance(logging_result, OpenAIFileObject)
or isinstance(logging_result, LiteLLMRealtimeStreamLoggingObject)
):
## HIDDEN PARAMS ##

View file

@ -4,6 +4,7 @@ Common utility functions used for translating messages across providers
import io
import mimetypes
import re
from os import PathLike
from typing import Dict, List, Literal, Mapping, Optional, Union, cast
@ -18,6 +19,7 @@ from litellm.types.utils import (
ExtractedFileData,
FileTypes,
ModelResponse,
SpecialEnums,
StreamingChoices,
)
@ -325,6 +327,29 @@ def get_file_ids_from_messages(messages: List[AllMessageValues]) -> List[str]:
return file_ids
def get_format_from_file_id(file_id: Optional[str]) -> Optional[str]:
"""
Gets format from file id
unified_file_id = litellm_proxy:{};unified_id,{}
If not a unified file id, returns 'file' as default format
"""
if not file_id:
return None
try:
if file_id.startswith(SpecialEnums.LITELM_MANAGED_FILE_ID_PREFIX.value):
match = re.match(
f"{SpecialEnums.LITELM_MANAGED_FILE_ID_PREFIX.value}:(.*?);unified_id",
file_id,
)
if match:
return match.group(1)
return None
except Exception:
return None
def update_messages_with_model_file_ids(
messages: List[AllMessageValues],
model_id: str,
@ -350,12 +375,18 @@ def update_messages_with_model_file_ids(
file_object = cast(ChatCompletionFileObject, c)
file_object_file_field = file_object["file"]
file_id = file_object_file_field.get("file_id")
format = file_object_file_field.get(
"format", get_format_from_file_id(file_id)
)
if file_id:
provider_file_id = (
model_file_id_mapping.get(file_id, {}).get(model_id)
or file_id
)
file_object_file_field["file_id"] = provider_file_id
if format:
file_object_file_field["format"] = format
return messages
@ -421,6 +452,7 @@ def extract_file_data(file_data: FileTypes) -> ExtractedFileData:
headers=file_headers,
)
def unpack_defs(schema, defs):
properties = schema.get("properties", None)
if properties is None:
@ -452,4 +484,3 @@ def unpack_defs(schema, defs):
unpack_defs(ref, defs)
value["items"] = ref
continue

View file

@ -11,10 +11,6 @@ model_list:
- model_name: "bedrock-nova"
litellm_params:
model: us.amazon.nova-pro-v1:0
- model_name: "gemini-2.0-flash"
litellm_params:
model: gemini/gemini-2.0-flash
api_key: os.environ/GEMINI_API_KEY
- model_name: openrouter_model
litellm_params:
model: openrouter/openrouter_model
@ -33,6 +29,7 @@ model_list:
model_info:
base_model: azure/gpt-4o-realtime-preview-2024-10-01
litellm_settings:
num_retries: 0
callbacks: ["prometheus"]

View file

@ -2692,10 +2692,6 @@ class PrismaCompatibleUpdateDBModel(TypedDict, total=False):
updated_by: str
class SpecialEnums(enum.Enum):
LITELM_MANAGED_FILE_ID_PREFIX = "litellm_proxy/"
class SpecialManagementEndpointEnums(enum.Enum):
DEFAULT_ORGANIZATION = "default_organization"

View file

@ -9,10 +9,16 @@ from litellm import verbose_logger
from litellm.caching.caching import DualCache
from litellm.integrations.custom_logger import CustomLogger
from litellm.litellm_core_utils.prompt_templates.common_utils import (
extract_file_data,
get_file_ids_from_messages,
)
from litellm.proxy._types import CallTypes, SpecialEnums, UserAPIKeyAuth
from litellm.types.llms.openai import OpenAIFileObject, OpenAIFilesPurpose
from litellm.proxy._types import CallTypes, UserAPIKeyAuth
from litellm.types.llms.openai import (
CreateFileRequest,
OpenAIFileObject,
OpenAIFilesPurpose,
)
from litellm.types.utils import SpecialEnums
if TYPE_CHECKING:
from opentelemetry.trace import Span as _Span
@ -106,14 +112,21 @@ class _PROXY_LiteLLMManagedFiles(CustomLogger):
@staticmethod
async def return_unified_file_id(
file_objects: List[OpenAIFileObject],
create_file_request: CreateFileRequest,
purpose: OpenAIFilesPurpose,
internal_usage_cache: InternalUsageCache,
litellm_parent_otel_span: Span,
) -> OpenAIFileObject:
unified_file_id = SpecialEnums.LITELM_MANAGED_FILE_ID_PREFIX.value + str(
uuid.uuid4()
## GET THE FILE TYPE FROM THE CREATE FILE REQUEST
file_data = extract_file_data(create_file_request["file"])
file_type = file_data["content_type"]
unified_file_id = SpecialEnums.LITELLM_MANAGED_FILE_COMPLETE_STR.value.format(
file_type, str(uuid.uuid4())
)
## CREATE RESPONSE OBJECT
## CREATE RESPONSE OBJECT
response = OpenAIFileObject(
id=unified_file_id,

View file

@ -34,7 +34,11 @@ from litellm.proxy.common_utils.openai_endpoint_utils import (
from litellm.proxy.hooks.managed_files import _PROXY_LiteLLMManagedFiles
from litellm.proxy.utils import ProxyLogging
from litellm.router import Router
from litellm.types.llms.openai import OpenAIFileObject, OpenAIFilesPurpose
from litellm.types.llms.openai import (
CREATE_FILE_REQUESTS_PURPOSE,
OpenAIFileObject,
OpenAIFilesPurpose,
)
router = APIRouter()
@ -147,6 +151,7 @@ async def create_file_for_each_model(
responses.append(individual_response)
response = await _PROXY_LiteLLMManagedFiles.return_unified_file_id(
file_objects=responses,
create_file_request=_create_file_request,
purpose=purpose,
internal_usage_cache=proxy_logging_obj.internal_usage_cache,
litellm_parent_otel_span=user_api_key_dict.parent_otel_span,
@ -232,7 +237,7 @@ async def create_file(
# Cast purpose to OpenAIFilesPurpose type
purpose = cast(OpenAIFilesPurpose, purpose)
data = {"purpose": purpose}
data = {}
# Include original request and headers in the data
data = await add_litellm_data_to_request(
@ -258,7 +263,9 @@ async def create_file(
model=router_model, llm_router=llm_router
)
_create_file_request = CreateFileRequest(file=file_data, **data)
_create_file_request = CreateFileRequest(
file=file_data, purpose=cast(CREATE_FILE_REQUESTS_PURPOSE, purpose), **data
)
response: Optional[OpenAIFileObject] = None
if (

View file

@ -2760,9 +2760,6 @@ class Router:
stripped_model, custom_llm_provider, _, _ = get_llm_provider(
model=data["model"]
)
# kwargs["file"] = replace_model_in_jsonl(
# file_content=kwargs["file"], new_model_name=stripped_model
# )
response = litellm.acreate_file(
**{

View file

@ -288,7 +288,10 @@ class OpenAIFileObject(BaseModel):
`error` field on `fine_tuning.job`.
"""
_hidden_params: dict = {}
_hidden_params: dict = {"response_cost": 0.0} # no cost for writing a file
CREATE_FILE_REQUESTS_PURPOSE = Literal["assistants", "batch", "fine-tune"]
# OpenAI Files Types
@ -307,8 +310,8 @@ class CreateFileRequest(TypedDict, total=False):
timeout: Optional[float] = None
"""
file: FileTypes
purpose: Literal["assistants", "batch", "fine-tune"]
file: Required[FileTypes]
purpose: Required[CREATE_FILE_REQUESTS_PURPOSE]
extra_headers: Optional[Dict[str, str]]
extra_body: Optional[Dict[str, str]]
timeout: Optional[float]

View file

@ -2221,3 +2221,8 @@ class ExtractedFileData(TypedDict):
content: bytes
content_type: Optional[str]
headers: Mapping[str, str]
class SpecialEnums(Enum):
LITELM_MANAGED_FILE_ID_PREFIX = "litellm_proxy"
LITELLM_MANAGED_FILE_COMPLETE_STR = "litellm_proxy:{};unified_id,{}"

View file

@ -0,0 +1,25 @@
import json
import os
import sys
from unittest.mock import MagicMock, patch
import pytest
sys.path.insert(
0, os.path.abspath("../../..")
) # Adds the parent directory to the system path
from litellm.litellm_core_utils.prompt_templates.common_utils import (
get_format_from_file_id,
update_messages_with_model_file_ids,
)
def test_update_messages_with_model_file_ids():
unified_file_id = (
"litellm_proxy:application/pdf;unified_id,cbbe3534-8bf8-4386-af00-f5f6b7e370bf"
)
format = get_format_from_file_id(unified_file_id)
assert format == "application/pdf"