mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 12:22:14 +03:00
* fix(stream): normalize delta.reasoning to reasoning_content in SSE streaming NVIDIA kimi-k2.5 (and potentially other providers) send reasoning tokens as `delta.reasoning` in SSE streaming chunks instead of the standard OpenAI `delta.reasoning_content` field. This caused reasoning content to be silently dropped during stream passthrough — clients received only the final answer with no reasoning separation. The non-streaming sanitizer (responseSanitizer.ts) already handled this alias, but the streaming pipeline did not. Fix applied in 4 locations: - stream.ts passthrough: normalize + force re-serialize sanitized chunk - stream.ts translate: accumulate reasoning from delta.reasoning - sseParser.ts: collect delta.reasoning in parseSSEToOpenAIResponse - streamPayloadCollector.ts: collect delta.reasoning in buildOpenAISummary * fix: eliminate injectedUsage reuse bug and add reasoning alias tests - Detect delta.reasoning alias before sanitizeStreamingChunk() which already normalizes it, removing dead post-sanitization normalization - Replace injectedUsage reuse with separate needsReserialization flag so reasoning re-serialization cannot block finish_reason/usage mutations on the same SSE chunk (fixes CRITICAL review finding) - Add unit test for parseSSEToOpenAIResponse reasoning alias - Add unit test for buildStreamSummaryFromEvents reasoning alias * fix(stream): separate reasoning from content in passthrough response body The passthroughAccumulatedContent variable was mixing delta.content and delta.reasoning_content into one string, causing the client_response log and responseBody to lose reasoning separation. - Add passthroughAccumulatedReasoning accumulator for reasoning deltas - Set message.reasoning_content in responseBody when reasoning exists - Only accumulate delta.content into passthroughAccumulatedContent * fix: trim leading whitespace from assembled content in log summaries NVIDIA and other providers emit token deltas with leading spaces (e.g. ' The', ' user'). When joined, these produce a leading space in the provider_response and parsed non-streaming response logs. Trim the joined content and reasoning_content in both buildOpenAISummary and parseSSEToOpenAIResponse for consistent log output. * fix(stream): split combined reasoning+content deltas into separate SSE events Some providers (e.g. NVIDIA NIM) send transition chunks with both `delta.reasoning` and `delta.content` in the same SSE event. After sanitization this becomes `reasoning_content` + `content`, which violates the standard OpenAI streaming contract where these fields are never mixed. Clients using if/else logic (LobeChat, etc.) skip content when reasoning_content is present, losing the first content token. Split such combined chunks into two separate SSE events: 1. Reasoning-only event (finish_reason=null, no usage) 2. Content-only event (carries finish_reason and usage)