LLM transport: graceful handling of reasoning models (thinking mode) #232
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
Self-hosters increasingly point Quest Board at reasoning models (Qwen3, DeepSeek-R1, gpt-oss, …) that emit hidden
reasoning_contentbefore any visible output. v3.10 addedchat_template_kwargs={"enable_thinking": false}on json-mode llama.cpp calls (llm_service._structured_llamacpp, commit07552a3), which fixes structured generation — every GM Workbench tool now works against a live Qwen3 server. This issue tracks the remaining rough edges so the experience degrades gracefully instead of opaquely.Discovered while dev-testing v3.10 against a local
qwen3.5llama.cpp server (see PR #225 thread).Rough edges to address
Admin → Bot Settings "Test LLM" is misleading for reasoning models.
audio_service._test_llm_llamacpp(and_test_llm_anthropic) cap the probe atmax_tokens=16. A reasoning model spends all 16 onreasoning_contentand returns empty visiblecontent, so the Test button reports empty/blank even though the endpoint is perfectly healthy. Fix: raise the probe'smax_tokens, and/or disable thinking in the test call, and/or detectreasoning_contentand report success. Apply consistently across the ollama/openai/anthropic/llama.cpp test variants.Opaque "empty response" on
finish_reason='length'.When a model exhausts its output budget on reasoning,
_structured_llamacppraises the generic "LLM returned an empty response … the model may not support json_object mode." For a reasoning model the real cause is token-budget exhaustion. Detectfinish_reason == 'length'and surface an actionable message (e.g. "the model hit its output-token limit — if it is a reasoning model, disable thinking or raise the output limit").Make thinking-disable configurable / broaden coverage.
Today
enable_thinking=falseis hardcoded to the json-mode llama.cpp path. Consider:llm_config;/api/generateaccepts athinkboolean);json_mode=false, e.g. campaign storyline) where a reasoning model can still overrun.Scope & priority
Backend LLM transport + Admin Bot Settings UI. Backend-only, no
BOT_CONTRACT_VERSIONbump. P4 / backlog quality-of-life — the core v3.10 path already works without it. Non-blocking for the v3.10 release.Ref: PR #225; commit
07552a3;llm_service.py,audio_service._test_llm_*.🤖 Generated with Claude Code
Verified before closing. Both reported bugs are fixed and tested; one "Consider:" item was not built, and this note is so that stays visible.
Fixed:
_TEST_LLM_MAX_TOKENS = 64applied consistently, with per-provider hidden-reasoning detection: llama.cpp checksreasoning_content, Ollama checksthinking, OpenAI checksusage.completion_tokens_details.reasoning_tokens(audio_service.py:4050-4192). Anthropic is N/A on a bare call. Tested across all four (tests/test_llm_transport.py:492-588).finish_reason='length'with empty content gives an actionable message —llm_service.py:605-619says the budget likely went to hidden reasoning and names the remedy, instead of the generic empty-response error.think: falsesent wheneverjson_mode(llm_service.py:480-501).Not built: the admin toggle ("this model uses reasoning") persisted in
llm_config. Greppedsettings_service.pyand the whole frontend for reasoning/thinking — nothing. Thinking-disable is unconditional forjson_moderather than operator-controlled. The issue phrased this as "Consider:" rather than a requirement, and unconditional-for-structured-output is defensible, since structured extraction never benefits from reasoning tokens. But an operator with a model that behaves differently has no lever.Deliberately not done, correctly: prose flows (
json_mode=False) keep thinking on, with a code comment explaining that turning it off is a narrative-quality trade-off this module has no basis to make on the operator's behalf, and a test pinning it (test_llamacpp_prose_mode_omits_thinking_kwarg). That boundary later became the model for #423's sampling decision.Closing. Part of a full acceptance-criteria pass across the v4.0.0 milestone.