litellm/litellm/containers
Mateo Wang 130699ca23
refactor(typing): clear 1143 basedpyright Any errors across 273 files (#46574)
* refactor(typing): narrow Any sources in proxy, guardrails, bedrock and responses code

Turns untyped json.loads, response.json(), header lookups and forwarded
kwargs into object or precise types at their source, so downstream code
type-checks without reportAny / reportExplicitAny. No runtime behavior
changes.

* refactor(typing): narrow Any sources in caching, MCP, providers and proxy request code

Decoded JSON, cache reads and header lookups now enter as object or
precise types, prisma tables used by the shadow-eval logger get typed
Protocol views, and a few SDK parameters move from dict[str, Any] to
Mapping[str, object]. No runtime behavior changes.

* refactor(typing): narrow Any sources in proxy server, router, logging and shared utils

* refactor(typing): narrow Any sources in provider transformations, integrations and proxy endpoints

* refactor(typing): narrow Any sources across providers, proxy and router

Deepens the Any cleanup on the hottest files: typed helpers in
core_helpers for attribute and item access, identity TypeAdapter(object)
wraps at untyped boundaries, explicit return annotations on shared
provider and proxy helpers, and Protocols in place of Any parameters.
No intended behavior change.
2026-10-15 21:08:47 +00:00
..
__init__.py chore(lint): clear grandfathered over-limit lint drift and ratchet budgets down 2026-08-05 12:18:13 -07:00
endpoint_factory.py refactor(typing): clear 1143 basedpyright Any errors across 273 files (#46574) 2026-10-15 21:08:47 +00:00
endpoints.json [Feat] Litellm new endpoint add container file upload (#18743) 2026-01-07 13:36:55 +05:30
main.py fix(proxy): route container create and list through model_list deployments 2026-09-01 23:26:23 +00:00
README.md test: finish the non-proxy half of tests/test_litellm (#43281) 2026-09-25 22:43:41 -07:00
utils.py refactor(typing): clear 1143 basedpyright Any errors across 273 files (#46574) 2026-10-15 21:08:47 +00:00

Container Files API

This module provides a unified interface for container file operations across multiple LLM providers (OpenAI, Azure OpenAI, etc.).

Architecture

endpoints.json                    # Declarative endpoint definitions
       ↓
endpoint_factory.py              # Auto-generates SDK functions
       ↓
container_handler.py             # Generic HTTP handler
       ↓
BaseContainerConfig              # Provider-specific transformations
├── OpenAIContainerConfig
└── AzureContainerConfig (example)

Files Overview

File Purpose
endpoints.json Single source of truth - Defines all container file endpoints
endpoint_factory.py Auto-generates SDK functions (list_container_files, etc.)
main.py Core container operations (create, list, retrieve, delete containers)
utils.py Request parameter utilities

Adding a New Endpoint

To add a new container file endpoint (e.g., get_container_file_content):

Step 1: Add to endpoints.json

{
  "name": "get_container_file_content",
  "async_name": "aget_container_file_content",
  "path": "/containers/{container_id}/files/{file_id}/content",
  "method": "GET",
  "path_params": ["container_id", "file_id"],
  "query_params": [],
  "response_type": "ContainerFileContentResponse"
}

Step 2: Add Response Type (if new)

In litellm/types/containers/main.py:

class ContainerFileContentResponse(BaseModel):
    """Response for file content download."""
    content: bytes
    # ... other fields

Step 3: Register Response Type

In litellm/llms/custom_httpx/container_handler.py, add to RESPONSE_TYPES:

RESPONSE_TYPES = {
    # ... existing types
    "ContainerFileContentResponse": ContainerFileContentResponse,
}

Step 4: Update Router (one-time setup)

In litellm/router.py, add the call_type to the factory_function Literal and _init_containers_api_endpoints condition.

In litellm/proxy/route_llm_request.py, add to the route mappings and skip-model-routing lists.

Step 5: Update Proxy Handler Factory (if new path params)

If your endpoint has a new combination of path parameters, add a handler in litellm/proxy/container_endpoints/handler_factory.py:

elif path_params == ["container_id", "file_id", "new_param"]:
    async def handler(...):
        # handler implementation

Adding a New Provider (e.g., Azure OpenAI)

Step 1: Create Provider Config

Create litellm/llms/azure/containers/transformation.py:

from typing import Dict, Optional, Tuple, Any
import httpx

from litellm.llms.base_llm.containers.transformation import BaseContainerConfig
from litellm.types.containers.main import (
    ContainerFileListResponse,
    ContainerFileObject,
    DeleteContainerFileResponse,
)
from litellm.types.router import GenericLiteLLMParams
from litellm.secret_managers.main import get_secret_str


class AzureContainerConfig(BaseContainerConfig):
    """Configuration class for Azure OpenAI container API."""

    def get_supported_openai_params(self) -> list:
        return ["name", "expires_after", "file_ids", "extra_headers"]

    def map_openai_params(
        self,
        container_create_optional_params,
        drop_params: bool,
    ) -> Dict:
        return dict(container_create_optional_params)

    def validate_environment(
        self,
        headers: dict,
        api_key: Optional[str] = None,
    ) -> dict:
        """Azure uses api-key header instead of Bearer token."""
        import litellm
        
        api_key = (
            api_key
            or litellm.azure_key
            or get_secret_str("AZURE_API_KEY")
        )
        headers["api-key"] = api_key
        return headers

    def get_complete_url(
        self,
        api_base: Optional[str],
        litellm_params: dict,
    ) -> str:
        """
        Azure format: 
        https://{resource}.openai.azure.com/openai/containers?api-version=2024-xx
        """
        if api_base is None:
            raise ValueError("api_base is required for Azure")
        
        api_version = litellm_params.get("api_version", "2024-02-15-preview")
        return f"{api_base.rstrip('/')}/openai/containers?api-version={api_version}"

    # Implement remaining abstract methods from BaseContainerConfig:
    # - transform_container_create_request
    # - transform_container_create_response
    # - transform_container_list_request
    # - transform_container_list_response
    # - transform_container_retrieve_request
    # - transform_container_retrieve_response
    # - transform_container_delete_request
    # - transform_container_delete_response
    # - transform_container_file_list_request
    # - transform_container_file_list_response

Step 2: Register Provider Config

In litellm/utils.py, find ProviderConfigManager.get_provider_container_config() and add:

@staticmethod
def get_provider_container_config(
    provider: LlmProviders,
) -> Optional[BaseContainerConfig]:
    if provider == LlmProviders.OPENAI:
        from litellm.llms.openai.containers.transformation import OpenAIContainerConfig
        return OpenAIContainerConfig()
    elif provider == LlmProviders.AZURE:
        from litellm.llms.azure.containers.transformation import AzureContainerConfig
        return AzureContainerConfig()
    return None

Step 3: Test the New Provider

# Create container via Azure
curl -X POST "http://localhost:4000/v1/containers" \
    -H "Authorization: Bearer <your-master-key>" \
    -H "custom-llm-provider: azure" \
    -H "Content-Type: application/json" \
    -d '{"name": "My Azure Container"}'

# List container files via Azure
curl -X GET "http://localhost:4000/v1/containers/cntr_123/files" \
    -H "Authorization: Bearer <your-master-key>" \
    -H "custom-llm-provider: azure"

How Provider Selection Works

  1. Proxy receives request with custom-llm-provider header/query/body
  2. Router calls ProviderConfigManager.get_provider_container_config(provider)
  3. Generic handler uses the provider config for:
    • URL construction (get_complete_url)
    • Authentication (validate_environment)
    • Request/response transformation

Testing

Run the container API tests:

cd /Users/ishaanjaffer/github/litellm
python -m pytest tests/unit/containers/ -v

Test via proxy:

# Start proxy (proxy_config.yaml reads its master key from LITELLM_MASTER_KEY)
export LITELLM_MASTER_KEY="sk-$(openssl rand -hex 32)"
cd litellm/proxy && python proxy_cli.py --config proxy_config.yaml --port 4000

# Test endpoints
curl -X GET "http://localhost:4000/v1/containers/cntr_123/files" \
    -H "Authorization: Bearer $LITELLM_MASTER_KEY"

Endpoint Reference

Endpoint Method Path
List container files GET /v1/containers/{container_id}/files
Retrieve container file GET /v1/containers/{container_id}/files/{file_id}
Delete container file DELETE /v1/containers/{container_id}/files/{file_id}

See endpoints.json for the complete list.