From 4a470aec6a1684b841b2f6ea178d818bfd4fe1d0 Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Thu, 2 Apr 2026 21:43:16 +0530 Subject: [PATCH] fix: resolve CodeQL module-level cyclic import errors Made-with: Cursor --- litellm/integrations/arize/arize.py | 7 +++- litellm/integrations/datadog/datadog.py | 5 ++- .../integrations/datadog/datadog_llm_obs.py | 29 +++++++------- litellm/integrations/opentelemetry.py | 4 +- litellm/llms/bedrock/chat/invoke_handler.py | 39 +++++++++++-------- litellm/llms/openai/openai.py | 9 +++-- .../llms/vertex_ai/gemini/transformation.py | 4 +- .../mcp_server/rest_endpoints.py | 6 ++- 8 files changed, 63 insertions(+), 40 deletions(-) diff --git a/litellm/integrations/arize/arize.py b/litellm/integrations/arize/arize.py index fe2f9f41f1b..2e2d1eaacf1 100644 --- a/litellm/integrations/arize/arize.py +++ b/litellm/integrations/arize/arize.py @@ -10,7 +10,6 @@ from typing import TYPE_CHECKING, Any, Optional, Union from litellm.integrations.arize import _utils from litellm.integrations.arize._utils import ArizeOTELAttributes -from litellm.integrations.opentelemetry import OpenTelemetry from litellm.types.integrations.arize import ArizeConfig from litellm.types.services import ServiceLoggerPayload from litellm.types.utils import StandardCallbackDynamicParams @@ -18,13 +17,19 @@ from litellm.types.utils import StandardCallbackDynamicParams if TYPE_CHECKING: from opentelemetry.trace import Span as _Span + from litellm.integrations.opentelemetry import OpenTelemetry as _OpenTelemetry from litellm.types.integrations.arize import Protocol as _Protocol Protocol = _Protocol Span = Union[_Span, Any] + OpenTelemetry = _OpenTelemetry else: Protocol = Any Span = Any + try: + from litellm.integrations.opentelemetry import OpenTelemetry + except ImportError: + OpenTelemetry = None # type: ignore class ArizeLogger(OpenTelemetry): diff --git a/litellm/integrations/datadog/datadog.py b/litellm/integrations/datadog/datadog.py index df8e91d59d9..6a1db8deb99 100644 --- a/litellm/integrations/datadog/datadog.py +++ b/litellm/integrations/datadog/datadog.py @@ -28,7 +28,6 @@ from litellm._logging import verbose_logger from litellm._uuid import uuid from litellm.integrations.custom_batch_logger import CustomBatchLogger from litellm.integrations.datadog.datadog_handler import ( - get_datadog_base_url_from_env, get_datadog_hostname, get_datadog_service, get_datadog_source, @@ -120,6 +119,10 @@ class DataDogLogger( self._configure_dd_direct_api() # Optional override for testing + from litellm.integrations.datadog.datadog_handler import ( + get_datadog_base_url_from_env, + ) + dd_base_url = get_datadog_base_url_from_env() if dd_base_url: self.intake_url = f"{dd_base_url}/api/v2/logs" diff --git a/litellm/integrations/datadog/datadog_llm_obs.py b/litellm/integrations/datadog/datadog_llm_obs.py index fa3266838e8..5d57dcc3d86 100644 --- a/litellm/integrations/datadog/datadog_llm_obs.py +++ b/litellm/integrations/datadog/datadog_llm_obs.py @@ -19,7 +19,6 @@ from litellm._logging import verbose_logger from litellm._uuid import uuid from litellm.integrations.custom_batch_logger import CustomBatchLogger from litellm.integrations.datadog.datadog_handler import ( - get_datadog_base_url_from_env, get_datadog_service, get_datadog_tags, ) @@ -80,6 +79,10 @@ class DataDogLLMObsLogger(CustomBatchLogger): self._configure_dd_direct_api() # Optional override for testing + from litellm.integrations.datadog.datadog_handler import ( + get_datadog_base_url_from_env, + ) + dd_base_url = get_datadog_base_url_from_env() if dd_base_url: self.intake_url = f"{dd_base_url}/api/intake/llm-obs/v1/trace/spans" @@ -341,9 +344,9 @@ class DataDogLLMObsLogger(CustomBatchLogger): if standard_logging_payload.get("status") == "failure": # Try to get structured error information first - error_information: Optional[ - StandardLoggingPayloadErrorInformation - ] = standard_logging_payload.get("error_information") + error_information: Optional[StandardLoggingPayloadErrorInformation] = ( + standard_logging_payload.get("error_information") + ) if error_information: error_info = DDLLMObsError( @@ -613,9 +616,9 @@ class DataDogLLMObsLogger(CustomBatchLogger): latency_metrics["litellm_overhead_time_ms"] = litellm_overhead_ms # Guardrail overhead latency - guardrail_info: Optional[ - list[StandardLoggingGuardrailInformation] - ] = standard_logging_payload.get("guardrail_information") + guardrail_info: Optional[list[StandardLoggingGuardrailInformation]] = ( + standard_logging_payload.get("guardrail_information") + ) if guardrail_info is not None: total_duration = 0.0 for info in guardrail_info: @@ -785,15 +788,15 @@ class DataDogLLMObsLogger(CustomBatchLogger): if function_arguments: # Store arguments as JSON string for Datadog if isinstance(function_arguments, str): - kv_pairs[ - f"tool_calls.{idx}.function.arguments" - ] = function_arguments + kv_pairs[f"tool_calls.{idx}.function.arguments"] = ( + function_arguments + ) else: import json - kv_pairs[ - f"tool_calls.{idx}.function.arguments" - ] = json.dumps(function_arguments) + kv_pairs[f"tool_calls.{idx}.function.arguments"] = ( + json.dumps(function_arguments) + ) except (KeyError, TypeError, ValueError) as e: verbose_logger.debug( f"DataDogLLMObs: Error processing tool call {idx}: {str(e)}" diff --git a/litellm/integrations/opentelemetry.py b/litellm/integrations/opentelemetry.py index 8d708ea939c..e23421dcf58 100644 --- a/litellm/integrations/opentelemetry.py +++ b/litellm/integrations/opentelemetry.py @@ -1,7 +1,7 @@ import os -import types from dataclasses import dataclass from datetime import datetime +from types import MethodType from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union, cast import litellm @@ -2112,7 +2112,7 @@ class OpenTelemetry(CustomLogger): setattr( exporter, "export", - types.MethodType(_export_with_failure_tracking, exporter), + MethodType(_export_with_failure_tracking, exporter), ) setattr(exporter, "_litellm_failure_tracking_wrapped", True) return exporter diff --git a/litellm/llms/bedrock/chat/invoke_handler.py b/litellm/llms/bedrock/chat/invoke_handler.py index 5f335dfdb8e..bb4d1360b30 100644 --- a/litellm/llms/bedrock/chat/invoke_handler.py +++ b/litellm/llms/bedrock/chat/invoke_handler.py @@ -70,7 +70,6 @@ from ..base_aws_llm import BaseAWSLLM from ..common_utils import ( BedrockError, ModelResponseIterator, - apply_embedded_bedrock_region_from_model_path, get_bedrock_tool_name, ) @@ -204,11 +203,13 @@ async def make_call( if client is None: client = get_async_httpx_client( llm_provider=litellm.LlmProviders.BEDROCK, - params={"ssl_verify": logging_obj.litellm_params.get("ssl_verify")} - if logging_obj - and logging_obj.litellm_params - and logging_obj.litellm_params.get("ssl_verify") - else None, + params=( + {"ssl_verify": logging_obj.litellm_params.get("ssl_verify")} + if logging_obj + and logging_obj.litellm_params + and logging_obj.litellm_params.get("ssl_verify") + else None + ), ) # Create a new client if none provided response = await client.post( @@ -298,11 +299,13 @@ def make_sync_call( try: if client is None: client = _get_httpx_client( - params={"ssl_verify": logging_obj.litellm_params.get("ssl_verify")} - if logging_obj - and logging_obj.litellm_params - and logging_obj.litellm_params.get("ssl_verify") - else None + params=( + {"ssl_verify": logging_obj.litellm_params.get("ssl_verify")} + if logging_obj + and logging_obj.litellm_params + and logging_obj.litellm_params.get("ssl_verify") + else None + ) ) response = client.post( @@ -552,9 +555,9 @@ class BedrockLLM(BaseAWSLLM): content=None, ) model_response.choices[0].message = _message # type: ignore - model_response._hidden_params[ - "original_response" - ] = outputText # allow user to access raw anthropic tool calling response + model_response._hidden_params["original_response"] = ( + outputText # allow user to access raw anthropic tool calling response + ) if ( _is_function_call is True and stream is not None @@ -887,9 +890,9 @@ class BedrockLLM(BaseAWSLLM): ): # completion(top_k=3) > anthropic_config(top_k=3) <- allows for dynamic variables to be passed in inference_params[k] = v if stream is True: - inference_params[ - "stream" - ] = True # cohere requires stream = True in inference params + inference_params["stream"] = ( + True # cohere requires stream = True in inference params + ) data = json.dumps({"prompt": prompt, **inference_params}) elif provider == "anthropic": if self.is_claude_messages_api_model(model): @@ -1284,6 +1287,8 @@ class BedrockLLM(BaseAWSLLM): else: modelId = model + from ..common_utils import apply_embedded_bedrock_region_from_model_path + modelId = apply_embedded_bedrock_region_from_model_path( modelId, optional_params ) diff --git a/litellm/llms/openai/openai.py b/litellm/llms/openai/openai.py index be542677480..4e934c4f95e 100644 --- a/litellm/llms/openai/openai.py +++ b/litellm/llms/openai/openai.py @@ -36,7 +36,6 @@ from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLogging from litellm.litellm_core_utils.logging_utils import track_llm_api_timing from litellm.llms.base_llm.base_model_iterator import BaseModelResponseIterator from litellm.llms.base_llm.chat.transformation import BaseConfig, BaseLLMException -from litellm.llms.bedrock.chat.invoke_handler import MockResponseIterator from litellm.types.utils import ( EmbeddingResponse, ImageResponse, @@ -562,9 +561,9 @@ class OpenAIChatCompletion(BaseLLM, BaseOpenAILLM): kwargs_with_provider = ( litellm_params.copy() if litellm_params else {} ) - kwargs_with_provider[ - "custom_llm_provider" - ] = custom_llm_provider + kwargs_with_provider["custom_llm_provider"] = ( + custom_llm_provider + ) # For OpenAI Chat Completions, use the chat completion agentic loop method agentic_response = ( @@ -596,6 +595,8 @@ class OpenAIChatCompletion(BaseLLM, BaseOpenAILLM): model: str, stream_options: Optional[dict] = None, ) -> CustomStreamWrapper: + from litellm.llms.bedrock.chat.invoke_handler import MockResponseIterator + completion_stream = MockResponseIterator(model_response=response) streaming_response = CustomStreamWrapper( completion_stream=completion_stream, diff --git a/litellm/llms/vertex_ai/gemini/transformation.py b/litellm/llms/vertex_ai/gemini/transformation.py index 723fa2b1616..35172ecb8b1 100644 --- a/litellm/llms/vertex_ai/gemini/transformation.py +++ b/litellm/llms/vertex_ai/gemini/transformation.py @@ -3,6 +3,7 @@ Transformation logic from OpenAI format to Gemini format. Why separate file? Make it easy to see how transformation works """ + import json import os from typing import TYPE_CHECKING, Dict, List, Literal, Optional, Tuple, Union, cast @@ -23,7 +24,6 @@ from litellm.litellm_core_utils.prompt_templates.factory import ( response_schema_prompt, ) from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler -from litellm.llms.vertex_ai.common_utils import pop_vertex_request_labels from litellm.types.files import ( get_file_mime_type_for_file_type, get_file_type_from_extension, @@ -713,6 +713,8 @@ def _transform_request_body( # noqa: PLR0915 config_fields = GenerationConfig.__annotations__.keys() # labels: optional explicit param and/or metadata.requester_metadata (OpenAI metadata) + from litellm.llms.vertex_ai.common_utils import pop_vertex_request_labels + labels = pop_vertex_request_labels(optional_params, litellm_params) filtered_params = { diff --git a/litellm/proxy/_experimental/mcp_server/rest_endpoints.py b/litellm/proxy/_experimental/mcp_server/rest_endpoints.py index 32560a2211d..41ad7cac288 100644 --- a/litellm/proxy/_experimental/mcp_server/rest_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/rest_endpoints.py @@ -9,7 +9,7 @@ from litellm.proxy._experimental.mcp_server.ui_session_utils import ( build_effective_auth_contexts, ) from litellm.proxy._experimental.mcp_server.utils import merge_mcp_headers -from litellm.proxy._types import LitellmUserRoles, UserAPIKeyAuth +from litellm.proxy._types import UserAPIKeyAuth from litellm.proxy.auth.ip_address_utils import IPAddressUtils from litellm.proxy.auth.user_api_key_auth import user_api_key_auth from litellm.proxy.common_utils.http_parsing_utils import _safe_get_request_headers @@ -1027,6 +1027,8 @@ if MCP_AVAILABLE: """ Test if we can connect to the provided MCP server before adding it """ + from litellm.proxy._types import LitellmUserRoles + if LitellmUserRoles.PROXY_ADMIN != user_api_key_dict.user_role: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, @@ -1057,6 +1059,8 @@ if MCP_AVAILABLE: """ Preview tools available from MCP server before adding it """ + from litellm.proxy._types import LitellmUserRoles + if LitellmUserRoles.PROXY_ADMIN != user_api_key_dict.user_role: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN,