"""
Main File for Files API implementation

https://platform.openai.com/docs/api-reference/files

"""

import asyncio
import contextvars
import time
import uuid as uuid_module
from functools import partial
from types import MappingProxyType
from typing import Any, Coroutine, Dict, Literal, Optional, Union, cast

import httpx

# Type aliases for provider parameters
FileCreateProvider = Literal[
    "openai",
    "azure",
    "gemini",
    "vertex_ai",
    "bedrock",
    "hosted_vllm",
    "manus",
    "anthropic",
]
FileRetrieveProvider = Literal[
    "openai", "azure", "gemini", "vertex_ai", "hosted_vllm", "manus", "anthropic"
]
FileDeleteProvider = Literal["openai", "azure", "gemini", "manus", "anthropic"]
FileListProvider = Literal["openai", "azure", "manus", "anthropic"]
import litellm
from litellm import get_secret_str
from litellm.files.streaming import FileContentStreamingResponse
from litellm.files.types import FileContentProvider, FileContentStreamingResult
from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider
from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj
from litellm.llms.azure.common_utils import get_azure_credentials
from litellm.llms.azure.files.handler import AzureOpenAIFilesAPI
from litellm.llms.bedrock.files.handler import BedrockFilesHandler
from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler
from litellm.llms.custom_httpx.llm_http_handler import BaseLLMHTTPHandler
from litellm.llms.openai.common_utils import get_openai_credentials
from litellm.llms.openai.openai import FileDeleted, FileObject, OpenAIFilesAPI
from litellm.llms.vertex_ai.files.handler import VertexAIFilesHandler
from litellm.types.llms.openai import (
    CreateFileRequest,
    FileContentRequest,
    FileExpiresAfter,
    FileTypes,
    HttpxBinaryResponseContent,
    OpenAIFileObject,
)
from litellm.types.router import *
from litellm.types.utils import (
    OPENAI_COMPATIBLE_BATCH_AND_FILES_PROVIDERS,
    LlmProviders,
)
from litellm.utils import (
    ProviderConfigManager,
    client,
    get_litellm_params,
    supports_httpx_timeout,
)

base_llm_http_handler = BaseLLMHTTPHandler()

####### ENVIRONMENT VARIABLES ###################


def _should_sdk_support_streaming(
    custom_llm_provider: Optional[Union[FileContentProvider, str]],
) -> bool:
    """
    Return whether file content streaming is supported for the provider.
    """
    return custom_llm_provider in OPENAI_COMPATIBLE_BATCH_AND_FILES_PROVIDERS


openai_files_instance = OpenAIFilesAPI()
azure_files_instance = AzureOpenAIFilesAPI()
vertex_ai_files_instance = VertexAIFilesHandler()
bedrock_files_instance = BedrockFilesHandler()
#################################################


def _add_trusted_model_credentials_to_litellm_params(
    litellm_params_dict: Dict[str, Any], kwargs: Dict[str, Any]
) -> None:
    trusted_model_credentials = kwargs.get("_litellm_internal_model_credentials")
    if isinstance(trusted_model_credentials, type(MappingProxyType({}))):
        litellm_params_dict["_litellm_internal_model_credentials"] = (
            trusted_model_credentials
        )


@client
async def acreate_file(
    file: FileTypes,
    purpose: Literal["assistants", "batch", "fine-tune", "messages"],
    expires_after: Optional[FileExpiresAfter] = None,
    custom_llm_provider: FileCreateProvider = "openai",
    extra_headers: Optional[Dict[str, str]] = None,
    extra_body: Optional[Dict[str, str]] = None,
    **kwargs,
) -> OpenAIFileObject:
    """
    Async: Files are used to upload documents that can be used with features like Assistants, Fine-tuning, and Batch API.

    LiteLLM Equivalent of POST: POST https://api.openai.com/v1/files
    """
    try:
        loop = asyncio.get_event_loop()
        kwargs["acreate_file"] = True

        call_args = {
            "file": file,
            "purpose": purpose,
            "expires_after": expires_after,
            "custom_llm_provider": custom_llm_provider,
            "extra_headers": extra_headers,
            "extra_body": extra_body,
            **kwargs,
        }

        # Use a partial function to pass your keyword arguments
        func = partial(create_file, **call_args)
        # Add the context to the function
        ctx = contextvars.copy_context()
        func_with_context = partial(ctx.run, func)
        init_response = await loop.run_in_executor(None, func_with_context)
        if asyncio.iscoroutine(init_response):
            response = await init_response
        else:
            response = init_response  # type: ignore

        return response
    except Exception as e:
        raise e


@client
def create_file(
    file: FileTypes,
    purpose: Literal["assistants", "batch", "fine-tune", "messages"],
    expires_after: Optional[FileExpiresAfter] = None,
    custom_llm_provider: Optional[FileCreateProvider] = None,
    extra_headers: Optional[Dict[str, str]] = None,
    extra_body: Optional[Dict[str, str]] = None,
    **kwargs,
) -> Union[OpenAIFileObject, Coroutine[Any, Any, OpenAIFileObject]]:
    """
    Files are used to upload documents that can be used with features like Assistants, Fine-tuning, and Batch API.

    LiteLLM Equivalent of POST: POST https://api.openai.com/v1/files

    Specify either provider_list or custom_llm_provider.
    """
    try:
        _is_async = kwargs.pop("acreate_file", False) is True
        optional_params = GenericLiteLLMParams(**kwargs)
        litellm_params_dict = dict(**kwargs)
        logging_obj = cast(
            Optional[LiteLLMLoggingObj], kwargs.get("litellm_logging_obj")
        )
        if logging_obj is None:
            raise ValueError("logging_obj is required")
        client = kwargs.get("client")

        ### TIMEOUT LOGIC ###
        timeout = optional_params.timeout or kwargs.get("request_timeout", 600) or 600
        # set timeout for 10 minutes by default

        if (
            timeout is not None
            and isinstance(timeout, httpx.Timeout)
            and supports_httpx_timeout(cast(str, custom_llm_provider)) is False
        ):
            read_timeout = timeout.read or 600
            timeout = read_timeout  # default 10 min timeout
        elif timeout is not None and not isinstance(timeout, httpx.Timeout):
            timeout = float(timeout)  # type: ignore
        elif timeout is None:
            timeout = 600.0

        if expires_after is not None:
            _create_file_request = CreateFileRequest(
                file=file,
                purpose=purpose,
                expires_after=expires_after,
                extra_headers=extra_headers,
                extra_body=extra_body,
            )
        else:
            _create_file_request = CreateFileRequest(
                file=file,
                purpose=purpose,
                extra_headers=extra_headers,
                extra_body=extra_body,
            )

        provider_config = ProviderConfigManager.get_provider_files_config(
            model="",
            provider=LlmProviders(custom_llm_provider),
        )
        if provider_config is not None:
            response = base_llm_http_handler.create_file(
                provider_config=provider_config,
                litellm_params=litellm_params_dict,
                create_file_data=_create_file_request,
                headers=extra_headers or {},
                api_base=optional_params.api_base,
                api_key=optional_params.api_key,
                logging_obj=logging_obj,
                _is_async=_is_async,
                client=(
                    client
                    if client is not None
                    and isinstance(client, (HTTPHandler, AsyncHTTPHandler))
                    else None
                ),
                timeout=timeout,
            )
        elif custom_llm_provider in OPENAI_COMPATIBLE_BATCH_AND_FILES_PROVIDERS:
            openai_creds = get_openai_credentials(
                api_base=optional_params.api_base,
                api_key=optional_params.api_key,
                organization=optional_params.organization,
            )
            response = openai_files_instance.create_file(
                _is_async=_is_async,
                api_base=openai_creds.api_base,
                api_key=openai_creds.api_key,
                timeout=timeout,
                max_retries=optional_params.max_retries,
                organization=openai_creds.organization,
                create_file_data=_create_file_request,
            )
        elif custom_llm_provider == "azure":
            azure_creds = get_azure_credentials(
                api_base=optional_params.api_base,
                api_key=optional_params.api_key,
                api_version=optional_params.api_version,
            )
            response = azure_files_instance.create_file(
                _is_async=_is_async,
                api_base=azure_creds.api_base,
                api_key=azure_creds.api_key,
                api_version=azure_creds.api_version,
                timeout=timeout,
                max_retries=optional_params.max_retries,
                create_file_data=_create_file_request,
                litellm_params=litellm_params_dict,
            )
        else:
            raise litellm.exceptions.BadRequestError(
                message="LiteLLM doesn't support {} for 'create_file'. Only ['openai', 'azure', 'vertex_ai', 'manus', 'anthropic'] are supported.".format(
                    custom_llm_provider
                ),
                model="n/a",
                llm_provider=custom_llm_provider,
                response=httpx.Response(
                    status_code=400,
                    content="Unsupported provider",
                    request=httpx.Request(method="create_file", url="https://github.com/BerriAI/litellm"),  # type: ignore
                ),
            )
        return response
    except Exception as e:
        raise e


@client
async def afile_retrieve(
    file_id: str,
    custom_llm_provider: FileRetrieveProvider = "openai",
    extra_headers: Optional[Dict[str, str]] = None,
    extra_body: Optional[Dict[str, str]] = None,
    **kwargs,
) -> OpenAIFileObject:
    """
    Async: Get file contents

    LiteLLM Equivalent of GET https://api.openai.com/v1/files
    """
    try:
        loop = asyncio.get_event_loop()
        kwargs["is_async"] = True

        # Use a partial function to pass your keyword arguments
        func = partial(
            file_retrieve,
            file_id,
            custom_llm_provider,
            extra_headers,
            extra_body,
            **kwargs,
        )

        # Add the context to the function
        ctx = contextvars.copy_context()
        func_with_context = partial(ctx.run, func)
        init_response = await loop.run_in_executor(None, func_with_context)
        if asyncio.iscoroutine(init_response):
            response = await init_response
        else:
            response = init_response

        return OpenAIFileObject(**response.model_dump())
    except Exception as e:
        raise e


@client
def file_retrieve(
    file_id: str,
    custom_llm_provider: FileRetrieveProvider = "openai",
    extra_headers: Optional[Dict[str, str]] = None,
    extra_body: Optional[Dict[str, str]] = None,
    **kwargs,
) -> FileObject:
    """
    Returns the contents of the specified file.

    LiteLLM Equivalent of POST: POST https://api.openai.com/v1/files
    """
    try:
        optional_params = GenericLiteLLMParams(**kwargs)
        ### TIMEOUT LOGIC ###
        timeout = optional_params.timeout or kwargs.get("request_timeout", 600) or 600
        # set timeout for 10 minutes by default

        if (
            timeout is not None
            and isinstance(timeout, httpx.Timeout)
            and supports_httpx_timeout(custom_llm_provider) is False
        ):
            read_timeout = timeout.read or 600
            timeout = read_timeout  # default 10 min timeout
        elif timeout is not None and not isinstance(timeout, httpx.Timeout):
            timeout = float(timeout)  # type: ignore
        elif timeout is None:
            timeout = 600.0

        _is_async = kwargs.pop("is_async", False) is True

        if custom_llm_provider in OPENAI_COMPATIBLE_BATCH_AND_FILES_PROVIDERS:
            openai_creds = get_openai_credentials(
                api_base=optional_params.api_base,
                api_key=optional_params.api_key,
                organization=optional_params.organization,
            )
            response = openai_files_instance.retrieve_file(
                file_id=file_id,
                _is_async=_is_async,
                api_base=openai_creds.api_base,
                api_key=openai_creds.api_key,
                timeout=timeout,
                max_retries=optional_params.max_retries,
                organization=openai_creds.organization,
            )
        elif custom_llm_provider == "azure":
            azure_creds = get_azure_credentials(
                api_base=optional_params.api_base,
                api_key=optional_params.api_key,
                api_version=optional_params.api_version,
            )
            response = azure_files_instance.retrieve_file(
                _is_async=_is_async,
                api_base=azure_creds.api_base,
                api_key=azure_creds.api_key,
                api_version=azure_creds.api_version,
                timeout=timeout,
                max_retries=optional_params.max_retries,
                file_id=file_id,
            )
        else:
            # Try using provider config pattern (for Manus, Bedrock, etc.)
            provider_config = ProviderConfigManager.get_provider_files_config(
                model="",
                provider=LlmProviders(custom_llm_provider),
            )
            if provider_config is not None:
                litellm_params_dict = get_litellm_params(**kwargs)
                _add_trusted_model_credentials_to_litellm_params(
                    litellm_params_dict=litellm_params_dict,
                    kwargs=kwargs,
                )
                litellm_params_dict["api_key"] = optional_params.api_key
                litellm_params_dict["api_base"] = optional_params.api_base

                logging_obj = kwargs.get("litellm_logging_obj")
                if logging_obj is None:
                    from litellm.litellm_core_utils.litellm_logging import (
                        Logging as LiteLLMLoggingObj,
                    )

                    logging_obj = LiteLLMLoggingObj(
                        model="",
                        messages=[],
                        stream=False,
                        call_type="afile_retrieve" if _is_async else "file_retrieve",
                        start_time=time.time(),
                        litellm_call_id=kwargs.get(
                            "litellm_call_id", str(uuid_module.uuid4())
                        ),
                        function_id=str(kwargs.get("id") or ""),
                    )

                client = kwargs.get("client")
                response = base_llm_http_handler.retrieve_file(
                    file_id=file_id,
                    provider_config=provider_config,
                    litellm_params=litellm_params_dict,
                    headers=extra_headers or {},
                    logging_obj=logging_obj,
                    _is_async=_is_async,
                    client=(
                        client
                        if client is not None
                        and isinstance(client, (HTTPHandler, AsyncHTTPHandler))
                        else None
                    ),
                    timeout=timeout,
                )
            else:
                raise litellm.exceptions.BadRequestError(
                    message="LiteLLM doesn't support {} for 'file_retrieve'. Only 'openai', 'azure', 'manus', and 'anthropic' are supported.".format(
                        custom_llm_provider
                    ),
                    model="n/a",
                    llm_provider=custom_llm_provider,
                    response=httpx.Response(
                        status_code=400,
                        content="Unsupported provider",
                        request=httpx.Request(method="create_thread", url="https://github.com/BerriAI/litellm"),  # type: ignore
                    ),
                )

        return cast(FileObject, response)
    except Exception as e:
        raise e


# Delete file
@client
async def afile_delete(
    file_id: str,
    custom_llm_provider: FileDeleteProvider = "openai",
    extra_headers: Optional[Dict[str, str]] = None,
    extra_body: Optional[Dict[str, str]] = None,
    **kwargs,
) -> Coroutine[Any, Any, FileObject]:
    """
    Async: Delete file

    LiteLLM Equivalent of DELETE https://api.openai.com/v1/files
    """
    try:
        loop = asyncio.get_event_loop()
        model = kwargs.pop("model", None)
        kwargs["is_async"] = True

        # Use a partial function to pass your keyword arguments
        func = partial(
            file_delete,
            file_id,
            model,
            custom_llm_provider,
            extra_headers,
            extra_body,
            **kwargs,
        )

        # Add the context to the function
        ctx = contextvars.copy_context()
        func_with_context = partial(ctx.run, func)
        init_response = await loop.run_in_executor(None, func_with_context)
        if asyncio.iscoroutine(init_response):
            response = await init_response
        else:
            response = init_response  # type: ignore

        return cast(FileDeleted, response)  # type: ignore
    except Exception as e:
        raise e


@client
def file_delete(
    file_id: str,
    model: Optional[str] = None,
    custom_llm_provider: Union[FileDeleteProvider, str] = "openai",
    extra_headers: Optional[Dict[str, str]] = None,
    extra_body: Optional[Dict[str, str]] = None,
    **kwargs,
) -> FileDeleted:
    """
    Delete file

    LiteLLM Equivalent of DELETE https://api.openai.com/v1/files
    """
    try:
        try:
            if model is not None:
                _, custom_llm_provider, _, _ = get_llm_provider(
                    model, custom_llm_provider
                )
        except Exception:
            pass
        optional_params = GenericLiteLLMParams(**kwargs)
        litellm_params_dict = get_litellm_params(**kwargs)
        _add_trusted_model_credentials_to_litellm_params(
            litellm_params_dict=litellm_params_dict,
            kwargs=kwargs,
        )
        ### TIMEOUT LOGIC ###
        timeout = optional_params.timeout or kwargs.get("request_timeout", 600) or 600
        # set timeout for 10 minutes by default
        client = kwargs.get("client")

        if (
            timeout is not None
            and isinstance(timeout, httpx.Timeout)
            and supports_httpx_timeout(custom_llm_provider) is False
        ):
            read_timeout = timeout.read or 600
            timeout = read_timeout  # default 10 min timeout
        elif timeout is not None and not isinstance(timeout, httpx.Timeout):
            timeout = float(timeout)  # type: ignore
        elif timeout is None:
            timeout = 600.0
        _is_async = kwargs.pop("is_async", False) is True
        if custom_llm_provider in OPENAI_COMPATIBLE_BATCH_AND_FILES_PROVIDERS:
            openai_creds = get_openai_credentials(
                api_base=optional_params.api_base,
                api_key=optional_params.api_key,
                organization=optional_params.organization,
            )
            response = openai_files_instance.delete_file(
                file_id=file_id,
                _is_async=_is_async,
                api_base=openai_creds.api_base,
                api_key=openai_creds.api_key,
                timeout=timeout,
                max_retries=optional_params.max_retries,
                organization=openai_creds.organization,
            )
        elif custom_llm_provider == "azure":
            azure_creds = get_azure_credentials(
                api_base=optional_params.api_base,
                api_key=optional_params.api_key,
                api_version=optional_params.api_version,
            )
            response = azure_files_instance.delete_file(
                _is_async=_is_async,
                api_base=azure_creds.api_base,
                api_key=azure_creds.api_key,
                api_version=azure_creds.api_version,
                timeout=timeout,
                max_retries=optional_params.max_retries,
                file_id=file_id,
                client=client,
                litellm_params=litellm_params_dict,
            )
        else:
            # Try using provider config pattern (for Manus, Bedrock, etc.)
            provider_config = ProviderConfigManager.get_provider_files_config(
                model="",
                provider=LlmProviders(custom_llm_provider),
            )
            if provider_config is not None:
                litellm_params_dict["api_key"] = optional_params.api_key
                litellm_params_dict["api_base"] = optional_params.api_base

                logging_obj = kwargs.get("litellm_logging_obj")
                if logging_obj is None:
                    from litellm.litellm_core_utils.litellm_logging import (
                        Logging as LiteLLMLoggingObj,
                    )

                    logging_obj = LiteLLMLoggingObj(
                        model="",
                        messages=[],
                        stream=False,
                        call_type="afile_delete" if _is_async else "file_delete",
                        start_time=time.time(),
                        litellm_call_id=kwargs.get(
                            "litellm_call_id", str(uuid_module.uuid4())
                        ),
                        function_id=str(kwargs.get("id") or ""),
                    )

                response = base_llm_http_handler.delete_file(
                    file_id=file_id,
                    provider_config=provider_config,
                    litellm_params=litellm_params_dict,
                    headers=extra_headers or {},
                    logging_obj=logging_obj,
                    _is_async=_is_async,
                    client=(
                        client
                        if client is not None
                        and isinstance(client, (HTTPHandler, AsyncHTTPHandler))
                        else None
                    ),
                    timeout=timeout,
                )
            else:
                raise litellm.exceptions.BadRequestError(
                    message="LiteLLM doesn't support {} for 'file_delete'. Only 'openai', 'azure', 'gemini', 'manus', and 'anthropic' are supported.".format(
                        custom_llm_provider
                    ),
                    model="n/a",
                    llm_provider=custom_llm_provider,
                    response=httpx.Response(
                        status_code=400,
                        content="Unsupported provider",
                        request=httpx.Request(method="create_thread", url="https://github.com/BerriAI/litellm"),  # type: ignore
                    ),
                )
        return cast(FileDeleted, response)
    except Exception as e:
        raise e


# List files
@client
async def afile_list(
    custom_llm_provider: FileListProvider = "openai",
    purpose: Optional[str] = None,
    extra_headers: Optional[Dict[str, str]] = None,
    extra_body: Optional[Dict[str, str]] = None,
    **kwargs,
):
    """
    Async: List files

    LiteLLM Equivalent of GET https://api.openai.com/v1/files
    """
    try:
        loop = asyncio.get_event_loop()
        kwargs["is_async"] = True

        # Use a partial function to pass your keyword arguments
        func = partial(
            file_list,
            custom_llm_provider,
            purpose,
            extra_headers,
            extra_body,
            **kwargs,
        )

        # Add the context to the function
        ctx = contextvars.copy_context()
        func_with_context = partial(ctx.run, func)
        init_response = await loop.run_in_executor(None, func_with_context)
        if asyncio.iscoroutine(init_response):
            response = await init_response
        else:
            response = init_response  # type: ignore

        return response
    except Exception as e:
        raise e


@client
def file_list(
    custom_llm_provider: FileListProvider = "openai",
    purpose: Optional[str] = None,
    extra_headers: Optional[Dict[str, str]] = None,
    extra_body: Optional[Dict[str, str]] = None,
    **kwargs,
):
    """
    List files

    LiteLLM Equivalent of GET https://api.openai.com/v1/files
    """
    try:
        optional_params = GenericLiteLLMParams(**kwargs)
        ### TIMEOUT LOGIC ###
        timeout = optional_params.timeout or kwargs.get("request_timeout", 600) or 600
        # set timeout for 10 minutes by default

        if (
            timeout is not None
            and isinstance(timeout, httpx.Timeout)
            and supports_httpx_timeout(custom_llm_provider) is False
        ):
            read_timeout = timeout.read or 600
            timeout = read_timeout  # default 10 min timeout
        elif timeout is not None and not isinstance(timeout, httpx.Timeout):
            timeout = float(timeout)  # type: ignore
        elif timeout is None:
            timeout = 600.0

        _is_async = kwargs.pop("is_async", False) is True

        # Check if provider has a custom files config (e.g., Manus, Bedrock, Vertex AI)
        provider_config = ProviderConfigManager.get_provider_files_config(
            model="",
            provider=LlmProviders(custom_llm_provider),
        )
        if provider_config is not None:
            litellm_params_dict = get_litellm_params(**kwargs)
            litellm_params_dict["api_key"] = optional_params.api_key
            litellm_params_dict["api_base"] = optional_params.api_base

            logging_obj = kwargs.get("litellm_logging_obj")
            if logging_obj is None:
                from litellm.litellm_core_utils.litellm_logging import (
                    Logging as LiteLLMLoggingObj,
                )

                logging_obj = LiteLLMLoggingObj(
                    model="",
                    messages=[],
                    stream=False,
                    call_type="afile_list" if _is_async else "file_list",
                    start_time=time.time(),
                    litellm_call_id=kwargs.get(
                        "litellm_call_id", str(uuid_module.uuid4())
                    ),
                    function_id=str(kwargs.get("id", "")),
                )

            client = kwargs.get("client")
            response = base_llm_http_handler.list_files(
                purpose=purpose,
                provider_config=provider_config,
                litellm_params=litellm_params_dict,
                headers=extra_headers or {},
                logging_obj=logging_obj,
                _is_async=_is_async,
                client=(
                    client
                    if client is not None
                    and isinstance(client, (HTTPHandler, AsyncHTTPHandler))
                    else None
                ),
                timeout=timeout,
            )
            return response
        elif custom_llm_provider in OPENAI_COMPATIBLE_BATCH_AND_FILES_PROVIDERS:
            openai_creds = get_openai_credentials(
                api_base=optional_params.api_base,
                api_key=optional_params.api_key,
                organization=optional_params.organization,
            )
            response = openai_files_instance.list_files(
                purpose=purpose,
                _is_async=_is_async,
                api_base=openai_creds.api_base,
                api_key=openai_creds.api_key,
                timeout=timeout,
                max_retries=optional_params.max_retries,
                organization=openai_creds.organization,
            )
        elif custom_llm_provider == "azure":
            azure_creds = get_azure_credentials(
                api_base=optional_params.api_base,
                api_key=optional_params.api_key,
                api_version=optional_params.api_version,
            )
            response = azure_files_instance.list_files(
                _is_async=_is_async,
                api_base=azure_creds.api_base,
                api_key=azure_creds.api_key,
                api_version=azure_creds.api_version,
                timeout=timeout,
                max_retries=optional_params.max_retries,
                purpose=purpose,
            )
        else:
            raise litellm.exceptions.BadRequestError(
                message="LiteLLM doesn't support {} for 'file_list'. Only 'openai', 'azure', 'manus', and 'anthropic' are supported.".format(
                    custom_llm_provider
                ),
                model="n/a",
                llm_provider=custom_llm_provider,
                response=httpx.Response(
                    status_code=400,
                    content="Unsupported provider",
                    request=httpx.Request(method="file_list", url="https://github.com/BerriAI/litellm"),  # type: ignore
                ),
            )
        return response
    except Exception as e:
        raise e


@client
async def afile_content(
    file_id: str,
    custom_llm_provider: FileContentProvider = "openai",
    extra_headers: Optional[Dict[str, str]] = None,
    extra_body: Optional[Dict[str, str]] = None,
    chunk_size: int = 1024 * 1024,
    stream: bool = False,
    **kwargs,
) -> Union[HttpxBinaryResponseContent, FileContentStreamingResult]:
    """
    Async: Get file contents

    LiteLLM Equivalent of GET https://api.openai.com/v1/files
    """
    try:
        loop = asyncio.get_event_loop()
        kwargs["afile_content"] = True
        model = kwargs.pop("model", None)

        # Use a partial function to pass your keyword arguments
        func = partial(
            file_content,
            file_id=file_id,
            model=model,
            custom_llm_provider=custom_llm_provider,
            extra_headers=extra_headers,
            extra_body=extra_body,
            chunk_size=chunk_size,
            stream=stream,
            **kwargs,
        )

        # Add the context to the function
        ctx = contextvars.copy_context()
        func_with_context = partial(ctx.run, func)
        init_response = await loop.run_in_executor(None, func_with_context)
        if asyncio.iscoroutine(init_response):
            response = await init_response
        else:
            response = init_response  # type: ignore

        return response
    except Exception as e:
        raise e


@client
def file_content(
    file_id: str,
    model: Optional[str] = None,
    custom_llm_provider: Optional[Union[FileContentProvider, str]] = None,
    extra_headers: Optional[Dict[str, str]] = None,
    extra_body: Optional[Dict[str, str]] = None,
    chunk_size: int = 1024 * 1024,
    stream: bool = False,
    **kwargs,
) -> Union[
    HttpxBinaryResponseContent,
    FileContentStreamingResult,
    Coroutine[Any, Any, HttpxBinaryResponseContent],
    Coroutine[Any, Any, FileContentStreamingResult],
]:
    """
    Returns the contents of the specified file.

    LiteLLM Equivalent of POST: POST https://api.openai.com/v1/files
    """
    try:
        optional_params = GenericLiteLLMParams(**kwargs)
        litellm_params_dict = get_litellm_params(**kwargs)
        _add_trusted_model_credentials_to_litellm_params(
            litellm_params_dict=litellm_params_dict,
            kwargs=kwargs,
        )
        ### TIMEOUT LOGIC ###
        timeout = optional_params.timeout or kwargs.get("request_timeout", 600) or 600
        client = kwargs.get("client")
        # set timeout for 10 minutes by default

        try:
            if model is not None:
                _, custom_llm_provider, _, _ = get_llm_provider(
                    model, custom_llm_provider
                )
        except Exception:
            pass

        if (
            timeout is not None
            and isinstance(timeout, httpx.Timeout)
            and supports_httpx_timeout(cast(str, custom_llm_provider)) is False
        ):
            read_timeout = timeout.read or 600
            timeout = read_timeout  # default 10 min timeout
        elif timeout is not None and not isinstance(timeout, httpx.Timeout):
            timeout = float(timeout)  # type: ignore
        elif timeout is None:
            timeout = 600.0

        _file_content_request = FileContentRequest(
            file_id=file_id,
            extra_headers=extra_headers,
            extra_body=extra_body,
        )

        _is_async = kwargs.pop("afile_content", False) is True

        if stream and _should_sdk_support_streaming(custom_llm_provider):
            return file_content_streaming(
                file_id=file_id,
                model=model,
                custom_llm_provider=custom_llm_provider,
                extra_headers=extra_headers,
                extra_body=extra_body,
                chunk_size=chunk_size,
                optional_params=optional_params,
                timeout=timeout,
                logging_obj=cast(
                    Optional[LiteLLMLoggingObj], kwargs.get("litellm_logging_obj")
                ),
                _is_async=_is_async,
                client=client,
            )

        # Check if provider has a custom files config (e.g., Anthropic, Manus)
        provider_config = ProviderConfigManager.get_provider_files_config(
            model="",
            provider=LlmProviders(custom_llm_provider),
        )
        if provider_config is not None:
            litellm_params_dict["api_key"] = optional_params.api_key
            litellm_params_dict["api_base"] = optional_params.api_base

            logging_obj = kwargs.get("litellm_logging_obj")
            if logging_obj is None:
                logging_obj = LiteLLMLoggingObj(
                    model="",
                    messages=[],
                    stream=False,
                    call_type="afile_content" if _is_async else "file_content",
                    start_time=time.time(),
                    litellm_call_id=kwargs.get(
                        "litellm_call_id", str(uuid_module.uuid4())
                    ),
                    function_id=str(kwargs.get("id") or ""),
                )

            response = base_llm_http_handler.retrieve_file_content(
                file_content_request=_file_content_request,
                provider_config=provider_config,
                litellm_params=litellm_params_dict,
                headers=extra_headers or {},
                logging_obj=logging_obj,
                _is_async=_is_async,
                client=(
                    client
                    if client is not None
                    and isinstance(client, (HTTPHandler, AsyncHTTPHandler))
                    else None
                ),
                timeout=timeout,
            )
            return response

        if custom_llm_provider in OPENAI_COMPATIBLE_BATCH_AND_FILES_PROVIDERS:
            openai_creds = get_openai_credentials(
                api_base=optional_params.api_base,
                api_key=optional_params.api_key,
                organization=optional_params.organization,
            )
            response = openai_files_instance.file_content(
                _is_async=_is_async,
                file_content_request=_file_content_request,
                api_base=openai_creds.api_base,
                api_key=openai_creds.api_key,
                timeout=timeout,
                max_retries=optional_params.max_retries,
                organization=openai_creds.organization,
            )
        elif custom_llm_provider == "azure":
            azure_creds = get_azure_credentials(
                api_base=optional_params.api_base,
                api_key=optional_params.api_key,
                api_version=optional_params.api_version,
            )
            response = azure_files_instance.file_content(
                _is_async=_is_async,
                api_base=azure_creds.api_base,
                api_key=azure_creds.api_key,
                api_version=azure_creds.api_version,
                timeout=timeout,
                max_retries=optional_params.max_retries,
                file_content_request=_file_content_request,
                client=client,
                litellm_params=litellm_params_dict,
            )
        elif custom_llm_provider == "vertex_ai":
            api_base = optional_params.api_base or ""
            vertex_ai_project = (
                optional_params.vertex_project
                or litellm.vertex_project
                or get_secret_str("VERTEXAI_PROJECT")
            )
            vertex_ai_location = (
                optional_params.vertex_location
                or litellm.vertex_location
                or get_secret_str("VERTEXAI_LOCATION")
            )
            vertex_credentials = optional_params.vertex_credentials or get_secret_str(
                "VERTEXAI_CREDENTIALS"
            )

            response = vertex_ai_files_instance.file_content(
                _is_async=_is_async,
                file_content_request=_file_content_request,
                api_base=api_base,
                vertex_credentials=vertex_credentials,
                vertex_project=vertex_ai_project,
                vertex_location=vertex_ai_location,
                timeout=timeout,
                max_retries=optional_params.max_retries,
                litellm_params=litellm_params_dict,
            )
        elif custom_llm_provider == "bedrock":
            response = bedrock_files_instance.file_content(
                _is_async=_is_async,
                file_content_request=_file_content_request,
                api_base=optional_params.api_base,
                optional_params=litellm_params_dict,
                timeout=timeout,
                max_retries=optional_params.max_retries,
            )
        else:
            raise litellm.exceptions.BadRequestError(
                message="LiteLLM doesn't support {} for 'file_content'. Supported providers are 'openai', 'azure', 'vertex_ai', 'bedrock', 'manus', 'anthropic'.".format(
                    custom_llm_provider
                ),
                model="n/a",
                llm_provider=custom_llm_provider,
                response=httpx.Response(
                    status_code=400,
                    content="Unsupported provider",
                    request=httpx.Request(method="create_thread", url="https://github.com/BerriAI/litellm"),  # type: ignore
                ),
            )
        return response
    except Exception as e:
        raise e


def file_content_streaming(
    *,
    file_id: str,
    model: Optional[str],
    custom_llm_provider: Optional[Union[FileContentProvider, str]],
    extra_headers: Optional[Dict[str, str]],
    extra_body: Optional[Dict[str, str]],
    chunk_size: int,
    optional_params: GenericLiteLLMParams,
    timeout: Union[float, httpx.Timeout],
    logging_obj: Optional[LiteLLMLoggingObj],
    _is_async: bool,
    client: Optional[Any],
) -> Union[FileContentStreamingResult, Coroutine[Any, Any, FileContentStreamingResult]]:
    if logging_obj is not None:
        logging_obj.model = model or ""
        logging_obj.model_call_details["model"] = model or ""
        logging_obj.model_call_details["custom_llm_provider"] = custom_llm_provider

        litellm_params = logging_obj.model_call_details.get("litellm_params", {}) or {}
        if optional_params.api_base is not None:
            litellm_params["api_base"] = optional_params.api_base
        logging_obj.model_call_details["litellm_params"] = litellm_params

    def _wrap_streaming_result(
        response: FileContentStreamingResult,
    ) -> FileContentStreamingResult:
        return FileContentStreamingResult(
            stream_iterator=FileContentStreamingResponse(
                stream_iterator=response.stream_iterator,
                file_id=file_id,
                model=model,
                custom_llm_provider=custom_llm_provider,
                logging_obj=logging_obj,
            ),
            headers=response.headers,
        )

    response: Union[
        FileContentStreamingResult, Coroutine[Any, Any, FileContentStreamingResult]
    ] = FileContentStreamingResult(stream_iterator=iter(()), headers={})
    if custom_llm_provider in OPENAI_COMPATIBLE_BATCH_AND_FILES_PROVIDERS:
        openai_creds = get_openai_credentials(
            api_base=optional_params.api_base,
            api_key=optional_params.api_key,
            organization=optional_params.organization,
        )
        response = openai_files_instance.file_content_streaming(
            _is_async=_is_async,
            file_content_request=FileContentRequest(
                file_id=file_id,
                extra_headers=extra_headers,
                extra_body=extra_body,
            ),
            api_base=openai_creds.api_base,
            api_key=openai_creds.api_key,
            timeout=timeout,
            max_retries=optional_params.max_retries,
            organization=openai_creds.organization,
            chunk_size=chunk_size,
            client=client,
        )
    else:
        raise litellm.exceptions.BadRequestError(
            message="LiteLLM doesn't support {} for streaming 'file_content'. Supported providers are {}.".format(
                custom_llm_provider,
                sorted(OPENAI_COMPATIBLE_BATCH_AND_FILES_PROVIDERS),
            ),
            model="n/a",
            llm_provider=custom_llm_provider,
            response=httpx.Response(
                status_code=400,
                content="Unsupported provider",
                request=httpx.Request(method="create_thread", url="https://github.com/BerriAI/litellm"),  # type: ignore
            ),
        )

    if asyncio.iscoroutine(response):

        async def _await_and_wrap() -> FileContentStreamingResult:
            return _wrap_streaming_result(await response)

        return _await_and_wrap()

    return _wrap_streaming_result(response)
