mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-05 08:07:05 +00:00
* 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
200 lines
6.4 KiB
Python
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,
|
|
)
|