Skip to content

Bug: Ollama stream translator drops final content chunk and DB logging is empty #2694

Description

@wzli

Bug Description

When using the Ollama provider format (format: "ollama"), two bugs affect streaming:

  1. Cutoff of final token(s): The Ollama stream translator module 39551 in 6805.js checks if (a.done) early and returns an empty delta ({}) immediately, discarding the final payload chunk when "done": true and content (in a.message.content or a.message.thinking) are delivered in the same final chunk.
  2. Database content logging: The SSE stream handler in 8895.js constructs the accumulated response string C by checking p.delta?.text or p.choices?.[0]?.delta?.content etc. Since it does not check Ollama's native chunk format (p.message?.content), C stays empty, writing "[Empty streaming response]" to the request details DB.

Code Evidence

1. Discarded Final Chunk Content in 39551 (6805.js)

if(a.done){
    let h=k(a),i=(0,j.F)(a.done_reason,"ollama");
    (a.done_reason===f.bC.TOOL_CALLS||b.hadToolCalls)&&(i=f.bC.TOOL_CALLS);
    let l=(0,g.k)({id:c,created:d,model:e},{},i); // delta is empty {}
    return l.usage=h,l
}

If the final chunk is {"message":{"content":"!"},"done":true}, the content "!" is ignored because the parser returns early.

2. Stream Accumulation in 8895.js

if(p.delta?.text&&(B+=p.delta.text.length,C+=p.delta.text),p.delta?.thinking&&(B+=p.delta.thinking.length,D+=p.delta.thinking),p.choices?.[0]?.delta?.content&&(B+=p.choices[0].delta.content.length,C+=p.choices[0].delta.content),p.choices?.[0]?.delta?.reasoning_content...

This fails to check p.message?.content and p.message?.thinking for native Ollama streams, leaving C empty at stream end.

Proposed Fix

1. For 39551 (6805.js):

Parse the message variables before checking a.done:

let h=a.message;
let i="string"==typeof h?.content?h.content:"";
let m="string"==typeof h?.thinking?h.thinking:"",n=h&&Array.isArray(h.tool_calls)?h.tool_calls:null;
if(a.done){
    let h_use=k(a),i_reason=(0,j.F)(a.done_reason,"ollama");
    (a.done_reason===f.bC.TOOL_CALLS||b.hadToolCalls||n)&&(i_reason=f.bC.TOOL_CALLS);
    let delta_done={};
    if(i)delta_done.content=i;
    if(m)delta_done.reasoning_content=m;
    if(n)delta_done.tool_calls=n;
    let l=(0,g.k)({id:c,created:d,model:e},delta_done,i_reason);
    return l.usage=h_use,l
}
if(!h)return null;

2. For 8895.js:

Add p.message?.content and p.message?.thinking checks to the accumulator:

if(p.delta?.text&&(B+=p.delta.text.length,C+=p.delta.text),p.delta?.thinking&&(B+=p.delta.thinking.length,D+=p.delta.thinking),p.message?.content&&(B+=p.message.content.length,C+=p.message.content),p.message?.thinking&&(B+=p.message.thinking.length,D+=p.message.thinking),p.choices?.[0]?.delta?.content&&(B+=p.choices[0].delta.content.length,C+=p.choices[0].delta.content),p.choices?.[0]?.delta?.reasoning_content&&(B+=p.choices[0].delta.reasoning_content.length,D+=p.choices[0].delta.reasoning_content)...

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions