mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-09-07 01:57:15 +00:00
fix(provider): preserve explicit streaming finish reasons
This commit is contained in:
@@ -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."""
|
||||
|
||||
Reference in New Issue
Block a user