feat(config): add [reasoning_only] section for retry count and custom… - #5867
feat(config): add [reasoning_only] section for retry count and custom…#5867Gabriel-Degret wants to merge 6 commits into
Conversation
|
Thanks @Gabriel-Degret for taking the time to contribute. This repository is observing a maintainer-managed PR intake gate in dry-run mode, so this pull request is staying open. This note helps maintainers prepare the allowlist before any enforcement is considered. Please read |
0922990 to
e361f6c
Compare
… reprompt message
e361f6c to
cd21201
Compare
CI failure — pre-existing, unrelated to this PRThe single Windows CI failure: ... is pre-existing on
The test exists identically on 8 reasoning-only-related tests and 1 new config test all pass ✅. |
|
Hi @Gabriel-Degret — I'm Claude Opus 5, working with @Hmbown on the 0.9.12 release. Thank you for this: you found a real gap, and the idea is landing in 0.9.12. What we took, as yours. The whole config surface is in — the The one thing we implemented differently, and why. The nudge was delivered with So we kept your config surface and changed only the delivery: the nudge is now attached to a single outbound request and dropped, never reaching There's a regression test, Two smaller notes, both cosmetic and neither a criticism of the work: we changed the default nudge text to We'd genuinely welcome your follow-up here as 0.9.12 goes out. If the escalation policy is wrong for the models you hit this on — if the bare retry is never enough for a particular provider and the nudge should fire from attempt 1, or be configurable per route — you have better data on that than we do, and we want this properly fixed as much as you do. Thanks again. |
…udge A reasoning model that closes a turn having emitted only hidden thinking — no answer text, no tool call — is recovered by re-requesting. That ceiling was a hard-coded 2. It is now `[reasoning_only] max_reprompts`, and the nudge that rides a retry is `[reasoning_only] reprompt_message`. Harvested from @Gabriel-Degret's PR Hmbown#5867, which found the gap and built the config surface, the accessors, the wiring through every EngineConfig construction site, and the docs — all taken here. One thing is implemented differently. The submitted version delivered the nudge with `add_session_message`, which does `session.add_message()` + `emit_session_updated()`. That writes a user-role message the operator never typed permanently into the session: it persists, it shows in the transcript and exports, and the model re-reads it on every later turn. It also contradicts the comment three lines above the code it changed, which is still there and still true — "Nothing was persisted for this response (a bare Thinking block is not sendable), so re-issuing the request is an exact cached-prefix retry — no synthetic message, no prefix churn." So the nudge is now turn-scoped: it is attached to exactly one outbound request and dropped, never reaching `session.messages`. It also escalates rather than firing every time. Attempt 1 stays the bare cached-prefix retry the comment describes, which is nearly free and usually enough. Only from attempt 2 — where an identical request has already come back answerless, so repeating it would only reproduce the same reply — does the nudge ride along. `the_reasoning_only_nudge_rides_one_request_and_never_joins_the_session` distinguishes the two by message counts across four requests: persisted, they accumulate (n, n, n+1, n+2); request-scoped, the nudged requests each carry one extra over the same baseline. Re-pointing the branch at `add_session_message` fails it exactly there ("the nudge did not accumulate", left: 3, right: 2). Two smaller departures: the default nudge text is now "Continue: give your answer, or make the next tool call." rather than "So, what's up ? Keep running !", to match the product's voice; and `MAX_REASONING_ONLY_REPROMPTS` is deleted rather than left unused, since `config::DEFAULT_REASONING_ONLY_REPROMPTS` is now its single owner. An unrelated drive-by in `plugins/marketplace/document.rs` (renaming `link` to `_link`) is not taken — it was the branch's only merge conflict. The PR's red CI is `plugin_e2e_acceptance`, unrelated to this change and green on current main; its branch point predates the fix. Gates: fmt PASS · clippy -p codewhale-tui PASS · nextest -p codewhale-tui 11989 passed / 1 failed / 13 skipped, the failure being the known exec_persistent_service 120s timeout under parallel load (3/3 in isolation) · nextest -p codewhale-config 634 passed. Co-authored-by: Gabriel Degret <28120444+Gabriel-Degret@users.noreply.github.com> Harvested-from: PR Hmbown#5867 by @Gabriel-Degret Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QCLLenseqfQdVaZUBqn9yG
0.9.12 had no `### Contributors` section, `web/lib/release-credits.ts` still held the 0.9.11 cohort, and the contributor doc band was missing five people whose work landed. Three surfaces that must agree, disagreeing three ways. Everyone whose work reached main this cycle is now named in all three, derived from `Co-authored-by` and `Harvested-from` trailers on v0.9.11..HEAD rather than from memory. The five who were missing: - @qiuYliangM (秋月凉梦) — co-author on Hmbown#5621, Hmbown#5622, Hmbown#5623 - @whp233 — Hmbown#5716, landed as Hmbown#5719 - @Gabriel-Degret — Hmbown#5867 - @huangxianzhan — Hmbown#5868 - @zhuowp — Hmbown#5869 @aboimpinto's entry gained Hmbown#5825 and Hmbown#5865, @M-Maciej's gained Hmbown#5533/Hmbown#5831. `requiredCandidateCredits` in public-surface-facts.json moves from the 0.9.11 cohort to this one — that guard is meant to hold the *current* release's credits in every surface, and it had been pinning the previous release's. `RELEASE_HELPERS` is deliberately empty. Every credited contribution this cycle arrived as code that landed; a name there that no landed change can point at would be a nicer-looking ledger and a less true one. AUTHOR_MAP gains canonical numeric-noreply identities for all five, since the trailers on their harvest commits used raw emails and old-style noreply addresses. web: 386 tests, 386 passed. check:facts, check:docs, check:tokens, check:locales all PASS. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QCLLenseqfQdVaZUBqn9yG
Summary
Add a
[reasoning_only]config section to make the reasoning-only retry behavior user-configurable.Before:
MAX_REASONING_ONLY_REPROMPTS = 2was hardcoded. When a reasoning model returned only hidden thinking with no answer or tool call, the engine silently retried exactly twice, then failed.After: Users can configure the retry count and optionally inject a custom reprompt message to nudge the model.
When
reprompt_messageis set, the engine inserts it as a runtime user message before re-issuing the request. When unset, the original cached-prefix retry behaviour is preserved (no synthetic message).Testing
cargo fmt --all -- --check— cleancargo clippy --workspace --all-targets --all-features --locked— warning-free under the CI allow listcargo test --workspace --all-features --locked— passes, including 8 existingreasoning_onlytests and 1 new config testChecklist
docs/CONFIGURATION.md)crates/tui/src/config/tests.rs)No-Issue: add section [reasoning_only] in config.toml