mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-21 00:21:49 +00:00
fix(mistral): read back files whose purpose Mistral never lets us upload as user_data
This commit is contained in:
parent
dd79c1f77d
commit
217ff78ae7
2 changed files with 24 additions and 5 deletions
|
|
@ -3,7 +3,8 @@ Mistral Files API. Reference: https://docs.mistral.ai/api/#tag/files
|
|||
|
||||
Mistral's file objects already carry the OpenAI field names (id, bytes, created_at,
|
||||
filename, purpose), so this config is URL routing, auth, and a purpose mapping:
|
||||
Mistral only accepts ``fine-tune``, ``batch`` and ``ocr`` as upload purposes.
|
||||
Mistral only accepts ``fine-tune``, ``batch`` and ``ocr`` as upload purposes, while files
|
||||
other Mistral products created read back with purposes outside that set and map onto ``user_data``.
|
||||
"""
|
||||
|
||||
import time
|
||||
|
|
@ -34,9 +35,10 @@ from ..common_utils import get_mistral_api_base, get_mistral_auth_headers, mistr
|
|||
|
||||
MistralFilePurpose: TypeAlias = Literal["fine-tune", "batch", "ocr"]
|
||||
|
||||
_OPENAI_PURPOSE_BY_MISTRAL: Final[Mapping[MistralFilePurpose, OpenAIFilesPurpose]] = MappingProxyType(
|
||||
_OPENAI_PURPOSE_BY_MISTRAL: Final[Mapping[str, OpenAIFilesPurpose]] = MappingProxyType(
|
||||
{"fine-tune": "fine-tune", "batch": "batch", "ocr": "user_data"}
|
||||
)
|
||||
_OPENAI_PURPOSE_FOR_UNMAPPED: Final[OpenAIFilesPurpose] = "user_data"
|
||||
_MISTRAL_PURPOSE_BY_OPENAI: Final[Mapping[str, MistralFilePurpose]] = MappingProxyType(
|
||||
{"fine-tune": "fine-tune", "batch": "batch", "ocr": "ocr", "user_data": "ocr"}
|
||||
)
|
||||
|
|
@ -59,7 +61,7 @@ class MistralFile(BaseModel):
|
|||
bytes: int = 0
|
||||
created_at: int | None = None
|
||||
filename: str = ""
|
||||
purpose: MistralFilePurpose = "batch"
|
||||
purpose: str = "batch"
|
||||
expires_at: int | None = None
|
||||
|
||||
|
||||
|
|
@ -89,8 +91,8 @@ def _to_openai_file_object(file: MistralFile) -> OpenAIFileObject:
|
|||
)
|
||||
|
||||
|
||||
def _to_openai_purpose(purpose: MistralFilePurpose) -> OpenAIFilesPurpose:
|
||||
return _OPENAI_PURPOSE_BY_MISTRAL[purpose]
|
||||
def _to_openai_purpose(purpose: str) -> OpenAIFilesPurpose:
|
||||
return _OPENAI_PURPOSE_BY_MISTRAL.get(purpose, _OPENAI_PURPOSE_FOR_UNMAPPED)
|
||||
|
||||
|
||||
def _to_mistral_purpose(purpose: str) -> MistralFilePurpose:
|
||||
|
|
|
|||
|
|
@ -158,6 +158,23 @@ def test_file_response_with_ocr_purpose_maps_onto_user_data(config):
|
|||
assert obj.expires_at == 1_800_000_000
|
||||
|
||||
|
||||
@pytest.mark.parametrize("purpose", ["playground", "audio", "code_interpreter"])
|
||||
def test_files_with_purposes_mistral_never_lets_us_upload_still_read_back(config, purpose):
|
||||
"""Regression: Mistral's live API returns purposes its upload endpoint rejects for files
|
||||
other Mistral products created, and both the unfiltered list and a retrieve of such a file
|
||||
used to fail validation, so one playground file 500'd ``GET /v1/files`` for the whole key."""
|
||||
retrieved = config.transform_retrieve_file_response(
|
||||
raw_response=_response(_file(purpose=purpose)), logging_obj=None, litellm_params={}
|
||||
)
|
||||
assert retrieved.purpose == "user_data"
|
||||
listed = config.transform_list_files_response(
|
||||
raw_response=_response({"data": [_file(purpose=purpose), _file(id="second")], "object": "list", "total": 2}),
|
||||
logging_obj=None,
|
||||
litellm_params={},
|
||||
)
|
||||
assert [(f.id, f.purpose) for f in listed] == [(FILE_ID, "user_data"), ("second", "batch")]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"method,suffix",
|
||||
[
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue