fix: use safe orjson imports and fix Black formatting for CI

Replace hard orjson removal with try/except safe imports that preserve
perf when orjson is available. Revert transformation.py to response.json()
matching main. Fix test assertions to use safe_dumps() for orjson-agnostic
JSON comparison. Apply Black formatting to sidecar files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Krrish Dholakia 2026-03-14 15:35:22 -07:00
parent 288bd225c2
commit 748ea4ece9
7 changed files with 494 additions and 405 deletions

View file

@ -68,7 +68,11 @@ class LiteLLMSidecarTransport(httpx.AsyncBaseTransport):
provider_base = f"{parsed.scheme}://{parsed.host}"
if parsed.port and parsed.port not in (80, 443):
provider_base += f":{parsed.port}"
path = parsed.raw_path.decode("ascii") if isinstance(parsed.raw_path, bytes) else str(parsed.raw_path)
path = (
parsed.raw_path.decode("ascii")
if isinstance(parsed.raw_path, bytes)
else str(parsed.raw_path)
)
# Extract auth header if present
api_key = ""

View file

@ -2,7 +2,6 @@
OpenAI-like chat completion transformation
"""
import json
from typing import TYPE_CHECKING, Any, List, Optional, Tuple, Union
import httpx
@ -95,7 +94,7 @@ class OpenAILikeChatConfig(OpenAIGPTConfig):
custom_llm_provider: Optional[str],
base_model: Optional[str],
) -> ModelResponse:
response_json = json.loads(response.content)
response_json = response.json()
logging_obj.post_call(
input=messages,
api_key="",

View file

@ -946,16 +946,22 @@ async def proxy_startup_event(app: FastAPI): # noqa: PLR0915
shared_aiohttp_session = await _initialize_shared_aiohttp_session()
## Initialize Rust sidecar client (optional, for high-perf forwarding)
_use_sidecar = os.environ.get("USE_SIDECAR", "").lower() == "true" or general_settings.get("use_sidecar", False)
_use_sidecar = os.environ.get(
"USE_SIDECAR", ""
).lower() == "true" or general_settings.get("use_sidecar", False)
if _use_sidecar:
# Ensure env var is set so AsyncHTTPHandler._should_use_sidecar_transport() picks it up
os.environ["USE_SIDECAR"] = "true"
from litellm.proxy.sidecar_client import init_sidecar_client
_sidecar_port = int(os.environ.get("SIDECAR_PORT", general_settings.get("sidecar_port", 8787)))
_sidecar_port = int(
os.environ.get("SIDECAR_PORT", general_settings.get("sidecar_port", 8787))
)
os.environ.setdefault("SIDECAR_PORT", str(_sidecar_port))
_sidecar_binary = os.environ.get("SIDECAR_BINARY", general_settings.get("sidecar_binary", ""))
_sidecar_binary = os.environ.get(
"SIDECAR_BINARY", general_settings.get("sidecar_binary", "")
)
await init_sidecar_client(
port=_sidecar_port,
binary=_sidecar_binary or None,

View file

@ -58,9 +58,7 @@ class SidecarClient:
self._healthy = await self._check_health()
if self._healthy:
verbose_proxy_logger.info(
f"Sidecar client connected to {self.sidecar_url}"
)
verbose_proxy_logger.info(f"Sidecar client connected to {self.sidecar_url}")
else:
verbose_proxy_logger.warning(
f"Sidecar not available at {self.sidecar_url}, will use fallback"
@ -144,9 +142,7 @@ class SidecarClient:
self._process.terminate()
try:
await asyncio.wait_for(
asyncio.get_event_loop().run_in_executor(
None, self._process.wait
),
asyncio.get_event_loop().run_in_executor(None, self._process.wait),
timeout=5,
)
except asyncio.TimeoutError:

View file

@ -18,6 +18,7 @@ from litellm.integrations._types.open_inference import (
)
from litellm.integrations.arize.arize import ArizeLogger
from litellm.integrations.custom_logger import CustomLogger
from litellm.litellm_core_utils.safe_json_dumps import safe_dumps
from litellm.types.utils import Choices, StandardCallbackDynamicParams
@ -88,7 +89,7 @@ def test_arize_set_attributes():
# Metadata attached to the span
span.set_attribute.assert_any_call(
SpanAttributes.METADATA, json.dumps({"key_1": "value_1", "key_2": None})
SpanAttributes.METADATA, safe_dumps({"key_1": "value_1", "key_2": None})
)
# Basic LLM information
@ -147,7 +148,7 @@ def test_arize_set_attributes():
# Invocation parameters
span.set_attribute.assert_any_call(
SpanAttributes.LLM_INVOCATION_PARAMETERS, '{"user": "test_user"}'
SpanAttributes.LLM_INVOCATION_PARAMETERS, safe_dumps({"user": "test_user"})
)
# User ID
@ -178,10 +179,20 @@ def test_arize_set_attributes_responses_api():
Verifies that multiple output types are correctly handled.
"""
from unittest.mock import MagicMock
from litellm.types.llms.openai import ResponsesAPIResponse, ResponseAPIUsage, OutputTokensDetails
from openai.types.responses import ResponseReasoningItem, ResponseOutputMessage, ResponseOutputText
from openai.types.responses import (
ResponseOutputMessage,
ResponseOutputText,
ResponseReasoningItem,
)
from openai.types.responses.response_reasoning_item import Summary
from litellm.types.llms.openai import (
OutputTokensDetails,
ResponseAPIUsage,
ResponsesAPIResponse,
)
span = MagicMock() # Mocked tracing span to test attribute setting
# Construct kwargs to simulate a real LLM request scenario
@ -212,11 +223,8 @@ def test_arize_set_attributes_responses_api():
id="reasoning-001",
type="reasoning",
summary=[
Summary(
text="First, I need to analyze...",
type="summary_text"
)
]
Summary(text="First, I need to analyze...", type="summary_text")
],
),
ResponseOutputMessage(
id="msg-001",
@ -229,17 +237,15 @@ def test_arize_set_attributes_responses_api():
text="The answer is 42",
type="output_text",
)
]
)
],
),
],
usage=ResponseAPIUsage(
input_tokens=120,
output_tokens=250,
total_tokens=370,
output_tokens_details=OutputTokensDetails(
reasoning_tokens=180
)
)
output_tokens_details=OutputTokensDetails(reasoning_tokens=180),
),
)
ArizeLogger.set_arize_attributes(span, kwargs, response_obj)
@ -247,21 +253,18 @@ def test_arize_set_attributes_responses_api():
# Verify reasoning summary was set (index 0)
span.set_attribute.assert_any_call(
f"{SpanAttributes.LLM_OUTPUT_MESSAGES}.0.{MessageAttributes.MESSAGE_REASONING_SUMMARY}",
"First, I need to analyze..."
"First, I need to analyze...",
)
# Verify message content was set (index 1)
span.set_attribute.assert_any_call(
SpanAttributes.OUTPUT_VALUE,
"The answer is 42"
)
span.set_attribute.assert_any_call(SpanAttributes.OUTPUT_VALUE, "The answer is 42")
span.set_attribute.assert_any_call(
f"{SpanAttributes.LLM_OUTPUT_MESSAGES}.1.{MessageAttributes.MESSAGE_CONTENT}",
"The answer is 42"
"The answer is 42",
)
span.set_attribute.assert_any_call(
f"{SpanAttributes.LLM_OUTPUT_MESSAGES}.1.{MessageAttributes.MESSAGE_ROLE}",
"assistant"
"assistant",
)
# Verify token counts including reasoning tokens
@ -335,42 +338,34 @@ def test_construct_dynamic_arize_headers():
# Test with all parameters present
dynamic_params_full = StandardCallbackDynamicParams(
arize_api_key="test_api_key",
arize_space_id="test_space_id"
arize_api_key="test_api_key", arize_space_id="test_space_id"
)
arize_logger = ArizeLogger()
headers = arize_logger.construct_dynamic_otel_headers(dynamic_params_full)
expected_headers = {
"api_key": "test_api_key",
"arize-space-id": "test_space_id"
}
expected_headers = {"api_key": "test_api_key", "arize-space-id": "test_space_id"}
assert headers == expected_headers
# Test with only space_id
dynamic_params_space_id_only = StandardCallbackDynamicParams(
arize_space_id="test_space_id"
)
headers = arize_logger.construct_dynamic_otel_headers(dynamic_params_space_id_only)
expected_headers = {
"arize-space-id": "test_space_id"
}
expected_headers = {"arize-space-id": "test_space_id"}
assert headers == expected_headers
# Test with empty parameters dict
dynamic_params_empty = StandardCallbackDynamicParams()
headers = arize_logger.construct_dynamic_otel_headers(dynamic_params_empty)
assert headers == {}
# test with space key and api key
dynamic_params_space_key_and_api_key = StandardCallbackDynamicParams(
arize_space_key="test_space_key",
arize_api_key="test_api_key"
arize_space_key="test_space_key", arize_api_key="test_api_key"
)
headers = arize_logger.construct_dynamic_otel_headers(dynamic_params_space_key_and_api_key)
expected_headers = {
"arize-space-id": "test_space_key",
"api_key": "test_api_key"
}
headers = arize_logger.construct_dynamic_otel_headers(
dynamic_params_space_key_and_api_key
)
expected_headers = {"arize-space-id": "test_space_key", "api_key": "test_api_key"}

View file

@ -4,6 +4,7 @@ from unittest.mock import AsyncMock, MagicMock
import pytest
from litellm.litellm_core_utils.safe_json_dumps import safe_dumps
from litellm.proxy.agent_endpoints.agent_registry import AgentRegistry
@ -110,5 +111,5 @@ async def test_update_agent_in_db_preserves_explicit_static_headers_and_extra_he
call_kwargs = mock_update.call_args.kwargs
update_data = call_kwargs["data"]
assert update_data["static_headers"] == '{"Authorization": "Bearer xyz"}'
assert update_data["static_headers"] == safe_dumps({"Authorization": "Bearer xyz"})
assert update_data["extra_headers"] == ["X-Custom-Header"]