litellm/tests/benchmarks/test_inference_benchmarks.py
Yassin Kortam 1eb7122465
test(benchmarks): add CodSpeed benchmarks for inference, MCP and A2A hot paths (#31716)
Guard the per-request CPU cost of the chat completion, MCP tool and A2A
message transforms against regressions on every commit. All benchmarks are
pure in-process work with no network I/O so they stay deterministic under
CodSpeed's simulation mode, and they import under the base dependency set the
benchmark job installs.

Inference covers the full SDK overhead via mock_response (simple, multi-turn,
tools, streaming) plus convert_to_model_response_object as a deterministic
anchor. MCP covers the client-side tool translation and the proxy server-side
tool-name prefix round-trip. A2A covers the client request/response transforms
and the proxy server-ingress message conversion.

Adds the mcp and a2a-sdk packages to the benchmark run since those transform
modules need them, and broadens the workflow triggers to litellm_internal_staging
so the internal branch flow is benchmarked too.
2026-06-30 10:27:12 -07:00

113 lines
3.5 KiB
Python

"""
Performance benchmarks for the LLM inference (chat completion) hot path.
The end-to-end cases use ``mock_response`` so the full SDK overhead is exercised
-- provider resolution, request/response transformation, ``ModelResponse``
construction, token counting and cost calculation -- without any network I/O. The
``convert_to_model_response_object`` case isolates the provider-response to
``ModelResponse`` translation, the single deterministic core every non-streaming
completion runs.
"""
import pytest
import litellm
from litellm.litellm_core_utils.llm_response_utils.convert_dict_to_response import (
convert_to_model_response_object,
)
from litellm.types.utils import ModelResponse
SIMPLE_MESSAGES = [{"role": "user", "content": "Hello, how are you?"}]
MULTI_TURN_MESSAGES = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "What is the capital of France?"},
{
"role": "assistant",
"content": "The capital of France is Paris. It is known as the City of Light.",
},
{"role": "user", "content": "Tell me more about Paris."},
]
TOOL_DEFINITIONS = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather in a given location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA",
},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
},
"required": ["location"],
},
},
}
]
MOCK_RESPONSE = "The capital of France is Paris, the country's largest city and cultural centre."
PROVIDER_RESPONSE = {
"id": "chatcmpl-abc123",
"object": "chat.completion",
"created": 1700000000,
"model": "gpt-4o",
"choices": [
{
"index": 0,
"finish_reason": "stop",
"message": {"role": "assistant", "content": MOCK_RESPONSE},
}
],
"usage": {"prompt_tokens": 12, "completion_tokens": 16, "total_tokens": 28},
}
@pytest.mark.benchmark
def test_completion_simple_message():
"""Benchmark a single-message completion through the full SDK path."""
litellm.completion(model="gpt-4o", messages=SIMPLE_MESSAGES, mock_response=MOCK_RESPONSE)
@pytest.mark.benchmark
def test_completion_multi_turn():
"""Benchmark a multi-turn completion through the full SDK path."""
litellm.completion(model="gpt-4o", messages=MULTI_TURN_MESSAGES, mock_response=MOCK_RESPONSE)
@pytest.mark.benchmark
def test_completion_with_tools():
"""Benchmark a completion that has to process tool schemas."""
litellm.completion(
model="gpt-4o",
messages=SIMPLE_MESSAGES,
tools=TOOL_DEFINITIONS,
mock_response=MOCK_RESPONSE,
)
@pytest.mark.benchmark
def test_completion_streaming():
"""Benchmark consuming a full streamed completion (CustomStreamWrapper)."""
stream = litellm.completion(
model="gpt-4o",
messages=SIMPLE_MESSAGES,
mock_response=MOCK_RESPONSE,
stream=True,
)
for _ in stream:
pass
@pytest.mark.benchmark
def test_response_to_model_response_object():
"""Benchmark the provider-response to ModelResponse translation core."""
convert_to_model_response_object(
response_object=PROVIDER_RESPONSE,
model_response_object=ModelResponse(),
)