mirror of
https://github.com/tiennm99/litellm.git
synced 2026-07-19 16:19:29 +00:00
* fix(cost_calculator.py): move to using `.get_model_info()` for cost per token calculations ensures cost tracking is reliable - handles edge cases of parsing model cost map * build(model_prices_and_context_window.json): add 'supports_response_schema' for select tgai models Fixes https://github.com/BerriAI/litellm/pull/7037#discussion_r1872157329 * build(model_prices_and_context_window.json): remove 'pdf input' and 'vision' support from nova micro in model map Bedrock docs indicate no support for micro - https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference-supported-models-features.html * fix(converse_transformation.py): support amazon nova tool use * fix(opentelemetry): Add missing LLM request type attribute to spans (#7041) * feat(opentelemetry): add LLM request type attribute to spans * lint * fix: curl usage (#7038) curl -d, --data <data> is lowercase d curl -D, --dump-header <filename> is uppercase D references: https://curl.se/docs/manpage.html#-d https://curl.se/docs/manpage.html#-D * fix(spend_tracking.py): handle empty 'id' in model response - when creating spend log Fixes https://github.com/BerriAI/litellm/issues/7023 * fix(streaming_chunk_builder.py): handle initial id being empty string Fixes https://github.com/BerriAI/litellm/issues/7023 * fix(anthropic_passthrough_logging_handler.py): add end user cost tracking for anthropic pass through endpoint * docs(pass_through/): refactor docs location + add table on supported features for pass through endpoints * feat(anthropic_passthrough_logging_handler.py): support end user cost tracking via anthropic sdk * docs(anthropic_completion.md): add docs on passing end user param for cost tracking on anthropic sdk * fix(litellm_logging.py): use standard logging payload if present in kwargs prevent datadog logging error for pass through endpoints * docs(bedrock.md): add rerank api usage example to docs * bugfix/change dummy tool name format (#7053) * fix viewing keys (#7042) * ui new build * build(model_prices_and_context_window.json): add bedrock region models to model cost map (#7044) * bye (#6982) * (fix) litellm router.aspeech (#6962) * doc Migrating Databases * fix aspeech on router * test_audio_speech_router * test_audio_speech_router * docs show supported providers on batches api doc * change dummy tool name format --------- Co-authored-by: Ishaan Jaff <ishaanjaffer0324@gmail.com> Co-authored-by: Krish Dholakia <krrishdholakia@gmail.com> Co-authored-by: yujonglee <yujonglee.dev@gmail.com> * fix: fix linting errors * test: update test * fix(litellm_logging.py): fix pass through check * fix(test_otel_logging.py): fix test * fix(cost_calculator.py): update handling for cost per second * fix(cost_calculator.py): fix cost check * test: fix test * (fix) adding public routes when using custom header (#7045) * get_api_key_from_custom_header * add test_get_api_key_from_custom_header * fix testing use 1 file for test user api key auth * fix test user api key auth * test_custom_api_key_header_name * build: update ui build --------- Co-authored-by: Doron Kopit <83537683+doronkopit5@users.noreply.github.com> Co-authored-by: lloydchang <lloydchang@gmail.com> Co-authored-by: hgulersen <haymigulersen@gmail.com> Co-authored-by: Ishaan Jaff <ishaanjaffer0324@gmail.com> Co-authored-by: yujonglee <yujonglee.dev@gmail.com>
363 lines
12 KiB
Python
363 lines
12 KiB
Python
"""
|
|
Testing for _assemble_complete_response_from_streaming_chunks
|
|
|
|
- Test 1 - ModelResponse with 1 list of streaming chunks. Assert chunks are added to the streaming_chunks, after final chunk sent assert complete_streaming_response is not None
|
|
- Test 2 - TextCompletionResponse with 1 list of streaming chunks. Assert chunks are added to the streaming_chunks, after final chunk sent assert complete_streaming_response is not None
|
|
- Test 3 - Have multiple lists of streaming chunks, Assert that chunks are added to the correct list and that complete_streaming_response is None. After final chunk sent assert complete_streaming_response is not None
|
|
- Test 4 - build a complete response when 1 chunk is poorly formatted
|
|
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
from datetime import datetime
|
|
from unittest.mock import AsyncMock
|
|
|
|
from pydantic.main import Model
|
|
|
|
sys.path.insert(
|
|
0, os.path.abspath("../..")
|
|
) # Adds the parent directory to the system path
|
|
|
|
|
|
import httpx
|
|
import pytest
|
|
from respx import MockRouter
|
|
|
|
import litellm
|
|
from litellm import Choices, Message, ModelResponse, TextCompletionResponse, TextChoices
|
|
|
|
from litellm.litellm_core_utils.litellm_logging import (
|
|
_assemble_complete_response_from_streaming_chunks,
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize("is_async", [True, False])
|
|
def test_assemble_complete_response_from_streaming_chunks_1(is_async):
|
|
"""
|
|
Test 1 - ModelResponse with 1 list of streaming chunks. Assert chunks are added to the streaming_chunks, after final chunk sent assert complete_streaming_response is not None
|
|
"""
|
|
|
|
request_kwargs = {
|
|
"model": "test_model",
|
|
"messages": [{"role": "user", "content": "Hello, world!"}],
|
|
}
|
|
|
|
list_streaming_chunks = []
|
|
chunk = {
|
|
"id": "chatcmpl-9mWtyDnikZZoB75DyfUzWUxiiE2Pi",
|
|
"choices": [
|
|
litellm.utils.StreamingChoices(
|
|
delta=litellm.utils.Delta(
|
|
content="hello in response",
|
|
function_call=None,
|
|
role=None,
|
|
tool_calls=None,
|
|
),
|
|
index=0,
|
|
logprobs=None,
|
|
)
|
|
],
|
|
"created": 1721353246,
|
|
"model": "gpt-3.5-turbo",
|
|
"object": "chat.completion.chunk",
|
|
"system_fingerprint": None,
|
|
"usage": None,
|
|
}
|
|
chunk = litellm.ModelResponse(**chunk, stream=True)
|
|
complete_streaming_response = _assemble_complete_response_from_streaming_chunks(
|
|
result=chunk,
|
|
start_time=datetime.now(),
|
|
end_time=datetime.now(),
|
|
request_kwargs=request_kwargs,
|
|
streaming_chunks=list_streaming_chunks,
|
|
is_async=is_async,
|
|
)
|
|
|
|
# this is the 1st chunk - complete_streaming_response should be None
|
|
|
|
print("list_streaming_chunks", list_streaming_chunks)
|
|
print("complete_streaming_response", complete_streaming_response)
|
|
assert complete_streaming_response is None
|
|
assert len(list_streaming_chunks) == 1
|
|
assert list_streaming_chunks[0] == chunk
|
|
|
|
# Add final chunk
|
|
chunk = {
|
|
"id": "chatcmpl-9mWtyDnikZZoB75DyfUzWUxiiE2Pi",
|
|
"choices": [
|
|
litellm.utils.StreamingChoices(
|
|
finish_reason="stop",
|
|
delta=litellm.utils.Delta(
|
|
content="end of response",
|
|
function_call=None,
|
|
role=None,
|
|
tool_calls=None,
|
|
),
|
|
index=0,
|
|
logprobs=None,
|
|
)
|
|
],
|
|
"created": 1721353246,
|
|
"model": "gpt-3.5-turbo",
|
|
"object": "chat.completion.chunk",
|
|
"system_fingerprint": None,
|
|
"usage": None,
|
|
}
|
|
chunk = litellm.ModelResponse(**chunk, stream=True)
|
|
complete_streaming_response = _assemble_complete_response_from_streaming_chunks(
|
|
result=chunk,
|
|
start_time=datetime.now(),
|
|
end_time=datetime.now(),
|
|
request_kwargs=request_kwargs,
|
|
streaming_chunks=list_streaming_chunks,
|
|
is_async=is_async,
|
|
)
|
|
|
|
print("list_streaming_chunks", list_streaming_chunks)
|
|
print("complete_streaming_response", complete_streaming_response)
|
|
|
|
# this is the 2nd chunk - complete_streaming_response should not be None
|
|
assert complete_streaming_response is not None
|
|
assert len(list_streaming_chunks) == 2
|
|
|
|
assert isinstance(complete_streaming_response, ModelResponse)
|
|
assert isinstance(complete_streaming_response.choices[0], Choices)
|
|
|
|
pass
|
|
|
|
|
|
@pytest.mark.parametrize("is_async", [True, False])
|
|
def test_assemble_complete_response_from_streaming_chunks_2(is_async):
|
|
"""
|
|
Test 2 - TextCompletionResponse with 1 list of streaming chunks. Assert chunks are added to the streaming_chunks, after final chunk sent assert complete_streaming_response is not None
|
|
"""
|
|
|
|
from litellm.utils import TextCompletionStreamWrapper
|
|
|
|
_text_completion_stream_wrapper = TextCompletionStreamWrapper(
|
|
completion_stream=None, model="test_model"
|
|
)
|
|
|
|
request_kwargs = {
|
|
"model": "test_model",
|
|
"messages": [{"role": "user", "content": "Hello, world!"}],
|
|
}
|
|
|
|
list_streaming_chunks = []
|
|
chunk = {
|
|
"id": "chatcmpl-9mWtyDnikZZoB75DyfUzWUxiiE2Pi",
|
|
"choices": [
|
|
litellm.utils.StreamingChoices(
|
|
delta=litellm.utils.Delta(
|
|
content="hello in response",
|
|
function_call=None,
|
|
role=None,
|
|
tool_calls=None,
|
|
),
|
|
index=0,
|
|
logprobs=None,
|
|
)
|
|
],
|
|
"created": 1721353246,
|
|
"model": "gpt-3.5-turbo",
|
|
"object": "chat.completion.chunk",
|
|
"system_fingerprint": None,
|
|
"usage": None,
|
|
}
|
|
chunk = litellm.ModelResponse(**chunk, stream=True)
|
|
chunk = _text_completion_stream_wrapper.convert_to_text_completion_object(chunk)
|
|
|
|
complete_streaming_response = _assemble_complete_response_from_streaming_chunks(
|
|
result=chunk,
|
|
start_time=datetime.now(),
|
|
end_time=datetime.now(),
|
|
request_kwargs=request_kwargs,
|
|
streaming_chunks=list_streaming_chunks,
|
|
is_async=is_async,
|
|
)
|
|
|
|
# this is the 1st chunk - complete_streaming_response should be None
|
|
|
|
print("list_streaming_chunks", list_streaming_chunks)
|
|
print("complete_streaming_response", complete_streaming_response)
|
|
assert complete_streaming_response is None
|
|
assert len(list_streaming_chunks) == 1
|
|
assert list_streaming_chunks[0] == chunk
|
|
|
|
# Add final chunk
|
|
chunk = {
|
|
"id": "chatcmpl-9mWtyDnikZZoB75DyfUzWUxiiE2Pi",
|
|
"choices": [
|
|
litellm.utils.StreamingChoices(
|
|
finish_reason="stop",
|
|
delta=litellm.utils.Delta(
|
|
content="end of response",
|
|
function_call=None,
|
|
role=None,
|
|
tool_calls=None,
|
|
),
|
|
index=0,
|
|
logprobs=None,
|
|
)
|
|
],
|
|
"created": 1721353246,
|
|
"model": "gpt-3.5-turbo",
|
|
"object": "chat.completion.chunk",
|
|
"system_fingerprint": None,
|
|
"usage": None,
|
|
}
|
|
chunk = litellm.ModelResponse(**chunk, stream=True)
|
|
chunk = _text_completion_stream_wrapper.convert_to_text_completion_object(chunk)
|
|
complete_streaming_response = _assemble_complete_response_from_streaming_chunks(
|
|
result=chunk,
|
|
start_time=datetime.now(),
|
|
end_time=datetime.now(),
|
|
request_kwargs=request_kwargs,
|
|
streaming_chunks=list_streaming_chunks,
|
|
is_async=is_async,
|
|
)
|
|
|
|
print("list_streaming_chunks", list_streaming_chunks)
|
|
print("complete_streaming_response", complete_streaming_response)
|
|
|
|
# this is the 2nd chunk - complete_streaming_response should not be None
|
|
assert complete_streaming_response is not None
|
|
assert len(list_streaming_chunks) == 2
|
|
|
|
assert isinstance(complete_streaming_response, TextCompletionResponse)
|
|
assert isinstance(complete_streaming_response.choices[0], TextChoices)
|
|
|
|
pass
|
|
|
|
|
|
@pytest.mark.parametrize("is_async", [True, False])
|
|
def test_assemble_complete_response_from_streaming_chunks_3(is_async):
|
|
|
|
request_kwargs = {
|
|
"model": "test_model",
|
|
"messages": [{"role": "user", "content": "Hello, world!"}],
|
|
}
|
|
|
|
list_streaming_chunks_1 = []
|
|
list_streaming_chunks_2 = []
|
|
|
|
chunk = {
|
|
"id": "chatcmpl-9mWtyDnikZZoB75DyfUzWUxiiE2Pi",
|
|
"choices": [
|
|
litellm.utils.StreamingChoices(
|
|
delta=litellm.utils.Delta(
|
|
content="hello in response",
|
|
function_call=None,
|
|
role=None,
|
|
tool_calls=None,
|
|
),
|
|
index=0,
|
|
logprobs=None,
|
|
)
|
|
],
|
|
"created": 1721353246,
|
|
"model": "gpt-3.5-turbo",
|
|
"object": "chat.completion.chunk",
|
|
"system_fingerprint": None,
|
|
"usage": None,
|
|
}
|
|
chunk = litellm.ModelResponse(**chunk, stream=True)
|
|
complete_streaming_response = _assemble_complete_response_from_streaming_chunks(
|
|
result=chunk,
|
|
start_time=datetime.now(),
|
|
end_time=datetime.now(),
|
|
request_kwargs=request_kwargs,
|
|
streaming_chunks=list_streaming_chunks_1,
|
|
is_async=is_async,
|
|
)
|
|
|
|
# this is the 1st chunk - complete_streaming_response should be None
|
|
|
|
print("list_streaming_chunks_1", list_streaming_chunks_1)
|
|
print("complete_streaming_response", complete_streaming_response)
|
|
assert complete_streaming_response is None
|
|
assert len(list_streaming_chunks_1) == 1
|
|
assert list_streaming_chunks_1[0] == chunk
|
|
assert len(list_streaming_chunks_2) == 0
|
|
|
|
# now add a chunk to the 2nd list
|
|
|
|
complete_streaming_response = _assemble_complete_response_from_streaming_chunks(
|
|
result=chunk,
|
|
start_time=datetime.now(),
|
|
end_time=datetime.now(),
|
|
request_kwargs=request_kwargs,
|
|
streaming_chunks=list_streaming_chunks_2,
|
|
is_async=is_async,
|
|
)
|
|
|
|
print("list_streaming_chunks_2", list_streaming_chunks_2)
|
|
print("complete_streaming_response", complete_streaming_response)
|
|
assert complete_streaming_response is None
|
|
assert len(list_streaming_chunks_2) == 1
|
|
assert list_streaming_chunks_2[0] == chunk
|
|
assert len(list_streaming_chunks_1) == 1
|
|
|
|
# now add a chunk to the 1st list
|
|
|
|
|
|
@pytest.mark.parametrize("is_async", [True, False])
|
|
def test_assemble_complete_response_from_streaming_chunks_4(is_async):
|
|
"""
|
|
Test 4 - build a complete response when 1 chunk is poorly formatted
|
|
|
|
- Assert complete_streaming_response is None
|
|
- Assert list_streaming_chunks is not empty
|
|
"""
|
|
|
|
request_kwargs = {
|
|
"model": "test_model",
|
|
"messages": [{"role": "user", "content": "Hello, world!"}],
|
|
}
|
|
|
|
list_streaming_chunks = []
|
|
|
|
chunk = {
|
|
"id": "chatcmpl-9mWtyDnikZZoB75DyfUzWUxiiE2Pi",
|
|
"choices": [
|
|
litellm.utils.StreamingChoices(
|
|
finish_reason="stop",
|
|
delta=litellm.utils.Delta(
|
|
content="end of response",
|
|
function_call=None,
|
|
role=None,
|
|
tool_calls=None,
|
|
),
|
|
index=0,
|
|
logprobs=None,
|
|
)
|
|
],
|
|
"created": 1721353246,
|
|
"model": "gpt-3.5-turbo",
|
|
"object": "chat.completion.chunk",
|
|
"system_fingerprint": None,
|
|
"usage": None,
|
|
}
|
|
chunk = litellm.ModelResponse(**chunk, stream=True)
|
|
|
|
# remove attribute id from chunk
|
|
del chunk.object
|
|
|
|
complete_streaming_response = _assemble_complete_response_from_streaming_chunks(
|
|
result=chunk,
|
|
start_time=datetime.now(),
|
|
end_time=datetime.now(),
|
|
request_kwargs=request_kwargs,
|
|
streaming_chunks=list_streaming_chunks,
|
|
is_async=is_async,
|
|
)
|
|
|
|
print("complete_streaming_response", complete_streaming_response)
|
|
assert complete_streaming_response is None
|
|
|
|
print("list_streaming_chunks", list_streaming_chunks)
|
|
|
|
assert len(list_streaming_chunks) == 1
|