open-webui / open-webui/computer

bug: repeated context compaction can drop every user message and break Qwen/vLLM

Open Beginner friendly
#162 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
569
Forks
79
PR merge metrics
No merged PRs in 30d

Description

Summary

On cptr 0.9.12, a long agentic Chat Completions task can fail after repeated automatic context compaction when the retained keep_zone contains only assistant and tool messages.

With a Qwen model served by vLLM through LiteLLM, the next request fails with:

litellm.BadRequestError: Hosted_vllmException -
{"error":{"message":"No user query found in messages.","type":"BadRequestError","code":400}}

Raising chat.compact_token_threshold only delays the failure.

Environment

  • Computer/cptr: 0.9.12
  • Connection: OpenAI-compatible Chat Completions
  • Gateway: LiteLLM hosted_vllm
  • Backend: vLLM
  • Model group: Qwen
  • OS: Windows (but the compaction logic is platform-independent)

Reproduction

  1. Start a task from one normal user request.
  2. Let the agent execute a long sequence of tool calls and tool results.
  3. Allow automatic compaction to run more than once.
  4. On a later compaction, the only retained user message can fall into the new drop zone.
  5. The next upstream request contains a system prompt/summary followed only by assistant/tool turns.

A sanitized real trace reproduced the second compaction as:

messages before split: 263
split index:           158
messages kept:         105
user messages kept:    0

Root cause

In cptr/utils/chat_task.py:

  • _find_safe_split() protects tool-call/tool-result boundaries and keeps at least two messages.
  • It does not guarantee that keep_zone contains a top-level user turn.
  • After compaction, messages = keep_zone.
  • api_messages is then sent to the provider unchanged with respect to roles.

For a task that began with one user prompt followed by many assistant/tool iterations, repeated 40% retention can eventually remove every user turn.

Expected behavior

Every compacted continuation sent through Chat Completions should contain a real top-level user anchor, while preserving the retained assistant/tool ordering.

Suggested fix

Add a small invariant immediately before constructing ChatCompletionForm:

def _ensure_user_query(messages: list[dict]) -> list[dict]:
    if any(message.get("role") == "user" for message in messages):
        return messages
    return [
        {
            "role": "user",
            "content": (
                "Continue the task using the conversation summary "
                "and the tool results below."
            ),
        },
        *messages,
    ]

# After removing internal message IDs:
api_messages = _ensure_user_query(api_messages)

This keeps normal histories unchanged and adds one synthetic continuation turn only when compaction removed every user message.

Regression tests

  1. A history that already has a user message is returned unchanged.
  2. An assistant/tool-only history gets one non-empty user message prepended.
  3. Existing assistant/tool messages retain their exact order and content.
  4. Repeated compaction of a single-user agentic task still produces a provider request containing a user turn.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in cptr/utils/chat_task.py by reading _find_safe_split and the code that removes internal message IDs before constructing ChatCompletionForm. Verify the existing compaction flow and add regression coverage for user-preserving, assistant/tool-only, ordering, and repeated-compaction cases. Done means every compacted Chat Completions request contains a top-level user turn without changing histories that already have one.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
api, backend, testing-qa
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.