litellm/tests/sdk_function_trace/fixtures.py
yujonglee 198906495f
refactor(python-bridge): split routes and add shared function tracing (#39031)
* refactor(python-bridge): split non-streaming bridge modules

* refactor(python-bridge): bring shared function tracing into route layer

* feat(dev): list Python route functions and call sites

* feat(dev): list Rust route functions and call sites

* docs(dev): record OCR parity gaps across Python and Rust

* feat(dev): list executed SDK calls with runtime tracing

* feat(dev): report Python vs Rust SDK pipeline steps in one CLI

* feat(dev): side-by-side pipeline step report in compare CLI

* fix(dev): drop invalid Final annotations in compare cell loop

* feat(dev): blue python-only and yellow rust-only steps in compare CLI

* feat(dev): vertical layout with section spacing in compare CLI

* fix(dev): validate SDK trace stages across sync and async routes

* refactor(rust): align SDK route call structure with Python

* refactor(python-bridge): share sync and async route call wrappers

* refactor(dev): split compare CLI into fixtures, runtime, and report modules

* fix(ci): run SDK trace tests and satisfy test lint
2026-09-02 16:26:35 -07:00

200 lines
6.4 KiB
Python

from __future__ import annotations
import base64
import io
import json
import wave
from collections.abc import Callable
from dataclasses import dataclass
from typing import Final, Protocol, cast
from tests.sdk_function_trace.mock_provider import MockProviderResponse
from tests.sdk_function_trace.steps import Engine
ANTHROPIC_MODEL: Final = "claude-sonnet-5"
OCR_MODEL: Final = "mistral-ocr-latest"
AUDIO_MODEL: Final = "mistral.voxtral-mini-3b-2507"
class SdkCall(Protocol):
def __call__(self, **kwargs: object) -> object: ...
@dataclass(frozen=True, slots=True)
class Fixture:
kwargs: dict[str, object]
provider_response: MockProviderResponse
@dataclass(frozen=True, slots=True)
class RouteSpec:
label: str
python_entrypoints: tuple[str, str]
rust_entrypoints: tuple[str, str]
fixture: Callable[[Engine], Fixture]
@dataclass(frozen=True, slots=True)
class Invocation:
function: SdkCall
kwargs: dict[str, object]
provider_response: MockProviderResponse
label: str
def audio_bytes() -> bytes:
with io.BytesIO() as buffer:
with wave.open(buffer, "wb") as audio:
audio.setnchannels(1)
audio.setsampwidth(2)
audio.setframerate(16000)
audio.writeframes(b"\x00\x00" * 1600)
return buffer.getvalue()
def _anthropic_message_response() -> MockProviderResponse:
body: Final = {
"id": "msg_trace",
"type": "message",
"role": "assistant",
"model": ANTHROPIC_MODEL,
"content": [{"type": "text", "text": "hello"}],
"stop_reason": "end_turn",
"stop_sequence": None,
"usage": {"input_tokens": 2, "output_tokens": 3},
}
return MockProviderResponse(200, (("content-type", "application/json"),), json.dumps(body).encode())
def _conversation() -> dict[str, object]:
return {"messages": [{"role": "user", "content": "hello"}], "max_tokens": 16}
def _ocr_fixture(engine: Engine) -> Fixture:
return Fixture(
kwargs={
"model": f"mistral/{OCR_MODEL}",
"document": {"type": "document_url", "document_url": "https://example.com/document.pdf"},
**({"optional_params": {"pages": [0]}} if engine == "rust" else {"pages": [0]}),
},
provider_response=MockProviderResponse(
200,
(("content-type", "application/json"),),
json.dumps(
{
"pages": [{"index": 0, "markdown": "hello"}],
"model": OCR_MODEL,
"usage_info": {"pages_processed": 1},
}
).encode(),
),
)
def _chat_completions_fixture(engine: Engine) -> Fixture:
conversation: Final = _conversation()
payload: Final = (
{"messages": conversation["messages"], "optional_params": {"max_tokens": 16}}
if engine == "rust"
else conversation
)
return Fixture(
kwargs={"model": f"anthropic/{ANTHROPIC_MODEL}", **payload},
provider_response=_anthropic_message_response(),
)
def _messages_fixture(engine: Engine) -> Fixture:
conversation: Final = _conversation()
payload: Final = {"body": {**conversation, "model": ANTHROPIC_MODEL}} if engine == "rust" else conversation
return Fixture(
kwargs={"model": f"anthropic/{ANTHROPIC_MODEL}", **payload},
provider_response=_anthropic_message_response(),
)
def _transcription_fixture(engine: Engine) -> Fixture:
credentials: Final = {
"aws_access_key_id": "test-access",
"aws_secret_access_key": "test-secret",
"aws_region_name": "us-east-1",
}
payload: Final = (
{
"audio": {"data": base64.b64encode(audio_bytes()).decode(), "format": "wav"},
"optional_params": credentials,
}
if engine == "rust"
else {"file": ("sample.wav", audio_bytes(), "audio/wav"), **credentials}
)
return Fixture(
kwargs={"model": f"bedrock/{AUDIO_MODEL}", **payload},
provider_response=MockProviderResponse(
200,
(("content-type", "application/json"),),
json.dumps(
{
"output": {"message": {"role": "assistant", "content": [{"text": "hello"}]}},
"stopReason": "end_turn",
"usage": {"inputTokens": 2, "outputTokens": 3, "totalTokens": 5},
}
).encode(),
),
)
ROUTE_SPECS: Final[dict[str, RouteSpec]] = {
"chat_completions": RouteSpec(
label="anthropic",
python_entrypoints=("completion", "acompletion"),
rust_entrypoints=("chat_completions", "achat_completions"),
fixture=_chat_completions_fixture,
),
"audio_transcription": RouteSpec(
label="bedrock (Rust-only provider; Python trace covers SDK dispatch)",
python_entrypoints=("transcription", "atranscription"),
rust_entrypoints=("transcription", "atranscription"),
fixture=_transcription_fixture,
),
"messages": RouteSpec(
label="anthropic",
python_entrypoints=("create", "acreate"),
rust_entrypoints=("messages", "amessages"),
fixture=_messages_fixture,
),
"ocr": RouteSpec(
label="mistral",
python_entrypoints=("ocr", "aocr"),
rust_entrypoints=("ocr", "aocr"),
fixture=_ocr_fixture,
),
}
ROUTES: Final = tuple(ROUTE_SPECS)
def sdk_invocation(route: str, *, engine: Engine, asynchronous: bool) -> Invocation:
import litellm
from litellm.anthropic_interface import messages as sdk_messages
from litellm.rust_bridge import get_native_bridge
rust: Final = engine == "rust"
bridge: Final = get_native_bridge() if rust else None
if rust and bridge is None:
raise RuntimeError("Build the native extension first: maturin develop")
spec: Final = ROUTE_SPECS.get(route)
if spec is None:
raise ValueError(f"Unknown route: {route}")
fixture: Final = spec.fixture(engine)
owner: Final = bridge if rust else (sdk_messages if route == "messages" else litellm)
entrypoint: Final = (spec.rust_entrypoints if rust else spec.python_entrypoints)[int(asynchronous)]
return Invocation(
function=cast(SdkCall, getattr(owner, entrypoint)),
kwargs={
**fixture.kwargs,
"api_key": "test-key",
**({"trace": True, "timeout_seconds": 5} if rust else {"timeout": 5}),
},
provider_response=fixture.provider_response,
label=spec.label,
)