Raisonnement : un gabarit qui refuse le niveau ne tue plus le tour

Qwen3.8-27B valide `reasoning_effort` au lieu de l'ignorer : il connaît
xhigh/medium/low, pas « high » — le niveau que l'interface enregistre par
défaut. Chaque message partait donc en 500, avec une trace jinja affichée
en guise d'erreur, et le 500 tombait dans la branche « prompt trop long »
de runChat : loki compactait l'historique pour rien avant d'abandonner.

Le refus dit lui-même ce que le gabarit accepte. On le lit (llm_effort.go),
on traduit le niveau demandé vers le plus proche sur l'échelle
none/minimal/low/medium/high/xhigh — à égalité, le plus fort, dégrader en
silence étant pire que générer un peu plus longtemps — et on rejoue le tour,
historique intact. Sans liste annoncée, le champ est simplement retiré.
« aucune » n'est jamais traduite : c'est une coupure, portée par
`enable_thinking` que tous les gabarits comprennent.

La traduction est retenue par modèle : les messages suivants ne repaient pas
l'aller-retour. Le repli est tracé sur stderr, sinon l'intensité choisie dans
l'interface n'est pas celle qui part au moteur sans que rien ne le dise.

« maximale » (xhigh) rejoint la liste des niveaux proposés : aucun gabarit ne
les connaît toutes, et sans elle le maximum d'un Qwen3.8 restait hors
d'atteinte. Le repli couvre les gabarits qui la refusent.
This commit is contained in:
Claude committed 2026-08-20 08:49:09 +00:00
1 parent 6a77999aac
commit f3b0f78b64
9 files changed
+370 -12

No files matched your search

+12 -5
View File
@@ -72,17 +72,24 @@ func reasoningActive(v string) bool {
// ReasoningEfforts liste les valeurs proposées par l'UI pour REASONING_EFFORT.
// "" (auto) n'y figure pas : c'est l'absence de réglage.
var ReasoningEfforts = []string{"none", "low", "medium", "high"}
//
// Aucun gabarit ne les connaît toutes — gpt-oss lit low/medium/high, Qwen3.8 lit
// low/medium/xhigh — et certains REFUSENT celles qu'ils ne connaissent pas (voir
// llm_effort.go, qui traduit alors vers le niveau accepté le plus proche). La
// liste reste donc l'union des niveaux courants plutôt que le plus petit
// dénominateur commun, sinon le maximum d'un modèle est hors d'atteinte.
var ReasoningEfforts = []string{"none", "low", "medium", "high", "xhigh"}
// reasoningEffortValue normalise REASONING_EFFORT en une valeur envoyable telle
// quelle à llama-server, ou "" pour ne rien envoyer du tout.
//
// llama-server accepte `reasoning_effort` dans /v1/chat/completions : la valeur
// `none` coupe le raisonnement, toute autre valeur est simplement passée au
// gabarit jinja du modèle. Un gabarit qui ne la lit pas l'ignore sans erreur —
// c'est pourquoi il n'y a pas de repli à prévoir ici, contrairement à un backend
// qui rejetterait la requête. En pratique seuls les modèles dont le gabarit
// gère `reasoning_effort` (gpt-oss et apparentés) changent de comportement.
// gabarit jinja du modèle. Un gabarit qui ne la lit pas l'ignore sans erreur ;
// un gabarit qui la VALIDE, lui, peut lever une exception jinja et faire
// répondre 500 à llama-server (Qwen3.8 sur « high »). Le repli n'est pas ici —
// on ne sait pas d'avance ce que le gabarit accepte — mais dans llm_effort.go,
// qui apprend la réponse du refus lui-même et rejoue le tour.
func reasoningEffortValue(v string) string {
switch s := strings.ToLower(strings.TrimSpace(v)); s {
case "", "auto", "default":