Skip to content

A streaming text delta that repeats everything streamed so far is missing from the recorded turn #435

Description

@ebarkhordar

If a streamed response contains a text delta identical to everything streamed before it, that delta does not make it onto the assistant turn. The caller watches the right text go by, and the turn that gets stored, and sent back as history on the next request, is short by one delta.

The easy case is a response starting with two identical deltas, which is what a leading "\n\n" often looks like coming off a local model.

No API key needed for this, it drives the completions provider's streaming contract:

from openai.types.chat import ChatCompletionChunk
from chatlas._provider_openai_completions import OpenAICompletionsProvider

provider = OpenAICompletionsProvider(api_key="unused", model="gpt-4o-mini")

def chunk(text, last=False):
    return ChatCompletionChunk.model_validate({
        "id": "chatcmpl-1",
        "object": "chat.completion.chunk",
        "created": 1,
        "model": "gpt-4o-mini",
        "choices": [{
            "index": 0,
            "delta": {"role": "assistant", "content": text},
            "finish_reason": "stop" if last else None,
        }],
    })

deltas = ["\n", "\n", "Here is the answer."]

completion = None
for i, d in enumerate(deltas):
    completion = provider.stream_merge_chunks(completion, chunk(d, last=i == len(deltas) - 1))
turn = provider.stream_turn(completion, has_data_model=False)

print("streamed:", repr("".join(deltas)))
print("turn:    ", repr(turn.text))
streamed: '\n\nHere is the answer.'
turn:     '\nHere is the answer.'

I expected both to be '\n\nHere is the answer.'.

It is not just the first two deltas. ["ab", "c", "abc"] streams abcabc and records abc, while ["ab", "c", "ab"] comes out fine, so it seems to bite when the new delta equals the whole accumulation so far.

chatlas at 7b3aa56, openai 3.13.0, python 3.12.14. Everything built on OpenAICompletionsProvider shares that call, so ChatOllama(), ChatGroq(), ChatDeepSeek() and ChatOpenRouter() are in the same boat. I found it driving the provider rather than a live model, so I cannot say how often a real stream lands on it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Priority: MediumValid bug or well-defined request with moderate impact or a workaround.ai-triage:doneMarks an issue whose AI triage workflow is complete.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions