diff --git a/litellm/__init__.py b/litellm/__init__.py index 59c8c78eb9e..11d85dc0428 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -1052,6 +1052,7 @@ from .proxy.proxy_cli import run_server from .router import Router from .assistants.main import * from .batches.main import * +from .moderations.main import * from .batch_completion.main import * # type: ignore from .rerank_api.main import * from .llms.anthropic.experimental_pass_through.messages.handler import * diff --git a/litellm/main.py b/litellm/main.py index ec00e1a3491..d82cb339dbe 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -4432,108 +4432,6 @@ def adapter_completion( return translated_response -##### Moderation ####################### - - -def moderation( - input: str, model: Optional[str] = None, api_key: Optional[str] = None, **kwargs -) -> OpenAIModerationResponse: - # only supports open ai for now - api_key = ( - api_key - or litellm.api_key - or litellm.openai_key - or get_secret_str("OPENAI_API_KEY") - ) - - openai_client = kwargs.get("client", None) - if openai_client is None: - openai_client = openai.OpenAI( - api_key=api_key, - ) - - if model is not None: - response = openai_client.moderations.create(input=input, model=model) - else: - response = openai_client.moderations.create(input=input) - - response_dict: Dict = response.model_dump() - return litellm.utils.LiteLLMResponseObjectHandler.convert_to_moderation_response( - response_object=response_dict, - ) - - -@client -async def amoderation( - input: str, - model: Optional[str] = None, - api_key: Optional[str] = None, - custom_llm_provider: Optional[str] = None, - **kwargs, -) -> OpenAIModerationResponse: - from openai import AsyncOpenAI - - # only supports open ai for now - api_key = ( - api_key - or litellm.api_key - or litellm.openai_key - or get_secret_str("OPENAI_API_KEY") - ) - openai_client = kwargs.get("client", None) - if openai_client is None or not isinstance(openai_client, AsyncOpenAI): - # call helper to get OpenAI client - # _get_openai_client maintains in-memory caching logic for OpenAI clients - _openai_client: AsyncOpenAI = openai_chat_completions._get_openai_client( # type: ignore - is_async=True, - api_key=api_key, - ) - else: - _openai_client = openai_client - - optional_params = GenericLiteLLMParams(**kwargs) - litellm_logging_obj: Optional[LiteLLMLoggingObj] = kwargs.get( - "litellm_logging_obj", None - ) - try: - ( - model, - custom_llm_provider, - _dynamic_api_key, - _dynamic_api_base, - ) = litellm.get_llm_provider( - model=model or "", - custom_llm_provider=custom_llm_provider, - api_base=optional_params.api_base, - api_key=optional_params.api_key, - ) - except litellm.BadRequestError: - # `model` is optional field for moderation - get_llm_provider will throw BadRequestError if model is not set / not recognized - pass - - # update litellm_logging_obj with environment variables - custom_llm_provider = custom_llm_provider or litellm.LlmProviders.OPENAI.value - if litellm_logging_obj is not None: - litellm_logging_obj.update_environment_variables( - model=model, - user=kwargs.get("user", None), - optional_params={}, - litellm_params={ - **kwargs, - }, - custom_llm_provider=custom_llm_provider, - ) - - if model is not None: - response = await _openai_client.moderations.create(input=input, model=model) - else: - response = await _openai_client.moderations.create(input=input) - response_dict: Dict = response.model_dump() - return litellm.utils.LiteLLMResponseObjectHandler.convert_to_moderation_response( - response_object=response_dict, - ) - - ##### Image Generation ####################### @client async def aimage_generation(*args, **kwargs) -> ImageResponse: diff --git a/litellm/moderations/main.py b/litellm/moderations/main.py new file mode 100644 index 00000000000..2eb336fffa1 --- /dev/null +++ b/litellm/moderations/main.py @@ -0,0 +1,147 @@ +from typing import Dict, Optional + +import openai + +import litellm + +############ Instantiated classes ############ +from litellm.main import openai_chat_completions as openai_api_client +from litellm.moderations.utils import ModerationAPIUtils +from litellm.secret_managers.main import get_secret_str +from litellm.types.llms.openai import OpenAIModerationResponse +from litellm.types.router import GenericLiteLLMParams +from litellm.utils import client + +############# Moderations API ####################### + + +@client +def moderation( + input: str, + model: Optional[str] = None, + api_key: Optional[str] = None, + custom_llm_provider: Optional[str] = None, + **kwargs, +) -> OpenAIModerationResponse: + # only supports open ai for now + api_key = ( + api_key + or litellm.api_key + or litellm.openai_key + or get_secret_str("OPENAI_API_KEY") + ) + + optional_params = GenericLiteLLMParams(**kwargs) + try: + ( + model, + custom_llm_provider, + _dynamic_api_key, + _dynamic_api_base, + ) = litellm.get_llm_provider( + model=model or "", + custom_llm_provider=custom_llm_provider, + api_base=optional_params.api_base, + api_key=optional_params.api_key, + ) + except litellm.BadRequestError: + # `model` is optional field for moderation - get_llm_provider will throw BadRequestError if model is not set / not recognized + pass + + openai_client = kwargs.get("client", None) + if openai_client is None: + openai_client = openai.OpenAI( + api_key=api_key, + ) + + # update litellm_logging_obj with request params (used for logging the correct values in the logging callbacks) + ModerationAPIUtils.init_litellm_logging_obj_for_moderations_call( + custom_llm_provider=custom_llm_provider, + model=model, + user=kwargs.get("user", None), + **kwargs, + ) + + if model is not None: + response = openai_client.moderations.create(input=input, model=model) + else: + response = openai_client.moderations.create(input=input) + + response_dict: Dict = response.model_dump() + return litellm.utils.LiteLLMResponseObjectHandler.convert_to_moderation_response( + response_object=response_dict, + ) + + +@client +async def amoderation( + input: str, + model: Optional[str] = None, + api_key: Optional[str] = None, + custom_llm_provider: Optional[str] = None, + **kwargs, +) -> OpenAIModerationResponse: + from openai import AsyncOpenAI + + ############################################################ + ######### Pre-Request Setup ################################# + ############################################################ + # only supports open ai for now + api_key = ( + api_key + or litellm.api_key + or litellm.openai_key + or get_secret_str("OPENAI_API_KEY") + ) + openai_client = kwargs.get("client", None) + if openai_client is None or not isinstance(openai_client, AsyncOpenAI): + # call helper to get OpenAI client + # _get_openai_client maintains in-memory caching logic for OpenAI clients + _openai_client: AsyncOpenAI = openai_api_client._get_openai_client( # type: ignore + is_async=True, + api_key=api_key, + ) + else: + _openai_client = openai_client + + optional_params = GenericLiteLLMParams(**kwargs) + try: + ( + model, + custom_llm_provider, + _dynamic_api_key, + _dynamic_api_base, + ) = litellm.get_llm_provider( + model=model or "", + custom_llm_provider=custom_llm_provider, + api_base=optional_params.api_base, + api_key=optional_params.api_key, + ) + except litellm.BadRequestError: + # `model` is optional field for moderation - get_llm_provider will throw BadRequestError if model is not set / not recognized + pass + + # update litellm_logging_obj with request params (used for logging the correct values in the logging callbacks) + ModerationAPIUtils.init_litellm_logging_obj_for_moderations_call( + custom_llm_provider=custom_llm_provider, + model=model, + user=kwargs.get("user", None), + **kwargs, + ) + + ############################################################ + ######### Make API Call ################################# + ############################################################ + + if model is not None: + response = await _openai_client.moderations.create(input=input, model=model) + else: + response = await _openai_client.moderations.create(input=input) + + ############################################################ + ######### Post-Request Processing ################################# + ############################################################ + response_dict: Dict = response.model_dump() + return litellm.utils.LiteLLMResponseObjectHandler.convert_to_moderation_response( + response_object=response_dict, + ) diff --git a/litellm/moderations/utils.py b/litellm/moderations/utils.py new file mode 100644 index 00000000000..c6b4a8f0563 --- /dev/null +++ b/litellm/moderations/utils.py @@ -0,0 +1,38 @@ +from typing import Optional + +import litellm +from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj + + +class ModerationAPIUtils: + + @staticmethod + def init_litellm_logging_obj_for_moderations_call( + custom_llm_provider: Optional[str] = None, + model: Optional[str] = None, + user: Optional[str] = None, + **kwargs, + ): + """ + Initialize the litellm_logging_obj for a moderations call + + Ensures the correct `custom_llm_provider`, model, and user are set in the litellm_logging_obj + + This will be used downstream when constructing the standard_logging_payload + """ + litellm_logging_obj: Optional[LiteLLMLoggingObj] = kwargs.get( + "litellm_logging_obj", None + ) + if litellm_logging_obj: + custom_llm_provider = ( + custom_llm_provider or litellm.LlmProviders.OPENAI.value + ) + litellm_logging_obj.update_environment_variables( + model=model, + user=kwargs.get("user", None), + optional_params={}, + litellm_params={ + **kwargs, + }, + custom_llm_provider=custom_llm_provider, + )