From 961720c1b17d31bc28fca6abadcdb149fb3bcb4f Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Sat, 14 Mar 2026 08:08:34 +0000 Subject: [PATCH] studio: handle reasoning_content in GGUF streaming llama-server sends thinking/reasoning tokens as "reasoning_content" in the SSE delta (separate from "content"). The studio was only reading delta.content, so all reasoning tokens from models like Qwen3.5, Qwen3-Thinking, DeepSeek-R1, etc. were silently dropped. This caused "replies with nothing" for thinking models: the model would spend its entire token budget on reasoning, produce zero content tokens, and the user would see an empty response. Fix: read reasoning_content from the delta and wrap it in ... tags. The frontend already has full support for these tags (parse-assistant-content.ts splits them into reasoning parts, reasoning.tsx renders a collapsible "Thinking..." indicator). Verified with Qwen3.5-27B-GGUF (UD-Q4_K_XL): - Before: "What is 2+2?" -> empty response (all tokens in reasoning) - After: shows collapsible thinking + answer "4" --- studio/backend/core/inference/llama_cpp.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/studio/backend/core/inference/llama_cpp.py b/studio/backend/core/inference/llama_cpp.py index 21a25d51b2..2f68958bbd 100644 --- a/studio/backend/core/inference/llama_cpp.py +++ b/studio/backend/core/inference/llama_cpp.py @@ -592,6 +592,7 @@ class LlamaCppBackend: url = f"{self.base_url}/v1/chat/completions" cumulative = "" + in_thinking = False try: with httpx.Client(timeout = None) as client: @@ -615,6 +616,9 @@ class LlamaCppBackend: if not line: continue if line == "data: [DONE]": + if in_thinking: + cumulative += "" + yield cumulative return if not line.startswith("data: "): continue @@ -624,8 +628,23 @@ class LlamaCppBackend: choices = data.get("choices", []) if choices: delta = choices[0].get("delta", {}) + + # Handle reasoning/thinking tokens + # llama-server sends these as "reasoning_content" + # Wrap in tags for the frontend parser + reasoning = delta.get("reasoning_content", "") + if reasoning: + if not in_thinking: + cumulative += "" + in_thinking = True + cumulative += reasoning + yield cumulative + token = delta.get("content", "") if token: + if in_thinking: + cumulative += "" + in_thinking = False cumulative += token yield cumulative except json.JSONDecodeError: