fix(provider): preserve explicit streaming finish reasons

This commit is contained in:
RockChinQ
2026-09-06 00:33:20 +08:00
parent f5d6a3b348
commit 75f117ae50
2 changed files with 31 additions and 0 deletions
@@ -1278,6 +1278,9 @@ class LiteLLMRequester(requester.ProviderAPIRequester):
delta_content = delta.get('content') or ''
reasoning_content = delta.get('reasoning_content') or ''
provider_fields = dict(delta.get('provider_specific_fields') or {})
if finish_reason:
# Preserve an explicit provider stop even when the final delta is empty.
provider_fields['finish_reason'] = finish_reason
raw_thinking_blocks = delta.get('thinking_blocks')
if raw_thinking_blocks:
thinking_blocks_state = self._merge_thinking_blocks(thinking_blocks_state, raw_thinking_blocks)
@@ -199,6 +199,34 @@ class TestInvokeLLMStreamUsage:
chunk.choices = []
return chunk
@pytest.mark.asyncio
@pytest.mark.parametrize('finish_reason', ['stop', 'length', 'content_filter', 'tool_calls'])
async def test_empty_final_delta_preserves_provider_finish_reason(self, finish_reason):
import langbot_plugin.api.entities.builtin.provider.message as provider_message
mock_ap = Mock()
mock_ap.tool_mgr.generate_tools_for_openai = AsyncMock(return_value=None)
requester = litellmchat.LiteLLMRequester(ap=mock_ap, config={})
model = MockRuntimeModel('gpt-4o', 'test-api-key')
async def stream():
yield self._make_chunk(finish_reason=finish_reason)
with patch.object(litellmchat, 'acompletion', new=AsyncMock(side_effect=lambda **kw: stream())):
chunks = [
chunk
async for chunk in requester.invoke_llm_stream(
query=None,
model=model,
messages=[provider_message.Message(role='user', content='Hi')],
)
]
assert len(chunks) == 1
assert chunks[0].is_final
assert not chunks[0].content
assert chunks[0].provider_specific_fields['finish_reason'] == finish_reason
@pytest.mark.asyncio
async def test_stream_usage_with_nonempty_choices(self):
"""Usage chunk that still has a choice must populate _stream_usage."""