Added LiteLLM to the stack

This commit is contained in:
2025-08-18 09:40:50 +00:00
parent 0648c1968c
commit d220b04e32
2682 changed files with 533609 additions and 1 deletions

View File

@@ -0,0 +1,905 @@
import json
import os
import sys
from datetime import datetime
sys.path.insert(
0, os.path.abspath("../../../")
) # Adds the parent directory to the system path
import litellm
import pytest
from datetime import timedelta
from litellm.types.utils import (
ModelResponse,
Message,
Choices,
PromptTokensDetailsWrapper,
CompletionTokensDetailsWrapper,
Usage,
)
from litellm.litellm_core_utils.llm_response_utils.convert_dict_to_response import (
convert_to_model_response_object,
)
def test_convert_to_model_response_object_basic():
"""Test basic conversion with all fields present."""
response_object = {
"id": "chatcmpl-123456",
"object": "chat.completion",
"created": 1728933352,
"model": "gpt-4o-2024-08-06",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Hi there! How can I assist you today?",
"refusal": None,
},
"finish_reason": "stop",
}
],
"usage": {
"prompt_tokens": 19,
"completion_tokens": 10,
"total_tokens": 29,
"prompt_tokens_details": {"cached_tokens": 0},
"completion_tokens_details": {"reasoning_tokens": 0},
},
"system_fingerprint": "fp_6b68a8204b",
}
result = convert_to_model_response_object(
model_response_object=ModelResponse(),
response_object=response_object,
stream=False,
start_time=datetime.now(),
end_time=datetime.now(),
hidden_params=None,
_response_headers=None,
convert_tool_call_to_json_mode=False,
)
assert isinstance(result, ModelResponse)
assert result.id == "chatcmpl-123456"
assert len(result.choices) == 1
assert isinstance(result.choices[0], Choices)
# Model details
assert result.model == "gpt-4o-2024-08-06"
assert result.object == "chat.completion"
assert result.created == 1728933352
# Choices assertions
choice = result.choices[0]
print("choice[0]", choice)
assert choice.index == 0
assert isinstance(choice.message, Message)
assert choice.message.role == "assistant"
assert choice.message.content == "Hi there! How can I assist you today?"
assert choice.finish_reason == "stop"
# Usage assertions
assert result.usage.prompt_tokens == 19
assert result.usage.completion_tokens == 10
assert result.usage.total_tokens == 29
assert result.usage.prompt_tokens_details == PromptTokensDetailsWrapper(
cached_tokens=0
)
assert result.usage.completion_tokens_details == CompletionTokensDetailsWrapper(
reasoning_tokens=0
)
# Other fields
assert result.system_fingerprint == "fp_6b68a8204b"
# hidden params
assert result._hidden_params is not None
def test_convert_image_input_dict_response_to_chat_completion_response():
"""Test conversion on a response with an image input."""
response_object = {
"id": "chatcmpl-123",
"object": "chat.completion",
"created": 1677652288,
"model": "gpt-4o-mini",
"system_fingerprint": "fp_44709d6fcb",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "\n\nThis image shows a wooden boardwalk extending through a lush green marshland.",
},
"logprobs": None,
"finish_reason": "stop",
}
],
"usage": {
"prompt_tokens": 9,
"completion_tokens": 12,
"total_tokens": 21,
"completion_tokens_details": {"reasoning_tokens": 0},
},
}
result = convert_to_model_response_object(
model_response_object=ModelResponse(),
response_object=response_object,
stream=False,
start_time=datetime.now(),
end_time=datetime.now(),
hidden_params=None,
_response_headers=None,
convert_tool_call_to_json_mode=False,
)
assert isinstance(result, ModelResponse)
assert result.id == "chatcmpl-123"
assert result.object == "chat.completion"
assert result.created == 1677652288
assert result.model == "gpt-4o-mini"
assert result.system_fingerprint == "fp_44709d6fcb"
assert len(result.choices) == 1
choice = result.choices[0]
assert choice.index == 0
assert isinstance(choice.message, Message)
assert choice.message.role == "assistant"
assert (
choice.message.content
== "\n\nThis image shows a wooden boardwalk extending through a lush green marshland."
)
assert choice.finish_reason == "stop"
assert result.usage.prompt_tokens == 9
assert result.usage.completion_tokens == 12
assert result.usage.total_tokens == 21
assert result.usage.completion_tokens_details == CompletionTokensDetailsWrapper(
reasoning_tokens=0
)
assert result._hidden_params is not None
def test_convert_to_model_response_object_tool_calls_invalid_json_arguments():
"""
Critical test - this is a basic response from OpenAI API
Test conversion with tool calls.
"""
response_object = {
"id": "chatcmpl-AK1uqisVA9OjUNkEuE53GJc8HPYlz",
"choices": [
{
"index": 0,
"finish_reason": "length",
"logprobs": None,
"message": {
"content": None,
"refusal": None,
"role": "assistant",
"audio": None,
"function_call": None,
"tool_calls": [
{
"id": "call_GED1Xit8lU7cNsjVM6dt2fTq",
"function": {
"arguments": '{"location":"Boston, MA","unit":"fahren',
"name": "get_current_weather",
},
"type": "function",
}
],
},
}
],
"created": 1729337288,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion",
"service_tier": None,
"system_fingerprint": "fp_45c6de4934",
"usage": {
"completion_tokens": 10,
"prompt_tokens": 92,
"total_tokens": 102,
"completion_tokens_details": {"audio_tokens": None, "reasoning_tokens": 0},
"prompt_tokens_details": {"audio_tokens": None, "cached_tokens": 0},
},
}
result = convert_to_model_response_object(
model_response_object=ModelResponse(),
response_object=response_object,
stream=False,
start_time=datetime.now(),
end_time=datetime.now(),
hidden_params=None,
_response_headers=None,
convert_tool_call_to_json_mode=False,
)
assert isinstance(result, ModelResponse)
assert result.id == "chatcmpl-AK1uqisVA9OjUNkEuE53GJc8HPYlz"
assert len(result.choices) == 1
assert result.choices[0].message.content is None
assert len(result.choices[0].message.tool_calls) == 1
assert (
result.choices[0].message.tool_calls[0].function.name == "get_current_weather"
)
assert (
result.choices[0].message.tool_calls[0].function.arguments
== '{"location":"Boston, MA","unit":"fahren'
)
assert result.choices[0].finish_reason == "length"
assert result.model == "gpt-4o-2024-08-06"
assert result.created == 1729337288
assert result.usage.completion_tokens == 10
assert result.usage.prompt_tokens == 92
assert result.usage.total_tokens == 102
assert result.system_fingerprint == "fp_45c6de4934"
def test_convert_to_model_response_object_tool_calls_valid_json_arguments():
"""
Critical test - this is a basic response from OpenAI API
Test conversion with tool calls.
"""
response_object = {
"id": "chatcmpl-AK1uqisVA9OjUNkEuE53GJc8HPYlz",
"choices": [
{
"index": 0,
"finish_reason": "length",
"logprobs": None,
"message": {
"content": None,
"refusal": None,
"role": "assistant",
"audio": None,
"function_call": None,
"tool_calls": [
{
"id": "call_GED1Xit8lU7cNsjVM6dt2fTq",
"function": {
"arguments": '{"location":"Boston, MA","unit":"fahrenheit"}',
"name": "get_current_weather",
},
"type": "function",
}
],
},
}
],
"created": 1729337288,
"model": "gpt-4o-2024-08-06",
"object": "chat.completion",
"service_tier": None,
"system_fingerprint": "fp_45c6de4934",
"usage": {
"completion_tokens": 10,
"prompt_tokens": 92,
"total_tokens": 102,
"completion_tokens_details": {"audio_tokens": None, "reasoning_tokens": 0},
"prompt_tokens_details": {"audio_tokens": None, "cached_tokens": 0},
},
}
result = convert_to_model_response_object(
model_response_object=ModelResponse(),
response_object=response_object,
stream=False,
start_time=datetime.now(),
end_time=datetime.now(),
hidden_params=None,
_response_headers=None,
convert_tool_call_to_json_mode=False,
)
assert isinstance(result, ModelResponse)
assert result.id == "chatcmpl-AK1uqisVA9OjUNkEuE53GJc8HPYlz"
assert len(result.choices) == 1
assert result.choices[0].message.content is None
assert len(result.choices[0].message.tool_calls) == 1
assert (
result.choices[0].message.tool_calls[0].function.name == "get_current_weather"
)
assert (
result.choices[0].message.tool_calls[0].function.arguments
== '{"location":"Boston, MA","unit":"fahrenheit"}'
)
assert result.choices[0].finish_reason == "length"
assert result.model == "gpt-4o-2024-08-06"
assert result.created == 1729337288
assert result.usage.completion_tokens == 10
assert result.usage.prompt_tokens == 92
assert result.usage.total_tokens == 102
assert result.system_fingerprint == "fp_45c6de4934"
def test_convert_to_model_response_object_json_mode():
"""
This test is verifying that when convert_tool_call_to_json_mode is True, a single tool call's arguments are correctly converted into the message content of the response.
"""
model_response_object = ModelResponse(model="gpt-3.5-turbo")
from litellm.constants import RESPONSE_FORMAT_TOOL_NAME
response_object = {
"choices": [
{
"message": {
"role": "assistant",
"tool_calls": [
{
"function": {
"arguments": '{"key": "value"}',
"name": RESPONSE_FORMAT_TOOL_NAME,
}
}
],
},
"finish_reason": None,
}
],
"usage": {"total_tokens": 10, "prompt_tokens": 5, "completion_tokens": 5},
"model": "gpt-3.5-turbo",
}
# Call the function
result = convert_to_model_response_object(
model_response_object=model_response_object,
response_object=response_object,
stream=False,
start_time=datetime.now(),
end_time=datetime.now(),
hidden_params=None,
_response_headers=None,
convert_tool_call_to_json_mode=True,
)
# Assertions
assert isinstance(result, ModelResponse)
assert len(result.choices) == 1
assert result.choices[0].message.content == '{"key": "value"}'
assert result.choices[0].finish_reason == "stop"
assert result.model == "gpt-3.5-turbo"
assert result.usage.total_tokens == 10
assert result.usage.prompt_tokens == 5
assert result.usage.completion_tokens == 5
def test_convert_to_model_response_object_function_output():
"""
Test conversion with function output.
From here: https://platform.openai.com/docs/api-reference/chat/create
"""
response_object = {
"id": "chatcmpl-abc123",
"object": "chat.completion",
"created": 1699896916,
"model": "gpt-4o-mini",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": None,
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_current_weather",
"arguments": '{\n"location": "Boston, MA"\n}',
},
}
],
},
"logprobs": None,
"finish_reason": "tool_calls",
}
],
"usage": {
"prompt_tokens": 82,
"completion_tokens": 17,
"total_tokens": 99,
"completion_tokens_details": {"reasoning_tokens": 0},
},
}
result = convert_to_model_response_object(
model_response_object=ModelResponse(),
response_object=response_object,
stream=False,
start_time=datetime.now(),
end_time=datetime.now(),
hidden_params=None,
_response_headers=None,
convert_tool_call_to_json_mode=False,
)
assert isinstance(result, ModelResponse)
assert result.id == "chatcmpl-abc123"
assert result.object == "chat.completion"
assert result.created == 1699896916
assert result.model == "gpt-4o-mini"
assert len(result.choices) == 1
choice = result.choices[0]
assert choice.index == 0
assert isinstance(choice.message, Message)
assert choice.message.role == "assistant"
assert choice.message.content is None
assert choice.finish_reason == "tool_calls"
assert len(choice.message.tool_calls) == 1
tool_call = choice.message.tool_calls[0]
assert tool_call.id == "call_abc123"
assert tool_call.type == "function"
assert tool_call.function.name == "get_current_weather"
assert tool_call.function.arguments == '{\n"location": "Boston, MA"\n}'
assert result.usage.prompt_tokens == 82
assert result.usage.completion_tokens == 17
assert result.usage.total_tokens == 99
assert result.usage.completion_tokens_details == CompletionTokensDetailsWrapper(
reasoning_tokens=0
)
assert result._hidden_params is not None
def test_convert_to_model_response_object_with_logprobs():
"""
Test conversion with logprobs in the response.
From here: https://platform.openai.com/docs/api-reference/chat/create
"""
response_object = {
"id": "chatcmpl-123",
"object": "chat.completion",
"created": 1702685778,
"model": "gpt-4o-mini",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello! How can I assist you today?",
},
"logprobs": {
"content": [
{
"token": "Hello",
"logprob": -0.31725305,
"bytes": [72, 101, 108, 108, 111],
"top_logprobs": [
{
"token": "Hello",
"logprob": -0.31725305,
"bytes": [72, 101, 108, 108, 111],
},
{
"token": "Hi",
"logprob": -1.3190403,
"bytes": [72, 105],
},
],
},
{
"token": "!",
"logprob": -0.02380986,
"bytes": [33],
"top_logprobs": [
{"token": "!", "logprob": -0.02380986, "bytes": [33]},
{
"token": " there",
"logprob": -3.787621,
"bytes": [32, 116, 104, 101, 114, 101],
},
],
},
{
"token": " How",
"logprob": -0.000054669687,
"bytes": [32, 72, 111, 119],
"top_logprobs": [
{
"token": " How",
"logprob": -0.000054669687,
"bytes": [32, 72, 111, 119],
},
{
"token": "<|end|>",
"logprob": -10.953937,
"bytes": None,
},
],
},
{
"token": " can",
"logprob": -0.015801601,
"bytes": [32, 99, 97, 110],
"top_logprobs": [
{
"token": " can",
"logprob": -0.015801601,
"bytes": [32, 99, 97, 110],
},
{
"token": " may",
"logprob": -4.161023,
"bytes": [32, 109, 97, 121],
},
],
},
{
"token": " I",
"logprob": -3.7697225e-6,
"bytes": [32, 73],
"top_logprobs": [
{
"token": " I",
"logprob": -3.7697225e-6,
"bytes": [32, 73],
},
{
"token": " assist",
"logprob": -13.596657,
"bytes": [32, 97, 115, 115, 105, 115, 116],
},
],
},
{
"token": " assist",
"logprob": -0.04571125,
"bytes": [32, 97, 115, 115, 105, 115, 116],
"top_logprobs": [
{
"token": " assist",
"logprob": -0.04571125,
"bytes": [32, 97, 115, 115, 105, 115, 116],
},
{
"token": " help",
"logprob": -3.1089056,
"bytes": [32, 104, 101, 108, 112],
},
],
},
{
"token": " you",
"logprob": -5.4385737e-6,
"bytes": [32, 121, 111, 117],
"top_logprobs": [
{
"token": " you",
"logprob": -5.4385737e-6,
"bytes": [32, 121, 111, 117],
},
{
"token": " today",
"logprob": -12.807695,
"bytes": [32, 116, 111, 100, 97, 121],
},
],
},
{
"token": " today",
"logprob": -0.0040071653,
"bytes": [32, 116, 111, 100, 97, 121],
"top_logprobs": [
{
"token": " today",
"logprob": -0.0040071653,
"bytes": [32, 116, 111, 100, 97, 121],
},
{"token": "?", "logprob": -5.5247097, "bytes": [63]},
],
},
{
"token": "?",
"logprob": -0.0008108172,
"bytes": [63],
"top_logprobs": [
{"token": "?", "logprob": -0.0008108172, "bytes": [63]},
{
"token": "?\n",
"logprob": -7.184561,
"bytes": [63, 10],
},
],
},
]
},
"finish_reason": "stop",
}
],
"usage": {
"prompt_tokens": 9,
"completion_tokens": 9,
"total_tokens": 18,
"completion_tokens_details": {"reasoning_tokens": 0},
},
"system_fingerprint": None,
}
print("ENTERING CONVERT")
try:
result = convert_to_model_response_object(
model_response_object=ModelResponse(),
response_object=response_object,
stream=False,
start_time=datetime.now(),
end_time=datetime.now(),
hidden_params=None,
_response_headers=None,
convert_tool_call_to_json_mode=False,
)
except Exception as e:
print(f"ERROR: {e}")
raise e
assert isinstance(result, ModelResponse)
assert result.id == "chatcmpl-123"
assert result.object == "chat.completion"
assert result.created == 1702685778
assert result.model == "gpt-4o-mini"
assert len(result.choices) == 1
choice = result.choices[0]
assert choice.index == 0
assert isinstance(choice.message, Message)
assert choice.message.role == "assistant"
assert choice.message.content == "Hello! How can I assist you today?"
assert choice.finish_reason == "stop"
# Check logprobs
assert choice.logprobs is not None
assert len(choice.logprobs.content) == 9
# Check each logprob entry
expected_tokens = [
"Hello",
"!",
" How",
" can",
" I",
" assist",
" you",
" today",
"?",
]
for i, logprob in enumerate(choice.logprobs.content):
assert logprob.token == expected_tokens[i]
assert isinstance(logprob.logprob, float)
assert isinstance(logprob.bytes, list)
assert len(logprob.top_logprobs) == 2
assert isinstance(logprob.top_logprobs[0].token, str)
assert isinstance(logprob.top_logprobs[0].logprob, float)
assert isinstance(logprob.top_logprobs[0].bytes, (list, type(None)))
assert result.usage.prompt_tokens == 9
assert result.usage.completion_tokens == 9
assert result.usage.total_tokens == 18
assert result.usage.completion_tokens_details == CompletionTokensDetailsWrapper(
reasoning_tokens=0
)
assert result.system_fingerprint is None
assert result._hidden_params is not None
def test_convert_to_model_response_object_error():
"""Test error handling for None response object."""
with pytest.raises(Exception, match="Error in response object format"):
convert_to_model_response_object(
model_response_object=None,
response_object=None,
stream=False,
start_time=None,
end_time=None,
hidden_params=None,
_response_headers=None,
convert_tool_call_to_json_mode=False,
)
def test_image_generation_openai_with_pydantic_warning(caplog):
try:
import logging
from litellm.types.utils import ImageResponse, ImageObject
convert_response_args = {
"response_object": {
"created": 1729709945,
"data": [
{
"b64_json": None,
"revised_prompt": "Generate an image of a baby sea otter. It should look incredibly cute, with big, soulful eyes and a fluffy, wet fur coat. The sea otter should be on its back, as sea otters often do, with its tiny hands holding onto a shell as if it is its precious toy. The background should be a tranquil sea under a clear sky, with soft sunlight reflecting off the waters. The color palette should be soothing with blues, browns, and white.",
"url": "https://oaidalleapiprodscus.blob.core.windows.net/private/org-ikDc4ex8NB5ZzfTf8m5WYVB7/user-JpwZsbIXubBZvan3Y3GchiiB/img-LL0uoOv4CFJIvNYxoNCKB8oc.png?st=2024-10-23T17%3A59%3A05Z&se=2024-10-23T19%3A59%3A05Z&sp=r&sv=2024-08-04&sr=b&rscd=inline&rsct=image/png&skoid=d505667d-d6c1-4a0a-bac7-5c84a87759f8&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2024-10-22T19%3A26%3A22Z&ske=2024-10-23T19%3A26%3A22Z&sks=b&skv=2024-08-04&sig=Hl4wczJ3H2vZNdLRt/7JvNi6NvQGDnbNkDy15%2Bl3k5s%3D",
}
],
},
"model_response_object": ImageResponse(
created=1729709929,
data=[],
),
"response_type": "image_generation",
"stream": False,
"start_time": None,
"end_time": None,
"hidden_params": None,
"_response_headers": None,
"convert_tool_call_to_json_mode": None,
}
resp: ImageResponse = convert_to_model_response_object(**convert_response_args)
assert resp is not None
assert resp.data is not None
assert len(resp.data) == 1
assert isinstance(resp.data[0], ImageObject)
except Exception as e:
pytest.fail(f"Test failed with exception: {e}")
def test_convert_to_model_response_object_with_empty_str():
"""Test that convert_to_model_response_object handles empty strings correctly."""
args = {
"response_object": {
"id": "chatcmpl-B0b1BmxhH4iSoRvFVbBJdLbMwr346",
"choices": [
{
"finish_reason": "stop",
"index": 0,
"logprobs": None,
"message": {
"content": "",
"refusal": None,
"role": "assistant",
"audio": None,
"function_call": None,
"tool_calls": None,
},
}
],
"created": 1739481997,
"model": "gpt-4o-mini-2024-07-18",
"object": "chat.completion",
"service_tier": "default",
"system_fingerprint": "fp_bd83329f63",
"usage": {
"completion_tokens": 1,
"prompt_tokens": 121,
"total_tokens": 122,
"completion_tokens_details": {
"accepted_prediction_tokens": 0,
"audio_tokens": 0,
"reasoning_tokens": 0,
"rejected_prediction_tokens": 0,
},
"prompt_tokens_details": {"audio_tokens": 0, "cached_tokens": 0},
},
},
"model_response_object": ModelResponse(
id="chatcmpl-9f9e5ad2-d570-46fe-a5e0-4983e9774318",
created=1739481997,
model=None,
object="chat.completion",
system_fingerprint=None,
choices=[
Choices(
finish_reason="stop",
index=0,
message=Message(
content=None,
role="assistant",
tool_calls=None,
function_call=None,
provider_specific_fields=None,
),
)
],
usage=Usage(
completion_tokens=0,
prompt_tokens=0,
total_tokens=0,
completion_tokens_details=None,
prompt_tokens_details=None,
),
),
"response_type": "completion",
"stream": False,
"start_time": None,
"end_time": None,
"hidden_params": None,
"_response_headers": {
"date": "Thu, 13 Feb 2025 21:26:37 GMT",
"content-type": "application/json",
"transfer-encoding": "chunked",
"connection": "keep-alive",
"access-control-expose-headers": "X-Request-ID",
"openai-organization": "reliablekeystest",
"openai-processing-ms": "297",
"openai-version": "2020-10-01",
"x-ratelimit-limit-requests": "30000",
"x-ratelimit-limit-tokens": "150000000",
"x-ratelimit-remaining-requests": "29999",
"x-ratelimit-remaining-tokens": "149999846",
"x-ratelimit-reset-requests": "2ms",
"x-ratelimit-reset-tokens": "0s",
"x-request-id": "req_651030cbda2c80353086eba8fd0a54ec",
"strict-transport-security": "max-age=31536000; includeSubDomains; preload",
"cf-cache-status": "DYNAMIC",
"set-cookie": "__cf_bm=0ihEMDdqKfEr0I8iP4XZ7C6xEA5rJeAc11XFXNxZgyE-1739481997-1.0.1.1-v5jbjAWhMUZ0faO8q2izQljUQC.R85Vexb18A2MCyS895bur5eRxcguP0.WGY6EkxXSaOKN55VL3Pg3NOdq_xA; path=/; expires=Thu, 13-Feb-25 21:56:37 GMT; domain=.api.openai.com; HttpOnly; Secure; SameSite=None, _cfuvid=jrNMSOBRrxUnGgJ62BltpZZSNImfnEqPX9Uu8meGFLY-1739481997919-0.0.1.1-604800000; path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None",
"x-content-type-options": "nosniff",
"server": "cloudflare",
"cf-ray": "9117e5d4caa1f7b5-LAX",
"content-encoding": "gzip",
"alt-svc": 'h3=":443"; ma=86400',
},
"convert_tool_call_to_json_mode": None,
}
resp: ModelResponse = convert_to_model_response_object(**args)
assert resp is not None
assert resp.choices[0].message.content is not None
def test_convert_to_model_response_object_with_thinking_content():
"""Test that convert_to_model_response_object handles thinking content correctly."""
args = {
"response_object": {
"id": "chatcmpl-8cc87354-70f3-4a14-b71b-332e965d98d2",
"created": 1741057687,
"model": "claude-3-7-sonnet-20250219",
"object": "chat.completion",
"system_fingerprint": None,
"choices": [
{
"finish_reason": "stop",
"index": 0,
"message": {
"content": "# LiteLLM\n\nLiteLLM is an open-source library that provides a unified interface for working with various Large Language Models (LLMs). It acts as an abstraction layer that lets developers interact with multiple LLM providers through a single, consistent API.\n\n## Key features:\n\n- **Universal API**: Standardizes interactions with models from OpenAI, Anthropic, Cohere, Azure, and many other providers\n- **Simple switching**: Easily swap between different LLM providers without changing your code\n- **Routing capabilities**: Manage load balancing, fallbacks, and cost optimization\n- **Prompt templates**: Handle different model-specific prompt formats automatically\n- **Logging and observability**: Track usage, performance, and costs across providers\n\nLiteLLM is particularly useful for teams who want flexibility in their LLM infrastructure without creating custom integration code for each provider.",
"role": "assistant",
"tool_calls": None,
"function_call": None,
"reasoning_content": "The person is asking about \"litellm\" and included what appears to be a UUID or some form of identifier at the end of their message (fffffe14-7991-43d0-acd8-d3e606db31a8).\n\nLiteLLM is an open-source library/project that provides a unified interface for working with various Large Language Models (LLMs). It's essentially a lightweight package that standardizes the way developers can work with different LLM APIs like OpenAI, Anthropic, Cohere, etc. through a consistent interface.\n\nSome key features and aspects of LiteLLM:\n\n1. Unified API for multiple LLM providers (OpenAI, Anthropic, Azure, etc.)\n2. Standardized input/output formats\n3. Handles routing, fallbacks, and load balancing\n4. Provides logging and observability\n5. Can help with cost tracking across different providers\n6. Makes it easier to switch between different LLM providers\n\nThe UUID-like string they included doesn't seem directly related to the question, unless it's some form of identifier they're including for tracking purposes.",
"thinking_blocks": [
{
"type": "thinking",
"thinking": "The person is asking about \"litellm\" and included what appears to be a UUID or some form of identifier at the end of their message (fffffe14-7991-43d0-acd8-d3e606db31a8).\n\nLiteLLM is an open-source library/project that provides a unified interface for working with various Large Language Models (LLMs). It's essentially a lightweight package that standardizes the way developers can work with different LLM APIs like OpenAI, Anthropic, Cohere, etc. through a consistent interface.\n\nSome key features and aspects of LiteLLM:\n\n1. Unified API for multiple LLM providers (OpenAI, Anthropic, Azure, etc.)\n2. Standardized input/output formats\n3. Handles routing, fallbacks, and load balancing\n4. Provides logging and observability\n5. Can help with cost tracking across different providers\n6. Makes it easier to switch between different LLM providers\n\nThe UUID-like string they included doesn't seem directly related to the question, unless it's some form of identifier they're including for tracking purposes.",
"signature": "ErUBCkYIARgCIkCf+r0qMSOMYkjlFERM00IxsY9I/m19dQGEF/Zv1E0AtvdZjKGnr+nr5vXUldmb/sUCgrQRH4YUyV0X3MoMrsNnEgxDqhUFcUTg1vM0CroaDEY1wKJ0Ca0EZ6S1jCIwF8ATum3xiF/mRSIIjoD6Virh0hFcOfH3Sz6Chtev9WUwwYMAVP4/hyzbrUDnsUlmKh0CfTayaXm6o63/6Kelr6pzLbErjQx2xZRnRjCypw==",
}
],
},
}
],
"usage": {
"completion_tokens": 460,
"prompt_tokens": 65,
"total_tokens": 525,
"completion_tokens_details": None,
"prompt_tokens_details": {"audio_tokens": None, "cached_tokens": 0},
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0,
},
},
"model_response_object": ModelResponse(),
}
resp: ModelResponse = convert_to_model_response_object(**args)
assert resp is not None
assert resp.choices[0].message.reasoning_content is not None

View File

@@ -0,0 +1,79 @@
import json
import os
import sys
from datetime import datetime
sys.path.insert(
0, os.path.abspath("../../")
) # Adds the parent directory to the system path
import litellm
import pytest
from litellm.litellm_core_utils.llm_response_utils.get_headers import (
get_response_headers,
_get_llm_provider_headers,
)
def test_get_response_headers_empty():
result = get_response_headers()
assert result == {}, "Expected empty dictionary for no input"
def test_get_response_headers_with_openai_headers():
"""
OpenAI headers are forwarded as is
Other headers are prefixed with llm_provider-
"""
input_headers = {
"x-ratelimit-limit-requests": "100",
"x-ratelimit-remaining-requests": "50",
"x-ratelimit-limit-tokens": "1000",
"x-ratelimit-remaining-tokens": "500",
"other-header": "value",
}
expected_output = {
"x-ratelimit-limit-requests": "100",
"x-ratelimit-remaining-requests": "50",
"x-ratelimit-limit-tokens": "1000",
"x-ratelimit-remaining-tokens": "500",
"llm_provider-x-ratelimit-limit-requests": "100",
"llm_provider-x-ratelimit-remaining-requests": "50",
"llm_provider-x-ratelimit-limit-tokens": "1000",
"llm_provider-x-ratelimit-remaining-tokens": "500",
"llm_provider-other-header": "value",
}
result = get_response_headers(input_headers)
assert result == expected_output, "Unexpected output for OpenAI headers"
def test_get_response_headers_without_openai_headers():
"""
Non-OpenAI headers are prefixed with llm_provider-
"""
input_headers = {"custom-header-1": "value1", "custom-header-2": "value2"}
expected_output = {
"llm_provider-custom-header-1": "value1",
"llm_provider-custom-header-2": "value2",
}
result = get_response_headers(input_headers)
assert result == expected_output, "Unexpected output for non-OpenAI headers"
def test_get_llm_provider_headers():
"""
If non OpenAI headers are already prefixed with llm_provider- they are not prefixed with llm_provider- again
"""
input_headers = {
"header1": "value1",
"header2": "value2",
"llm_provider-existing": "existing_value",
}
expected_output = {
"llm_provider-header1": "value1",
"llm_provider-header2": "value2",
"llm_provider-existing": "existing_value",
}
result = _get_llm_provider_headers(input_headers)
assert result == expected_output, "Unexpected output for _get_llm_provider_headers"