From 3731ee436a2edfed0849b51bbc50073f0b6668fd Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 9 May 2025 17:18:48 -0700 Subject: [PATCH] [Refactor] Use pip package for enterprise/ folder (#10709) * init enterprise pip * init enterprise pip * init enterprise pip * test: enterprise pip * add litellm-enterprise to pip * litellm ent check * litellm ent check * fix import email router * fix setup_litellm_enterprise_pip * fix local testing with enterprise pip --- .circleci/config.yml | 17 +- .../litellm_enterprise-0.1.1-py3-none-any.whl | Bin 0 -> 10405 bytes .../dist/litellm_enterprise-0.1.1.tar.gz | Bin 0 -> 7566 bytes enterprise/litellm_enterprise/__init__.py | 0 .../example_logging_api.py | 6 +- .../generic_api_callback.py | 0 .../send_emails/base_email.py | 6 +- .../send_emails/endpoints.py | 0 .../send_emails/resend_email.py | 0 enterprise/proxy/enterprise_routes.py | 4 +- enterprise/pyproject.toml | 30 ++ litellm/litellm_core_utils/litellm_logging.py | 288 +++++++++--------- .../proxy/hooks/key_management_event_hooks.py | 3 +- .../hooks/user_management_event_hooks.py | 4 +- poetry.lock | 16 +- pyproject.toml | 2 + requirements.txt | 6 +- tests/code_coverage_tests/liccheck.ini | 3 +- .../send_emails/test_base_email.py | 5 +- .../send_emails/test_endpoints.py | 11 +- .../send_emails/test_resend_email.py | 6 +- .../test_generic_api_callback.py | 2 +- .../test_unit_tests_init_callbacks.py | 4 +- 23 files changed, 245 insertions(+), 168 deletions(-) create mode 100644 enterprise/dist/litellm_enterprise-0.1.1-py3-none-any.whl create mode 100644 enterprise/dist/litellm_enterprise-0.1.1.tar.gz create mode 100644 enterprise/litellm_enterprise/__init__.py rename enterprise/{ => litellm_enterprise}/enterprise_callbacks/example_logging_api.py (83%) rename enterprise/{ => litellm_enterprise}/enterprise_callbacks/generic_api_callback.py (100%) rename enterprise/{ => litellm_enterprise}/enterprise_callbacks/send_emails/base_email.py (97%) rename enterprise/{ => litellm_enterprise}/enterprise_callbacks/send_emails/endpoints.py (100%) rename enterprise/{ => litellm_enterprise}/enterprise_callbacks/send_emails/resend_email.py (100%) create mode 100644 enterprise/pyproject.toml diff --git a/.circleci/config.yml b/.circleci/config.yml index 9fb1aeaf4f..26660e109e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -16,6 +16,14 @@ commands: echo "nameserver 127.0.0.11" | sudo tee /etc/resolv.conf echo "nameserver 8.8.8.8" | sudo tee -a /etc/resolv.conf echo "nameserver 8.8.4.4" | sudo tee -a /etc/resolv.conf + setup_litellm_enterprise_pip: + steps: + - run: + name: "Install local version of litellm-enterprise" + command: | + cd enterprise + python -m pip install -e . + cd .. jobs: # Add Windows testing job @@ -111,6 +119,7 @@ jobs: pip install "pytest-xdist==3.6.1" pip install "websockets==13.1.0" pip uninstall posthog -y + - setup_litellm_enterprise_pip - save_cache: paths: - ./venv @@ -228,6 +237,7 @@ jobs: pip install "Pillow==10.3.0" pip install "jsonschema==4.22.0" pip install "websockets==13.1.0" + - setup_litellm_enterprise_pip - save_cache: paths: - ./venv @@ -334,6 +344,7 @@ jobs: pip install "Pillow==10.3.0" pip install "jsonschema==4.22.0" pip install "websockets==13.1.0" + - setup_litellm_enterprise_pip - save_cache: paths: - ./venv @@ -445,6 +456,7 @@ jobs: pip install "pytest-retry==1.6.3" pip install "pytest-asyncio==0.21.1" # Run pytest and generate JUnit XML report + - setup_litellm_enterprise_pip - run: name: Run tests command: | @@ -589,6 +601,7 @@ jobs: pip install "jsonschema==4.22.0" pip install "pytest-postgresql==7.0.1" pip install "fakeredis==2.28.1" + - setup_litellm_enterprise_pip - save_cache: paths: - ./venv @@ -861,7 +874,7 @@ jobs: pip install "mcp==1.5.0" pip install "requests-mock>=1.12.1" pip install "responses==0.25.7" - + - setup_litellm_enterprise_pip # Run pytest and generate JUnit XML report - run: name: Run tests @@ -1088,6 +1101,7 @@ jobs: pip install "google-cloud-aiplatform==1.43.0" pip install "mlflow==2.17.2" # Run pytest and generate JUnit XML report + - setup_litellm_enterprise_pip - run: name: Run tests command: | @@ -1135,6 +1149,7 @@ jobs: pip install "tokenizers==0.20.0" pip install "uvloop==0.21.0" pip install jsonschema + - setup_litellm_enterprise_pip - run: name: Run tests command: | diff --git a/enterprise/dist/litellm_enterprise-0.1.1-py3-none-any.whl b/enterprise/dist/litellm_enterprise-0.1.1-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..d9a8ef41e62a18a6f7b82752600a8920ec5492c5 GIT binary patch literal 10405 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 literal 0 HcmV?d00001 diff --git a/enterprise/dist/litellm_enterprise-0.1.1.tar.gz b/enterprise/dist/litellm_enterprise-0.1.1.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..98cf132b213d4fe9d4002ccffe4e7dc36e00c7e0 GIT binary patch literal 7566 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 literal 0 HcmV?d00001 diff --git a/enterprise/litellm_enterprise/__init__.py b/enterprise/litellm_enterprise/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/enterprise/enterprise_callbacks/example_logging_api.py b/enterprise/litellm_enterprise/enterprise_callbacks/example_logging_api.py similarity index 83% rename from enterprise/enterprise_callbacks/example_logging_api.py rename to enterprise/litellm_enterprise/enterprise_callbacks/example_logging_api.py index 2084ffb548..14d34f5d1e 100644 --- a/enterprise/enterprise_callbacks/example_logging_api.py +++ b/enterprise/litellm_enterprise/enterprise_callbacks/example_logging_api.py @@ -7,11 +7,11 @@ app = FastAPI() @app.post("/log-event") async def log_event(request: Request): try: - print("Received /log-event request") # noqa + print("Received /log-event request") # noqa # Assuming the incoming request has JSON data data = await request.json() - print("Received request data:") # noqa - print(data) # noqa + print("Received request data:") # noqa + print(data) # noqa # Your additional logic can go here # For now, just printing the received data diff --git a/enterprise/enterprise_callbacks/generic_api_callback.py b/enterprise/litellm_enterprise/enterprise_callbacks/generic_api_callback.py similarity index 100% rename from enterprise/enterprise_callbacks/generic_api_callback.py rename to enterprise/litellm_enterprise/enterprise_callbacks/generic_api_callback.py diff --git a/enterprise/enterprise_callbacks/send_emails/base_email.py b/enterprise/litellm_enterprise/enterprise_callbacks/send_emails/base_email.py similarity index 97% rename from enterprise/enterprise_callbacks/send_emails/base_email.py rename to enterprise/litellm_enterprise/enterprise_callbacks/send_emails/base_email.py index e476852b2e..d9f3ce46ba 100644 --- a/enterprise/enterprise_callbacks/send_emails/base_email.py +++ b/enterprise/litellm_enterprise/enterprise_callbacks/send_emails/base_email.py @@ -105,9 +105,9 @@ class BaseEmailLogger(CustomLogger): support_contact = os.getenv("EMAIL_SUPPORT_CONTACT", self.DEFAULT_SUPPORT_EMAIL) base_url = os.getenv("PROXY_BASE_URL", "http://0.0.0.0:4000") - recipient_email: Optional[str] = ( - user_email or await self._lookup_user_email_from_db(user_id=user_id) - ) + recipient_email: Optional[ + str + ] = user_email or await self._lookup_user_email_from_db(user_id=user_id) if recipient_email is None: raise ValueError( f"User email not found for user_id: {user_id}. User email is required to send email." diff --git a/enterprise/enterprise_callbacks/send_emails/endpoints.py b/enterprise/litellm_enterprise/enterprise_callbacks/send_emails/endpoints.py similarity index 100% rename from enterprise/enterprise_callbacks/send_emails/endpoints.py rename to enterprise/litellm_enterprise/enterprise_callbacks/send_emails/endpoints.py diff --git a/enterprise/enterprise_callbacks/send_emails/resend_email.py b/enterprise/litellm_enterprise/enterprise_callbacks/send_emails/resend_email.py similarity index 100% rename from enterprise/enterprise_callbacks/send_emails/resend_email.py rename to enterprise/litellm_enterprise/enterprise_callbacks/send_emails/resend_email.py diff --git a/enterprise/proxy/enterprise_routes.py b/enterprise/proxy/enterprise_routes.py index d7aff1952b..8dcc1c77d2 100644 --- a/enterprise/proxy/enterprise_routes.py +++ b/enterprise/proxy/enterprise_routes.py @@ -1,7 +1,9 @@ from fastapi import APIRouter from fastapi.responses import Response +from litellm_enterprise.enterprise_callbacks.send_emails.endpoints import ( + router as email_events_router, +) -from ..enterprise_callbacks.send_emails.endpoints import router as email_events_router from .utils import _should_block_robots from .vector_stores.endpoints import router as vector_stores_router diff --git a/enterprise/pyproject.toml b/enterprise/pyproject.toml new file mode 100644 index 0000000000..a04accb96c --- /dev/null +++ b/enterprise/pyproject.toml @@ -0,0 +1,30 @@ +[tool.poetry] +name = "litellm-enterprise" +version = "0.1.1" +description = "Package for LiteLLM Enterprise features" +authors = ["BerriAI"] +readme = "README.md" + + +[tool.poetry.urls] +homepage = "https://litellm.ai" +Homepage = "https://litellm.ai" +repository = "https://github.com/BerriAI/litellm" +Repository = "https://github.com/BerriAI/litellm" +documentation = "https://docs.litellm.ai" +Documentation = "https://docs.litellm.ai" + +[tool.poetry.dependencies] +python = ">=3.8.1,<4.0, !=3.9.7" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + +[tool.commitizen] +version = "0.1.18" +version_files = [ + "pyproject.toml:version", + "../requirements.txt:litellm-enterprise==", + "../pyproject.toml:litellm-enterprise = {version = \"" +] \ No newline at end of file diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index 89702e0345..354cde5739 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -134,8 +134,10 @@ from .initialize_dynamic_callback_params import ( from .specialty_caches.dynamic_logging_cache import DynamicLoggingCache try: - from enterprise.enterprise_callbacks.generic_api_callback import GenericAPILogger - from enterprise.enterprise_callbacks.send_emails.resend_email import ( + from litellm_enterprise.enterprise_callbacks.generic_api_callback import ( + GenericAPILogger, + ) + from litellm_enterprise.enterprise_callbacks.send_emails.resend_email import ( ResendEmailLogger, ) except Exception as e: @@ -257,9 +259,9 @@ class Logging(LiteLLMLoggingBaseClass): self.litellm_trace_id: str = litellm_trace_id or str(uuid.uuid4()) self.function_id = function_id self.streaming_chunks: List[Any] = [] # for generating complete stream response - self.sync_streaming_chunks: List[Any] = ( - [] - ) # for generating complete stream response + self.sync_streaming_chunks: List[ + Any + ] = [] # for generating complete stream response self.log_raw_request_response = log_raw_request_response # Initialize dynamic callbacks @@ -610,9 +612,9 @@ class Logging(LiteLLMLoggingBaseClass): if anthropic_cache_control_logger := AnthropicCacheControlHook.get_custom_logger_for_anthropic_cache_control_hook( non_default_params ): - self.model_call_details["prompt_integration"] = ( - anthropic_cache_control_logger.__class__.__name__ - ) + self.model_call_details[ + "prompt_integration" + ] = anthropic_cache_control_logger.__class__.__name__ return anthropic_cache_control_logger ######################################################### @@ -630,9 +632,9 @@ class Logging(LiteLLMLoggingBaseClass): ), ) ) - self.model_call_details["prompt_integration"] = ( - vector_store_custom_logger.__class__.__name__ - ) + self.model_call_details[ + "prompt_integration" + ] = vector_store_custom_logger.__class__.__name__ return vector_store_custom_logger return None @@ -684,9 +686,9 @@ class Logging(LiteLLMLoggingBaseClass): model ): # if model name was changes pre-call, overwrite the initial model call name with the new one self.model_call_details["model"] = model - self.model_call_details["litellm_params"]["api_base"] = ( - self._get_masked_api_base(additional_args.get("api_base", "")) - ) + self.model_call_details["litellm_params"][ + "api_base" + ] = self._get_masked_api_base(additional_args.get("api_base", "")) def pre_call(self, input, api_key, model=None, additional_args={}): # noqa: PLR0915 # Log the exact input to the LLM API @@ -715,10 +717,10 @@ class Logging(LiteLLMLoggingBaseClass): try: # [Non-blocking Extra Debug Information in metadata] if turn_off_message_logging is True: - _metadata["raw_request"] = ( - "redacted by litellm. \ + _metadata[ + "raw_request" + ] = "redacted by litellm. \ 'litellm.turn_off_message_logging=True'" - ) else: curl_command = self._get_request_curl_command( api_base=additional_args.get("api_base", ""), @@ -729,32 +731,32 @@ class Logging(LiteLLMLoggingBaseClass): _metadata["raw_request"] = str(curl_command) # split up, so it's easier to parse in the UI - self.model_call_details["raw_request_typed_dict"] = ( - RawRequestTypedDict( - raw_request_api_base=str( - additional_args.get("api_base") or "" - ), - raw_request_body=self._get_raw_request_body( - additional_args.get("complete_input_dict", {}) - ), - raw_request_headers=self._get_masked_headers( - additional_args.get("headers", {}) or {}, - ignore_sensitive_headers=True, - ), - error=None, - ) + self.model_call_details[ + "raw_request_typed_dict" + ] = RawRequestTypedDict( + raw_request_api_base=str( + additional_args.get("api_base") or "" + ), + raw_request_body=self._get_raw_request_body( + additional_args.get("complete_input_dict", {}) + ), + raw_request_headers=self._get_masked_headers( + additional_args.get("headers", {}) or {}, + ignore_sensitive_headers=True, + ), + error=None, ) except Exception as e: - self.model_call_details["raw_request_typed_dict"] = ( - RawRequestTypedDict( - error=str(e), - ) + self.model_call_details[ + "raw_request_typed_dict" + ] = RawRequestTypedDict( + error=str(e), ) - _metadata["raw_request"] = ( - "Unable to Log \ + _metadata[ + "raw_request" + ] = "Unable to Log \ raw request: {}".format( - str(e) - ) + str(e) ) if self.logger_fn and callable(self.logger_fn): try: @@ -1085,9 +1087,9 @@ class Logging(LiteLLMLoggingBaseClass): verbose_logger.debug( f"response_cost_failure_debug_information: {debug_info}" ) - self.model_call_details["response_cost_failure_debug_information"] = ( - debug_info - ) + self.model_call_details[ + "response_cost_failure_debug_information" + ] = debug_info return None try: @@ -1112,9 +1114,9 @@ class Logging(LiteLLMLoggingBaseClass): verbose_logger.debug( f"response_cost_failure_debug_information: {debug_info}" ) - self.model_call_details["response_cost_failure_debug_information"] = ( - debug_info - ) + self.model_call_details[ + "response_cost_failure_debug_information" + ] = debug_info return None @@ -1174,9 +1176,9 @@ class Logging(LiteLLMLoggingBaseClass): end_time = datetime.datetime.now() if self.completion_start_time is None: self.completion_start_time = end_time - self.model_call_details["completion_start_time"] = ( - self.completion_start_time - ) + self.model_call_details[ + "completion_start_time" + ] = self.completion_start_time self.model_call_details["log_event_type"] = "successful_api_call" self.model_call_details["end_time"] = end_time self.model_call_details["cache_hit"] = cache_hit @@ -1256,39 +1258,39 @@ class Logging(LiteLLMLoggingBaseClass): "response_cost" ] else: - self.model_call_details["response_cost"] = ( - self._response_cost_calculator(result=logging_result) - ) + self.model_call_details[ + "response_cost" + ] = self._response_cost_calculator(result=logging_result) ## STANDARDIZED LOGGING PAYLOAD - self.model_call_details["standard_logging_object"] = ( - get_standard_logging_object_payload( - kwargs=self.model_call_details, - init_response_obj=logging_result, - start_time=start_time, - end_time=end_time, - logging_obj=self, - status="success", - standard_built_in_tools_params=self.standard_built_in_tools_params, - ) + self.model_call_details[ + "standard_logging_object" + ] = get_standard_logging_object_payload( + kwargs=self.model_call_details, + init_response_obj=logging_result, + start_time=start_time, + end_time=end_time, + logging_obj=self, + status="success", + standard_built_in_tools_params=self.standard_built_in_tools_params, ) elif isinstance(result, dict) or isinstance(result, list): ## STANDARDIZED LOGGING PAYLOAD - self.model_call_details["standard_logging_object"] = ( - get_standard_logging_object_payload( - kwargs=self.model_call_details, - init_response_obj=result, - start_time=start_time, - end_time=end_time, - logging_obj=self, - status="success", - standard_built_in_tools_params=self.standard_built_in_tools_params, - ) + self.model_call_details[ + "standard_logging_object" + ] = get_standard_logging_object_payload( + kwargs=self.model_call_details, + init_response_obj=result, + start_time=start_time, + end_time=end_time, + logging_obj=self, + status="success", + standard_built_in_tools_params=self.standard_built_in_tools_params, ) elif standard_logging_object is not None: - self.model_call_details["standard_logging_object"] = ( - standard_logging_object - ) + self.model_call_details[ + "standard_logging_object" + ] = standard_logging_object else: # streaming chunks + image gen. self.model_call_details["response_cost"] = None @@ -1344,23 +1346,23 @@ class Logging(LiteLLMLoggingBaseClass): verbose_logger.debug( "Logging Details LiteLLM-Success Call streaming complete" ) - self.model_call_details["complete_streaming_response"] = ( - complete_streaming_response - ) - self.model_call_details["response_cost"] = ( - self._response_cost_calculator(result=complete_streaming_response) - ) + self.model_call_details[ + "complete_streaming_response" + ] = complete_streaming_response + self.model_call_details[ + "response_cost" + ] = self._response_cost_calculator(result=complete_streaming_response) ## STANDARDIZED LOGGING PAYLOAD - self.model_call_details["standard_logging_object"] = ( - get_standard_logging_object_payload( - kwargs=self.model_call_details, - init_response_obj=complete_streaming_response, - start_time=start_time, - end_time=end_time, - logging_obj=self, - status="success", - standard_built_in_tools_params=self.standard_built_in_tools_params, - ) + self.model_call_details[ + "standard_logging_object" + ] = get_standard_logging_object_payload( + kwargs=self.model_call_details, + init_response_obj=complete_streaming_response, + start_time=start_time, + end_time=end_time, + logging_obj=self, + status="success", + standard_built_in_tools_params=self.standard_built_in_tools_params, ) callbacks = self.get_combined_callback_list( dynamic_success_callbacks=self.dynamic_success_callbacks, @@ -1680,10 +1682,10 @@ class Logging(LiteLLMLoggingBaseClass): ) else: if self.stream and complete_streaming_response: - self.model_call_details["complete_response"] = ( - self.model_call_details.get( - "complete_streaming_response", {} - ) + self.model_call_details[ + "complete_response" + ] = self.model_call_details.get( + "complete_streaming_response", {} ) result = self.model_call_details["complete_response"] openMeterLogger.log_success_event( @@ -1723,10 +1725,10 @@ class Logging(LiteLLMLoggingBaseClass): ) else: if self.stream and complete_streaming_response: - self.model_call_details["complete_response"] = ( - self.model_call_details.get( - "complete_streaming_response", {} - ) + self.model_call_details[ + "complete_response" + ] = self.model_call_details.get( + "complete_streaming_response", {} ) result = self.model_call_details["complete_response"] @@ -1833,9 +1835,9 @@ class Logging(LiteLLMLoggingBaseClass): if complete_streaming_response is not None: print_verbose("Async success callbacks: Got a complete streaming response") - self.model_call_details["async_complete_streaming_response"] = ( - complete_streaming_response - ) + self.model_call_details[ + "async_complete_streaming_response" + ] = complete_streaming_response try: if self.model_call_details.get("cache_hit", False) is True: self.model_call_details["response_cost"] = 0.0 @@ -1845,10 +1847,10 @@ class Logging(LiteLLMLoggingBaseClass): model_call_details=self.model_call_details ) # base_model defaults to None if not set on model_info - self.model_call_details["response_cost"] = ( - self._response_cost_calculator( - result=complete_streaming_response - ) + self.model_call_details[ + "response_cost" + ] = self._response_cost_calculator( + result=complete_streaming_response ) verbose_logger.debug( @@ -1861,16 +1863,16 @@ class Logging(LiteLLMLoggingBaseClass): self.model_call_details["response_cost"] = None ## STANDARDIZED LOGGING PAYLOAD - self.model_call_details["standard_logging_object"] = ( - get_standard_logging_object_payload( - kwargs=self.model_call_details, - init_response_obj=complete_streaming_response, - start_time=start_time, - end_time=end_time, - logging_obj=self, - status="success", - standard_built_in_tools_params=self.standard_built_in_tools_params, - ) + self.model_call_details[ + "standard_logging_object" + ] = get_standard_logging_object_payload( + kwargs=self.model_call_details, + init_response_obj=complete_streaming_response, + start_time=start_time, + end_time=end_time, + logging_obj=self, + status="success", + standard_built_in_tools_params=self.standard_built_in_tools_params, ) callbacks = self.get_combined_callback_list( dynamic_success_callbacks=self.dynamic_async_success_callbacks, @@ -2076,18 +2078,18 @@ class Logging(LiteLLMLoggingBaseClass): ## STANDARDIZED LOGGING PAYLOAD - self.model_call_details["standard_logging_object"] = ( - get_standard_logging_object_payload( - kwargs=self.model_call_details, - init_response_obj={}, - start_time=start_time, - end_time=end_time, - logging_obj=self, - status="failure", - error_str=str(exception), - original_exception=exception, - standard_built_in_tools_params=self.standard_built_in_tools_params, - ) + self.model_call_details[ + "standard_logging_object" + ] = get_standard_logging_object_payload( + kwargs=self.model_call_details, + init_response_obj={}, + start_time=start_time, + end_time=end_time, + logging_obj=self, + status="failure", + error_str=str(exception), + original_exception=exception, + standard_built_in_tools_params=self.standard_built_in_tools_params, ) return start_time, end_time @@ -2861,9 +2863,9 @@ def _init_custom_logger_compatible_class( # noqa: PLR0915 endpoint=arize_config.endpoint, ) - os.environ["OTEL_EXPORTER_OTLP_TRACES_HEADERS"] = ( - f"space_key={arize_config.space_key},api_key={arize_config.api_key}" - ) + os.environ[ + "OTEL_EXPORTER_OTLP_TRACES_HEADERS" + ] = f"space_key={arize_config.space_key},api_key={arize_config.api_key}" for callback in _in_memory_loggers: if ( isinstance(callback, ArizeLogger) @@ -2887,9 +2889,9 @@ def _init_custom_logger_compatible_class( # noqa: PLR0915 # auth can be disabled on local deployments of arize phoenix if arize_phoenix_config.otlp_auth_headers is not None: - os.environ["OTEL_EXPORTER_OTLP_TRACES_HEADERS"] = ( - arize_phoenix_config.otlp_auth_headers - ) + os.environ[ + "OTEL_EXPORTER_OTLP_TRACES_HEADERS" + ] = arize_phoenix_config.otlp_auth_headers for callback in _in_memory_loggers: if ( @@ -2980,9 +2982,9 @@ def _init_custom_logger_compatible_class( # noqa: PLR0915 exporter="otlp_http", endpoint="https://langtrace.ai/api/trace", ) - os.environ["OTEL_EXPORTER_OTLP_TRACES_HEADERS"] = ( - f"api_key={os.getenv('LANGTRACE_API_KEY')}" - ) + os.environ[ + "OTEL_EXPORTER_OTLP_TRACES_HEADERS" + ] = f"api_key={os.getenv('LANGTRACE_API_KEY')}" for callback in _in_memory_loggers: if ( isinstance(callback, OpenTelemetry) @@ -3526,10 +3528,10 @@ class StandardLoggingPayloadSetup: for key in StandardLoggingHiddenParams.__annotations__.keys(): if key in hidden_params: if key == "additional_headers": - clean_hidden_params["additional_headers"] = ( - StandardLoggingPayloadSetup.get_additional_headers( - hidden_params[key] - ) + clean_hidden_params[ + "additional_headers" + ] = StandardLoggingPayloadSetup.get_additional_headers( + hidden_params[key] ) else: clean_hidden_params[key] = hidden_params[key] # type: ignore @@ -3901,9 +3903,9 @@ def scrub_sensitive_keys_in_metadata(litellm_params: Optional[dict]): ): for k, v in metadata["user_api_key_metadata"].items(): if k == "logging": # prevent logging user logging keys - cleaned_user_api_key_metadata[k] = ( - "scrubbed_by_litellm_for_sensitive_keys" - ) + cleaned_user_api_key_metadata[ + k + ] = "scrubbed_by_litellm_for_sensitive_keys" else: cleaned_user_api_key_metadata[k] = v diff --git a/litellm/proxy/hooks/key_management_event_hooks.py b/litellm/proxy/hooks/key_management_event_hooks.py index 047dd97692..082b506ddd 100644 --- a/litellm/proxy/hooks/key_management_event_hooks.py +++ b/litellm/proxy/hooks/key_management_event_hooks.py @@ -297,9 +297,10 @@ class KeyManagementEventHooks: @staticmethod async def _send_key_created_email(response: dict): - from enterprise.enterprise_callbacks.send_emails.base_email import ( + from litellm_enterprise.enterprise_callbacks.send_emails.base_email import ( BaseEmailLogger, ) + from litellm.proxy.proxy_server import general_settings, proxy_logging_obj from litellm.types.enterprise.enterprise_callbacks.send_emails import ( SendKeyCreatedEmailEvent, diff --git a/litellm/proxy/hooks/user_management_event_hooks.py b/litellm/proxy/hooks/user_management_event_hooks.py index b56d3143da..eb0b003d23 100644 --- a/litellm/proxy/hooks/user_management_event_hooks.py +++ b/litellm/proxy/hooks/user_management_event_hooks.py @@ -7,10 +7,12 @@ import uuid from datetime import datetime, timezone from typing import Optional +from litellm_enterprise.enterprise_callbacks.send_emails.base_email import ( + BaseEmailLogger, +) from pydantic import BaseModel import litellm -from enterprise.enterprise_callbacks.send_emails.base_email import BaseEmailLogger from litellm._logging import verbose_proxy_logger from litellm.proxy._types import ( AUDIT_ACTIONS, diff --git a/poetry.lock b/poetry.lock index afd8290a83..f6b6d5cd38 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1709,6 +1709,18 @@ files = [ importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""} referencing = ">=0.31.0" +[[package]] +name = "litellm-enterprise" +version = "0.1.1" +description = "Package for LiteLLM Enterprise features" +optional = true +python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" +groups = ["main"] +markers = "extra == \"proxy\"" +files = [ + {file = "litellm_enterprise-0.1.1.tar.gz", hash = "sha256:58465200b1ab8e8c3b5e8a4ba08267502ac35dc42bc05e3a388575d02a5219b6"}, +] + [[package]] name = "litellm-proxy-extras" version = "0.1.18" @@ -4669,9 +4681,9 @@ type = ["pytest-mypy"] [extras] extra-proxy = ["azure-identity", "azure-keyvault-secrets", "google-cloud-kms", "prisma", "redisvl", "resend"] -proxy = ["PyJWT", "apscheduler", "backoff", "boto3", "cryptography", "fastapi", "fastapi-sso", "gunicorn", "litellm-proxy-extras", "mcp", "orjson", "pynacl", "python-multipart", "pyyaml", "rich", "rq", "uvicorn", "uvloop", "websockets"] +proxy = ["PyJWT", "apscheduler", "backoff", "boto3", "cryptography", "fastapi", "fastapi-sso", "gunicorn", "litellm-enterprise", "litellm-proxy-extras", "mcp", "orjson", "pynacl", "python-multipart", "pyyaml", "rich", "rq", "uvicorn", "uvloop", "websockets"] [metadata] lock-version = "2.1" python-versions = ">=3.8.1,<4.0, !=3.9.7" -content-hash = "b0e92d112a8265cbd9a75ad30b2a0bb2ad429ea86ebc34c79f2b4eba03c1d364" +content-hash = "2eb67698c4810b12b9c31ef478721b952236438764b5b52bcb67b97884b2320c" diff --git a/pyproject.toml b/pyproject.toml index 9af558ad30..771bcc8fff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,6 +57,7 @@ redisvl = {version = "^0.4.1", optional = true, markers = "python_version >= '3. mcp = {version = "1.5.0", optional = true, python = ">=3.10"} litellm-proxy-extras = {version = "0.1.18", optional = true} rich = {version = "13.7.1", optional = true} +litellm-enterprise = {version = "0.1.1", optional = true} [tool.poetry.extras] proxy = [ @@ -78,6 +79,7 @@ proxy = [ "boto3", "mcp", "litellm-proxy-extras", + "litellm-enterprise", "rich", ] diff --git a/requirements.txt b/requirements.txt index b803516d3e..08ed4c7534 100644 --- a/requirements.txt +++ b/requirements.txt @@ -52,4 +52,8 @@ tenacity==8.2.3 # for retrying requests, when litellm.num_retries set pydantic==2.10.2 # proxy + openai req. jsonschema==4.22.0 # validating json schema websockets==13.1.0 # for realtime API -#### + +######################## +# LITELLM ENTERPRISE DEPENDENCIES +######################## +litellm-enterprise==0.1.1 diff --git a/tests/code_coverage_tests/liccheck.ini b/tests/code_coverage_tests/liccheck.ini index c5ecc898f2..e3fd3922ca 100644 --- a/tests/code_coverage_tests/liccheck.ini +++ b/tests/code_coverage_tests/liccheck.ini @@ -86,4 +86,5 @@ detect-secrets: >=1.5.0 # MIT License importlib-metadata: >=6.8.0 # Apache 2.0 License tokenizers: >=0.20.2 # Apache 2.0 License jinja2: >=3.1.4 # BSD 3-Clause License -litellm-proxy-extras: >=0.1.1 # MIT License \ No newline at end of file +litellm-proxy-extras: >=0.1.1 # MIT License +litellm-enterprise: >=0.1.1 # LiteLLM Enterprise License \ No newline at end of file diff --git a/tests/litellm/enterprise/enterprise_callbacks/send_emails/test_base_email.py b/tests/litellm/enterprise/enterprise_callbacks/send_emails/test_base_email.py index c46912e3a8..22d553e87c 100644 --- a/tests/litellm/enterprise/enterprise_callbacks/send_emails/test_base_email.py +++ b/tests/litellm/enterprise/enterprise_callbacks/send_emails/test_base_email.py @@ -8,7 +8,10 @@ from fastapi.testclient import TestClient sys.path.insert(0, os.path.abspath("../../..")) -from enterprise.enterprise_callbacks.send_emails.base_email import BaseEmailLogger +from litellm_enterprise.enterprise_callbacks.send_emails.base_email import ( + BaseEmailLogger, +) + from litellm.proxy._types import Litellm_EntityType, WebhookEvent from litellm.types.enterprise.enterprise_callbacks.send_emails import ( SendKeyCreatedEmailEvent, diff --git a/tests/litellm/enterprise/enterprise_callbacks/send_emails/test_endpoints.py b/tests/litellm/enterprise/enterprise_callbacks/send_emails/test_endpoints.py index 2012b728b7..8169c457c2 100644 --- a/tests/litellm/enterprise/enterprise_callbacks/send_emails/test_endpoints.py +++ b/tests/litellm/enterprise/enterprise_callbacks/send_emails/test_endpoints.py @@ -9,7 +9,7 @@ from fastapi.testclient import TestClient sys.path.insert(0, os.path.abspath("../../..")) -from enterprise.enterprise_callbacks.send_emails.endpoints import ( +from litellm_enterprise.enterprise_callbacks.send_emails.endpoints import ( _get_email_settings, _save_email_settings, get_email_event_settings, @@ -17,6 +17,7 @@ from enterprise.enterprise_callbacks.send_emails.endpoints import ( router, update_event_settings, ) + from litellm.types.enterprise.enterprise_callbacks.send_emails import ( DefaultEmailSettings, EmailEvent, @@ -146,7 +147,7 @@ async def test_get_email_event_settings(mock_prisma_client, mock_user_api_key_au # Setup mocks with mock.patch("litellm.proxy.proxy_server.prisma_client", mock_prisma_client): with mock.patch( - "enterprise.enterprise_callbacks.send_emails.endpoints._get_email_settings", + "litellm_enterprise.enterprise_callbacks.send_emails.endpoints._get_email_settings", side_effect=mock_get_settings, ): # Call the endpoint function directly @@ -185,11 +186,11 @@ async def test_update_event_settings(mock_prisma_client, mock_user_api_key_auth) # Setup mocks with mock.patch("litellm.proxy.proxy_server.prisma_client", mock_prisma_client): with mock.patch( - "enterprise.enterprise_callbacks.send_emails.endpoints._get_email_settings", + "litellm_enterprise.enterprise_callbacks.send_emails.endpoints._get_email_settings", side_effect=mock_get_settings, ): with mock.patch( - "enterprise.enterprise_callbacks.send_emails.endpoints._save_email_settings", + "litellm_enterprise.enterprise_callbacks.send_emails.endpoints._save_email_settings", side_effect=mock_save_settings, ): # Create request with updated settings @@ -227,7 +228,7 @@ async def test_reset_event_settings(mock_prisma_client, mock_user_api_key_auth): # Setup mocks with mock.patch("litellm.proxy.proxy_server.prisma_client", mock_prisma_client): with mock.patch( - "enterprise.enterprise_callbacks.send_emails.endpoints._save_email_settings", + "litellm_enterprise.enterprise_callbacks.send_emails.endpoints._save_email_settings", side_effect=mock_save_settings, ): # Call the endpoint function directly diff --git a/tests/litellm/enterprise/enterprise_callbacks/send_emails/test_resend_email.py b/tests/litellm/enterprise/enterprise_callbacks/send_emails/test_resend_email.py index 980fd49b94..dffa343d99 100644 --- a/tests/litellm/enterprise/enterprise_callbacks/send_emails/test_resend_email.py +++ b/tests/litellm/enterprise/enterprise_callbacks/send_emails/test_resend_email.py @@ -7,7 +7,9 @@ from httpx import Response sys.path.insert(0, os.path.abspath("../../..")) -from enterprise.enterprise_callbacks.send_emails.resend_email import ResendEmailLogger +from litellm_enterprise.enterprise_callbacks.send_emails.resend_email import ( + ResendEmailLogger, +) @pytest.fixture @@ -19,7 +21,7 @@ def mock_env_vars(): @pytest.fixture def mock_httpx_client(): with mock.patch( - "enterprise.enterprise_callbacks.send_emails.resend_email.get_async_httpx_client" + "litellm_enterprise.enterprise_callbacks.send_emails.resend_email.get_async_httpx_client" ) as mock_client: # Create a mock response mock_response = mock.AsyncMock(spec=Response) diff --git a/tests/logging_callback_tests/test_generic_api_callback.py b/tests/logging_callback_tests/test_generic_api_callback.py index 0bdec437fe..c033f323ec 100644 --- a/tests/logging_callback_tests/test_generic_api_callback.py +++ b/tests/logging_callback_tests/test_generic_api_callback.py @@ -28,7 +28,7 @@ from litellm.types.utils import ( ) verbose_logger.setLevel(logging.DEBUG) -from enterprise.enterprise_callbacks.generic_api_callback import GenericAPILogger +from litellm_enterprise.enterprise_callbacks.generic_api_callback import GenericAPILogger diff --git a/tests/logging_callback_tests/test_unit_tests_init_callbacks.py b/tests/logging_callback_tests/test_unit_tests_init_callbacks.py index f6edf71498..71aece0407 100644 --- a/tests/logging_callback_tests/test_unit_tests_init_callbacks.py +++ b/tests/logging_callback_tests/test_unit_tests_init_callbacks.py @@ -42,8 +42,8 @@ from litellm.integrations.azure_storage.azure_storage import AzureBlobStorageLog from litellm.integrations.agentops import AgentOps from litellm.integrations.humanloop import HumanloopLogger from litellm.proxy.hooks.dynamic_rate_limiter import _PROXY_DynamicRateLimitHandler -from enterprise.enterprise_callbacks.generic_api_callback import GenericAPILogger -from enterprise.enterprise_callbacks.send_emails.resend_email import ResendEmailLogger +from litellm_enterprise.enterprise_callbacks.generic_api_callback import GenericAPILogger +from litellm_enterprise.enterprise_callbacks.send_emails.resend_email import ResendEmailLogger from unittest.mock import patch # clear prometheus collectors / registry