From 2dd2b8a8e3a7350eab5602390162bbade1e85104 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Tue, 26 Mar 2024 08:57:44 -0700 Subject: [PATCH] test(test_streaming.py): add unit testing for custom stream wrapper --- litellm/tests/test_streaming.py | 68 +++++++++++++++++++++++++++++++++ litellm/utils.py | 24 +++++++++--- 2 files changed, 87 insertions(+), 5 deletions(-) diff --git a/litellm/tests/test_streaming.py b/litellm/tests/test_streaming.py index ee7cb64cdd..51a3ae04ea 100644 --- a/litellm/tests/test_streaming.py +++ b/litellm/tests/test_streaming.py @@ -2212,3 +2212,71 @@ async def test_acompletion_claude_3_function_call_with_streaming(): # raise Exception("it worked!") except Exception as e: pytest.fail(f"Error occurred: {e}") + + +class ModelResponseIterator: + def __init__(self, model_response): + self.model_response = model_response + self.is_done = False + + # Sync iterator + def __iter__(self): + return self + + def __next__(self): + if self.is_done: + raise StopIteration + self.is_done = True + return self.model_response + + # Async iterator + def __aiter__(self): + return self + + async def __anext__(self): + if self.is_done: + raise StopAsyncIteration + self.is_done = True + return self.model_response + + +def test_unit_test_custom_stream_wrapper(): + """ + Test if last streaming chunk ends with '?', if the message repeats itself. + """ + litellm.set_verbose = False + chunk = { + "id": "chatcmpl-123", + "object": "chat.completion.chunk", + "created": 1694268190, + "model": "gpt-3.5-turbo-0125", + "system_fingerprint": "fp_44709d6fcb", + "choices": [ + {"index": 0, "delta": {"content": "How are you?"}, "finish_reason": "stop"} + ], + } + chunk = litellm.ModelResponse(**chunk, stream=True) + + completion_stream = ModelResponseIterator(model_response=chunk) + + response = litellm.CustomStreamWrapper( + completion_stream=completion_stream, + model="gpt-3.5-turbo", + custom_llm_provider="cached_response", + logging_obj=litellm.Logging( + model="gpt-3.5-turbo", + messages=[{"role": "user", "content": "Hey"}], + stream=True, + call_type="completion", + start_time=time.time(), + litellm_call_id="12345", + function_id="1245", + ), + ) + + freq = 0 + for chunk in response: + if chunk.choices[0].delta.content is not None: + if "How are you?" in chunk.choices[0].delta.content: + freq += 1 + assert freq == 1 diff --git a/litellm/utils.py b/litellm/utils.py index 896a1a876b..f672fe5c5d 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -422,8 +422,11 @@ class StreamingChoices(OpenAIObject): else: self.finish_reason = None self.index = index - if delta: - self.delta = delta + if delta is not None: + if isinstance(delta, Delta): + self.delta = delta + if isinstance(delta, dict): + self.delta = Delta(**delta) else: self.delta = Delta() if enhancements is not None: @@ -491,14 +494,25 @@ class ModelResponse(OpenAIObject): ): if stream is not None and stream == True: object = "chat.completion.chunk" - choices = [StreamingChoices()] + if choices is not None and isinstance(choices, list): + new_choices = [] + for choice in choices: + _new_choice = StreamingChoices(**choice) + new_choices.append(_new_choice) + choices = new_choices + else: + choices = [StreamingChoices()] else: if model in litellm.open_ai_embedding_models: object = "embedding" else: object = "chat.completion" - if choices: - choices = [Choices(*choices)] + if choices is not None and isinstance(choices, list): + new_choices = [] + for choice in choices: + _new_choice = Choices(**choice) + new_choices.append(_new_choice) + choices = new_choices else: choices = [Choices()] if id is None: