diff --git a/docs/my-website/docs/container_files.md b/docs/my-website/docs/container_files.md index 25b58a043c8..1ef7687ea77 100644 --- a/docs/my-website/docs/container_files.md +++ b/docs/my-website/docs/container_files.md @@ -21,6 +21,7 @@ Looking for how to use Code Interpreter? See the [Code Interpreter Guide](/docs/ | Endpoint | Method | Description | |----------|--------|-------------| +| `/v1/containers/{container_id}/files` | POST | Upload file to container | | `/v1/containers/{container_id}/files` | GET | List files in container | | `/v1/containers/{container_id}/files/{file_id}` | GET | Get file metadata | | `/v1/containers/{container_id}/files/{file_id}/content` | GET | Download file content | @@ -28,6 +29,45 @@ Looking for how to use Code Interpreter? See the [Code Interpreter Guide](/docs/ ## LiteLLM Python SDK +### Upload Container File + +Upload files directly to a container session. This is useful when `/chat/completions` or `/responses` sends files to the container but the input file type is limited to PDF. This endpoint lets you work with other file types like CSV, Excel, Python scripts, etc. + +```python showLineNumbers title="upload_container_file.py" +from litellm import upload_container_file + +# Upload a CSV file +file = upload_container_file( + container_id="cntr_123...", + file=("data.csv", open("data.csv", "rb").read(), "text/csv"), + custom_llm_provider="openai" +) + +print(f"Uploaded: {file.id}") +print(f"Path: {file.path}") +``` + +**Async:** + +```python showLineNumbers title="aupload_container_file.py" +from litellm import aupload_container_file + +file = await aupload_container_file( + container_id="cntr_123...", + file=("script.py", b"print('hello world')", "text/x-python"), + custom_llm_provider="openai" +) +``` + +**Supported file formats:** +- CSV (`.csv`) +- Excel (`.xlsx`) +- Python scripts (`.py`) +- JSON (`.json`) +- Markdown (`.md`) +- Text files (`.txt`) +- And more... + ### List Container Files ```python showLineNumbers title="list_container_files.py" @@ -103,6 +143,40 @@ print(f"Deleted: {result.deleted}") import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; +### Upload File + + + + +```python showLineNumbers title="upload_file.py" +from openai import OpenAI + +client = OpenAI( + api_key="sk-1234", + base_url="http://localhost:4000" +) + +file = client.containers.files.create( + container_id="cntr_123...", + file=open("data.csv", "rb") +) + +print(f"Uploaded: {file.id}") +print(f"Path: {file.path}") +``` + + + + +```bash showLineNumbers title="upload_file.sh" +curl "http://localhost:4000/v1/containers/cntr_123.../files" \ + -H "Authorization: Bearer sk-1234" \ + -F file="@data.csv" +``` + + + + ### List Files @@ -236,6 +310,13 @@ curl -X DELETE "http://localhost:4000/v1/containers/cntr_123.../files/cfile_456. ## Parameters +### Upload File + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `container_id` | string | Yes | Container ID | +| `file` | FileTypes | Yes | File to upload. Can be a tuple of (filename, content, content_type), file-like object, or bytes | + ### List Files | Parameter | Type | Required | Description | diff --git a/litellm/llms/custom_httpx/container_handler.py b/litellm/llms/custom_httpx/container_handler.py index ed112e4dd58..73017eaaf30 100644 --- a/litellm/llms/custom_httpx/container_handler.py +++ b/litellm/llms/custom_httpx/container_handler.py @@ -88,6 +88,34 @@ def _build_query_params( return params +def _prepare_multipart_file_upload( + file: Any, + headers: Dict[str, Any], +) -> tuple: + """ + Prepare file and headers for multipart upload. + + Returns: + Tuple of (files_dict, headers_without_content_type) + """ + from litellm.litellm_core_utils.prompt_templates.common_utils import ( + extract_file_data, + ) + + extracted = extract_file_data(file) + filename = extracted.get("filename") or "file" + content = extracted.get("content") or b"" + content_type = extracted.get("content_type") or "application/octet-stream" + files = {"file": (filename, content, content_type)} + + # Remove content-type header - httpx will set it automatically for multipart + headers_copy = headers.copy() + headers_copy.pop("content-type", None) + headers_copy.pop("Content-Type", None) + + return files, headers_copy + + class GenericContainerHandler: """ Generic handler for container file API endpoints. @@ -210,6 +238,7 @@ class GenericContainerHandler: # Make request method = endpoint_config["method"].upper() returns_binary = endpoint_config.get("returns_binary", False) + is_multipart = endpoint_config.get("is_multipart", False) try: if method == "GET": @@ -217,7 +246,11 @@ class GenericContainerHandler: elif method == "DELETE": response = http_client.delete(url=url, headers=headers, params=query_params) elif method == "POST": - response = http_client.post(url=url, headers=headers, params=query_params) + if is_multipart and "file" in kwargs: + files, headers = _prepare_multipart_file_upload(kwargs["file"], headers) + response = http_client.post(url=url, headers=headers, params=query_params, files=files) + else: + response = http_client.post(url=url, headers=headers, params=query_params) else: raise ValueError(f"Unsupported HTTP method: {method}") @@ -307,6 +340,7 @@ class GenericContainerHandler: # Make request method = endpoint_config["method"].upper() returns_binary = endpoint_config.get("returns_binary", False) + is_multipart = endpoint_config.get("is_multipart", False) try: if method == "GET": @@ -314,7 +348,11 @@ class GenericContainerHandler: elif method == "DELETE": response = await http_client.delete(url=url, headers=headers, params=query_params) elif method == "POST": - response = await http_client.post(url=url, headers=headers, params=query_params) + if is_multipart and "file" in kwargs: + files, headers = _prepare_multipart_file_upload(kwargs["file"], headers) + response = await http_client.post(url=url, headers=headers, params=query_params, files=files) + else: + response = await http_client.post(url=url, headers=headers, params=query_params) else: raise ValueError(f"Unsupported HTTP method: {method}") diff --git a/provider_endpoints_support.json b/provider_endpoints_support.json index bc5dea7b97c..617e9d1e3d5 100644 --- a/provider_endpoints_support.json +++ b/provider_endpoints_support.json @@ -1515,7 +1515,7 @@ "list_containers": true, "retrieve_container": true, "delete_container": true, - "create_container_file": false, + "create_container_file": true, "list_container_files": true, "retrieve_container_file": true, "retrieve_container_file_content": true,