fix(streaming): clear type-discipline on mock EOF terminal chunk

Yield a deep-copied terminal stop chunk instead of mutating the
shared mock response, and give the max-parallel SSE fixture a real
finish_reason before [DONE] so OpenAI-compatible exhaustion still works.
This commit is contained in:
lei_lei 2026-09-09 03:37:23 +00:00
parent 3a02837041
commit 7edec3cdcc
2 changed files with 23 additions and 27 deletions

View file

@ -6975,7 +6975,6 @@ def mock_completion_streaming_obj(model_response, mock_response, model, n: int |
completion_obj = Delta(role="assistant", content=mock_response[i : i + 3])
if n is None:
model_response.choices[0].delta = completion_obj
model_response.choices[0].finish_reason = None
else:
_all_choices = []
for j in range(n):
@ -6986,19 +6985,16 @@ def mock_completion_streaming_obj(model_response, mock_response, model, n: int |
_all_choices.append(_streaming_choice)
model_response.choices = _all_choices
yield model_response
# Separate terminal object so content chunks keep finish_reason unset.
terminal: Final = model_response.model_copy(deep=True)
if n is None:
model_response.choices[0].delta = Delta(role="assistant", content=None)
model_response.choices[0].finish_reason = "stop"
terminal.choices[0].delta = Delta(role="assistant", content=None)
terminal.choices[0].finish_reason = "stop"
else:
model_response.choices = [
litellm.utils.StreamingChoices(
index=j,
delta=litellm.utils.Delta(role="assistant", content=None),
finish_reason="stop",
)
for j in range(n)
]
yield model_response
for j in range(n):
terminal.choices[j].delta = litellm.utils.Delta(role="assistant", content=None)
terminal.choices[j].finish_reason = "stop"
yield terminal
async def async_mock_completion_streaming_obj(
@ -7016,7 +7012,6 @@ async def async_mock_completion_streaming_obj(
completion_obj = Delta(role="assistant", content=mock_response[i : i + 3])
if n is None:
model_response.choices[0].delta = completion_obj
model_response.choices[0].finish_reason = None
else:
_all_choices = []
for j in range(n):
@ -7027,19 +7022,16 @@ async def async_mock_completion_streaming_obj(
_all_choices.append(_streaming_choice)
model_response.choices = _all_choices
yield model_response
# Separate terminal object so content chunks keep finish_reason unset.
terminal: Final = model_response.model_copy(deep=True)
if n is None:
model_response.choices[0].delta = Delta(role="assistant", content=None)
model_response.choices[0].finish_reason = "stop"
terminal.choices[0].delta = Delta(role="assistant", content=None)
terminal.choices[0].finish_reason = "stop"
else:
model_response.choices = [
litellm.utils.StreamingChoices(
index=j,
delta=litellm.utils.Delta(role="assistant", content=None),
finish_reason="stop",
)
for j in range(n)
]
yield model_response
for j in range(n):
terminal.choices[j].delta = litellm.utils.Delta(role="assistant", content=None)
terminal.choices[j].finish_reason = "stop"
yield terminal
########## Reading Config File ############################

View file

@ -13295,10 +13295,14 @@ class _InFlightTracker:
self.current -= 1
_SSE_CHUNKS: Final[tuple[bytes, ...]] = tuple(
_SSE_CHUNKS: Final[tuple[bytes, ...]] = (
*(
b'data: {"id":"c","object":"chat.completion.chunk","created":1,"model":"gpt-5.6",'
b'"choices":[{"index":0,"delta":{"content":"x"},"finish_reason":null}]}\n\n'
for _ in range(5)
),
b'data: {"id":"c","object":"chat.completion.chunk","created":1,"model":"gpt-5.6",'
b'"choices":[{"index":0,"delta":{"content":"x"},"finish_reason":null}]}\n\n'
for _ in range(5)
b'"choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}\n\n',
)