Synchronisation avec l'amont AJEAN (v0.9.5 → v0.10.7)

Le fork est parti de la v0.9.4 ; l'amont en est à la v0.10.7. Reprise de ce
qui manque VRAIMENT ici, en laissant de côté ce que loki a déjà résolu à sa
façon (contexte MTP via --parallel 1, jauge de contexte, chrono de tour,
vignettes d'images, ligne d'état de génération).

Rendu du chat cadencé puis lissé (amont v0.9.5 issue #24, v0.10.5). Chaque
token re-parsait le Markdown du bloc ENTIER : du O(n²) qui faisait ramer
l'interface sur un long raisonnement — le moteur débitait toujours autant, mais
les tokens semblaient arriver au ralenti et un simple rafraîchissement
« réparait » tout. Le texte s'accumule désormais et n'est re-rendu qu'à
intervalle adaptatif (16 ms sur un petit bloc, jusqu'à 500 ms sur un énorme),
soldé à chaque frontière (outil, bascule de rôle, fin de tour, erreur, rejeu).
Par-dessus, un lissage d'apparition découple l'arrivée de l'affichage : le
décodage spéculatif rend les tokens par rafales, le texte sautait par paquets ;
il s'écoule maintenant à cadence régulière. Rejeu exclu — relire un fil ne doit
pas être une lente réécriture.

Échantillonnage réglable par preset (amont v0.9.5/v0.9.6) : TEMP, TOP_P, TOP_K,
MIN_P, PRESENCE_PENALTY, REPEAT_PENALTY, injectés dans chaque requête (donc sans
redémarrage du moteur), vide = défaut du serveur. Sans ça seule la température
voyageait et le reste retombait sur les défauts de llama.cpp, rarement ceux que
recommande le modèle. Différence avec l'amont : REASONING_EFFORT n'est PAS
traité là — loki lui réserve un chemin plus riche, et l'écrire ici écraserait
`chat_template_kwargs`, donc la consigne « aucune ».

Un seul message système, en tête, à l'envoi (amont v0.9.8, issue #26).
steerSystem ne couvrait que les consignes de loki ; un historique venu
d'ailleurs peut encore en porter deux, et Qwen3.x en --jinja répond alors
« System message must be at the beginning ». Copie normalisée : l'historique
affiché et persisté garde sa forme.

Détection Vulkan multi-distro (amont issues #28, #29) : le chemin Debian codé en
dur est invisible sur Fedora/RHEL/Atomic, où le plan de build retombait sur le
CPU. ldconfig d'abord, puis les chemins connus.

Dossier de travail (amont v0.10.2) : la consigne dit maintenant ce que le
dossier EST — l'endroit par défaut de tout ce que le modèle produit — et nomme
les dossiers système à ne pas toucher, au lieu d'interdire vaguement d'en sortir.

Non repris : les tâches planifiées (~1200 lignes + interface, à décider), et le
quoting cmd.exe par .bat temporaire (loki tourne en conteneur Linux).
This commit is contained in:
Claude committed 2026-08-20 09:31:59 +00:00
1 parent 40f34170b9
commit 03ae361ade
12 files changed
+607 -20

No files matched your search

+55
View File
@@ -234,6 +234,61 @@ func splitArgs(s string) []string {
return out
}
// samplingKeys : les paramètres d'échantillonnage réglables par preset. Repris
// de l'amont AJEAN (v0.9.5/v0.9.6).
//
// Ils sont injectés dans CHAQUE requête de complétion (applySampling) et non
// passés à llama-server au lancement : une valeur présente dans la requête
// l'emporte de toute façon sur le défaut du serveur, donc c'est le seul endroit
// où le réglage prend vraiment effet. Sans ça, seule la température voyageait, et
// top_k/min_p/… retombaient sur les défauts de llama.cpp — rarement ceux que
// recommande le modèle (Qwen3.8 veut top_k 20 / min_p 0, pas 40 / 0.05).
//
// Correspondance clé de config → champ du corps OpenAI. Une valeur vide vaut
// « clé absente » : llama-server garde alors son propre défaut pour ce champ.
var samplingKeys = []struct {
cfg, field string
integer bool
}{
{"TEMP", "temperature", false},
{"TOP_P", "top_p", false},
{"TOP_K", "top_k", true},
{"MIN_P", "min_p", false},
{"PRESENCE_PENALTY", "presence_penalty", false},
{"REPEAT_PENALTY", "repeat_penalty", false},
}
// applySampling superpose les paramètres d'échantillonnage du preset au corps
// d'une requête de complétion. Les clés vides sont ignorées (le serveur garde son
// défaut) ; TEMP, s'il est défini, l'emporte sur la température déjà posée dans le
// payload — c'est le réglage recommandé du modèle, et l'interface n'a pas de
// curseur de température qui pourrait le contredire.
//
// Contrairement à l'amont, REASONING_EFFORT n'est PAS traité ici : loki lui
// réserve un chemin plus riche (llm_client.go + llm_effort.go — champ de haut
// niveau, kwargs du gabarit, repli quand le gabarit refuse le niveau). L'écrire
// depuis ici écraserait `chat_template_kwargs`, donc la consigne « aucune » avec.
func applySampling(payload map[string]any) { applySamplingFrom(payload, ReadConfig()) }
// applySamplingFrom est la partie pure d'applySampling : testable sans base.
func applySamplingFrom(payload map[string]any, cfg map[string]string) {
for _, s := range samplingKeys {
v := strings.TrimSpace(cfg[s.cfg])
if v == "" {
continue
}
if s.integer {
if n, err := strconv.Atoi(v); err == nil {
payload[s.field] = n
}
continue
}
if f, err := strconv.ParseFloat(v, 64); err == nil {
payload[s.field] = f
}
}
}
// Le plafond d'appels d'outils par tour (TOOL_LIMIT) et l'anti-boucle ont été
// retirés : ils coupaient surtout des tours légitimes. Le bouton stop est le
// seul frein.