mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-29 06:33:28 +00:00
fix(bedrock): reject a malformed batch line whatever the upload's MIME type
The upload only converts, and so re-checks, batch files whose MIME type is on the allowlist. Falling back to the generic object name let a malformed file sent as application/x-jsonl skip both checks and land in S3 unchanged, so the object-name step now runs the same strict per-line parse and raises the 400.
This commit is contained in:
parent
5420ca2ed4
commit
f0eed8c56b
2 changed files with 26 additions and 12 deletions
|
|
@ -600,12 +600,11 @@ class BedrockFilesConfig(BaseAWSLLM, BaseFilesConfig):
|
||||||
## 1. If jsonl, check if there's a model name
|
## 1. If jsonl, check if there's a model name
|
||||||
file_content: Final = self._get_content_from_openai_file(extracted_file_data_content)
|
file_content: Final = self._get_content_from_openai_file(extracted_file_data_content)
|
||||||
|
|
||||||
# Split into lines and parse each line as JSON. A file that is not JSONL gets the generic name below; the
|
# Reject a malformed line here rather than falling back: the upload only converts (and so re-checks) files
|
||||||
# upload's own conversion is what rejects a malformed batch file.
|
# whose MIME type is on the batch allowlist, so for any other MIME type this is the only check.
|
||||||
with suppress(json.JSONDecodeError):
|
openai_jsonl_content: Final = _parse_batch_jsonl(file_content)
|
||||||
openai_jsonl_content: Final = [json.loads(line) for line in file_content.splitlines() if line.strip()]
|
if len(openai_jsonl_content) > 0:
|
||||||
if len(openai_jsonl_content) > 0:
|
return self._get_s3_object_name_from_batch_jsonl(openai_jsonl_content)
|
||||||
return self._get_s3_object_name_from_batch_jsonl(openai_jsonl_content)
|
|
||||||
|
|
||||||
## 2. If not jsonl, store under a server-generated managed object name
|
## 2. If not jsonl, store under a server-generated managed object name
|
||||||
filename: Final = extracted_file_data.get("filename")
|
filename: Final = extracted_file_data.get("filename")
|
||||||
|
|
|
||||||
|
|
@ -4387,10 +4387,25 @@ class TestMalformedBatchJsonl:
|
||||||
assert exc_info.value.status_code == 400
|
assert exc_info.value.status_code == 400
|
||||||
assert "line 3" in exc_info.value.message
|
assert "line 3" in exc_info.value.message
|
||||||
|
|
||||||
def test_object_name_inference_still_falls_back_for_a_non_json_file(self):
|
@pytest.mark.parametrize("content_type", ["application/jsonl", "application/x-jsonl"])
|
||||||
from litellm.llms.bedrock.files.transformation import BedrockFilesConfig
|
def test_create_file_rejects_a_malformed_line_before_uploading(self, content_type: str):
|
||||||
|
"""application/x-jsonl is not on the batch MIME allowlist, so the upload skips conversion for it; the line
|
||||||
|
check must still run so the broken file is rejected instead of landing in S3 unchanged."""
|
||||||
|
from litellm.llms.bedrock.common_utils import BedrockError
|
||||||
|
|
||||||
object_name = BedrockFilesConfig().get_object_name(
|
with respx.mock as router:
|
||||||
{"content": b"just some text, not jsonl", "filename": "notes.txt"}, purpose="batch"
|
any_request: Final = router.route()
|
||||||
)
|
with pytest.raises(BedrockError) as exc_info:
|
||||||
assert object_name.endswith("notes.txt")
|
litellm.create_file(
|
||||||
|
file=("batch.jsonl", _MALFORMED_BATCH_JSONL, content_type),
|
||||||
|
purpose="batch",
|
||||||
|
custom_llm_provider="bedrock",
|
||||||
|
model="bedrock/amazon.nova-pro-v1:0",
|
||||||
|
s3_bucket_name="litellm-batch-bucket",
|
||||||
|
aws_region_name="us-west-2",
|
||||||
|
aws_access_key_id="test-key-id",
|
||||||
|
aws_secret_access_key="test-secret",
|
||||||
|
)
|
||||||
|
assert exc_info.value.status_code == 400
|
||||||
|
assert "line 3" in exc_info.value.message
|
||||||
|
assert not any_request.called
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue