Use audio content for caching

This commit is contained in:
Sameer Kankute 2025-12-08 19:31:22 +05:30
parent b83bc10562
commit f486fb2283
3 changed files with 114 additions and 1 deletions

View file

@ -2,6 +2,7 @@
Utils used for litellm.transcription() and litellm.atranscription()
"""
import hashlib
import os
from dataclasses import dataclass
from typing import Optional
@ -127,6 +128,67 @@ def get_audio_file_name(file_obj: FileTypes) -> str:
return repr(file_obj)
def get_audio_file_content_hash(file_obj: FileTypes) -> str:
"""
Compute SHA-256 hash of audio file content for cache keys.
Falls back to filename hash if content extraction fails.
"""
file_content: Optional[bytes] = None
fallback_filename: Optional[str] = None
if isinstance(file_obj, tuple):
if len(file_obj) < 2:
fallback_filename = str(file_obj[0]) if len(file_obj) > 0 else None
else:
fallback_filename = str(file_obj[0]) if file_obj[0] is not None else None
file_content_obj = file_obj[1]
else:
file_content_obj = file_obj
fallback_filename = get_audio_file_name(file_obj)
try:
if isinstance(file_content_obj, (bytes, bytearray)):
file_content = bytes(file_content_obj)
elif isinstance(file_content_obj, (str, os.PathLike)):
try:
with open(str(file_content_obj), "rb") as f:
file_content = f.read()
if fallback_filename is None:
fallback_filename = str(file_content_obj)
except (OSError, IOError):
fallback_filename = str(file_content_obj)
file_content = None
elif hasattr(file_content_obj, "read"):
try:
current_position = file_content_obj.tell() if hasattr(file_content_obj, "tell") else None
if hasattr(file_content_obj, "seek"):
file_content_obj.seek(0)
file_content = file_content_obj.read() # type: ignore
if current_position is not None and hasattr(file_content_obj, "seek"):
file_content_obj.seek(current_position) # type: ignore
except (OSError, IOError, AttributeError):
file_content = None
else:
file_content = None
except Exception:
file_content = None
if file_content is not None and isinstance(file_content, bytes):
try:
hash_object = hashlib.sha256(file_content)
return hash_object.hexdigest()
except Exception:
pass
if fallback_filename:
hash_object = hashlib.sha256(fallback_filename.encode('utf-8'))
return hash_object.hexdigest()
file_obj_str = str(file_obj)
hash_object = hashlib.sha256(file_obj_str.encode('utf-8'))
return hash_object.hexdigest()
def get_audio_file_for_health_check() -> FileTypes:
"""
Get an audio file for health check

View file

@ -790,7 +790,7 @@ def function_setup( # noqa: PLR0915
):
_file_obj: FileTypes = args[1] if len(args) > 1 else kwargs["file"]
file_checksum = (
litellm.litellm_core_utils.audio_utils.utils.get_audio_file_name(
litellm.litellm_core_utils.audio_utils.utils.get_audio_file_content_hash(
file_obj=_file_obj
)
)
@ -7346,6 +7346,8 @@ class ProviderConfigManager:
return litellm.NvidiaNimRerankConfig()
elif litellm.LlmProviders.VERTEX_AI == provider:
return litellm.VertexAIRerankConfig()
elif litellm.LlmProviders.FIREWORKS_AI == provider:
return litellm.FireworksAIRerankConfig()
return litellm.CohereRerankConfig()
@staticmethod

View file

@ -12,6 +12,7 @@ import pytest
from litellm.litellm_core_utils.audio_utils.utils import (
ProcessedAudioFile,
calculate_request_duration,
get_audio_file_content_hash,
get_audio_file_for_health_check,
get_audio_file_name,
process_audio_file,
@ -263,3 +264,51 @@ class TestCalculateRequestDuration:
assert file_obj.tell() == len(
wav_header
), "File position should be restored to original position"
class TestGetAudioFileContentHash:
"""Test the get_audio_file_content_hash function for cache key generation"""
def test_different_content_same_filename_different_hash(self):
"""Test that different content with same filename produces different hashes"""
content1 = b"audio content 1"
content2 = b"audio content 2"
filename = "test.mp3"
hash1 = get_audio_file_content_hash((filename, content1))
hash2 = get_audio_file_content_hash((filename, content2))
assert hash1 != hash2, "Different content should produce different hashes"
def test_same_content_same_hash(self):
"""Test that same content produces same hash"""
content = b"same audio content"
filename1 = "test1.mp3"
filename2 = "test2.mp3"
hash1 = get_audio_file_content_hash((filename1, content))
hash2 = get_audio_file_content_hash((filename2, content))
assert hash1 == hash2, "Same content should produce same hash regardless of filename"
def test_bytes_input(self):
"""Test that raw bytes input works"""
content = b"raw bytes content"
hash1 = get_audio_file_content_hash(content)
hash2 = get_audio_file_content_hash(content)
assert hash1 == hash2, "Same bytes should produce same hash"
assert len(hash1) == 64, "SHA-256 hash should be 64 characters"
def test_fallback_to_filename(self):
"""Test that function falls back to filename when content extraction fails"""
# Use a non-readable object that will trigger fallback
class UnreadableFile:
def __init__(self, name):
self.name = name
file_obj = UnreadableFile("test.mp3")
hash_result = get_audio_file_content_hash(file_obj)
assert isinstance(hash_result, str)
assert len(hash_result) == 64, "Should return valid hash even on fallback"