mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-18 20:57:05 +02:00
Passe de qualité : contexte explosé par les captures, sonde vision, courses
Le journal moteur de production a livré la vraie cause première des 500 en cascade : « request (55407 tokens) exceeds the available context size (32768) ». Le message multimodal qui relaie une capture d'écran au modèle était PERSISTÉ dans l'historique ; son base64 (des dizaines de milliers de tokens) repartait à chaque tour, et la conversation dépassait définitivement la fenêtre — plus aucun tour ne passait, et l'exception Jinja du rattrapage (corrigée au commit précédent) masquait tout. - L'image devient ÉPHÉMÈRE : jointe au tour en cours, jamais à l'historique. Le modèle la regarde maintenant ; sa description textuelle, elle, reste. - stripImageParts guérit les conversations déjà empoisonnées au chargement et à la bascule : parties image retirées, texte aplati. - engineSeesImages : l'image n'est envoyée que si llama-server DÉCLARE la vision (/props, modalities.vision, cache 10 s). La clé MMPROJ ne suffit pas — projecteur d'un autre modèle ou modèle sans vision (gpt-oss), le gabarit sérialise le base64 en texte. La description de l'outil suit le même état : ne jamais promettre une image qui n'arrivera pas. Deux défauts trouvés à l'analyse, au passage : - renderBody reconstruit le DOM à chaque delta du streaming : une image déjà affichée était RE-TÉLÉCHARGÉE à chaque token arrivé après elle. Cache de blobs par URL, une seule requête par capture. - les opérations de discussions (création, bascule, renommage, suppression) entrelaçaient leurs lectures-écritures d'index sous requêtes simultanées : sérialisées par un verrou dédié. Tests : historique guéri (aplati sans l'image), transmission conditionnée à la sonde, description alignée ; suite complète, vet, staticcheck verts.
This commit is contained in:
7 files changed
+172
-14
No files matched your search
@@ -146,21 +146,30 @@ function renderBody(el, text){ const b=bodyOf(el); b.innerHTML = md(encodeMdLink
|
||||
// est définie. On récupère donc l'image par fetch authentifié et on la pose en
|
||||
// blob:. Sans ça, toute instance protégée par une clé n'affichait que des images
|
||||
// cassées. Les URLs externes (http…) ne sont pas touchées.
|
||||
// Cache des blobs par URL source. renderBody est rappelé à CHAQUE delta du
|
||||
// streaming et reconstruit le DOM : sans cache, une image déjà affichée était
|
||||
// re-téléchargée à chaque token arrivé après elle. On garde l'objet URL (pas de
|
||||
// revoke) : quelques captures par discussion, mémoire négligeable, et un revoke
|
||||
// casserait les rendus suivants qui réutilisent la même entrée.
|
||||
const IMG_CACHE = new Map();
|
||||
function hydrateImages(root){
|
||||
root.querySelectorAll('img[src*="/api/chat/image"]').forEach(async img => {
|
||||
if(img.dataset.hydrated) return;
|
||||
img.dataset.hydrated = '1';
|
||||
const src = img.getAttribute('src');
|
||||
img.classList.add('chatimg');
|
||||
const cached = IMG_CACHE.get(src);
|
||||
if(cached){ img.src = cached; return; }
|
||||
try{
|
||||
const r = await jfetch(src.startsWith('/') ? src : '/' + src);
|
||||
if(!r.ok) throw new Error(r.status);
|
||||
const url = URL.createObjectURL(await r.blob());
|
||||
IMG_CACHE.set(src, url);
|
||||
img.src = url;
|
||||
img.classList.add('chatimg');
|
||||
// La révocation attend le chargement : révoquer tout de suite laisserait
|
||||
// une image vide sur les navigateurs qui décodent en différé.
|
||||
img.addEventListener('load', () => URL.revokeObjectURL(url), {once:true});
|
||||
}catch(e){
|
||||
// Pas de remplacement définitif pendant le STREAMING : l'URL peut être
|
||||
// tronquée en cours de frappe (404 transitoire) et le prochain delta
|
||||
// re-rend le markdown complet. On note l'échec sans le graver.
|
||||
const note = document.createElement('span');
|
||||
note.className = 'muted';
|
||||
note.textContent = '[image indisponible : ' + src.replace(/^.*path=/, '') + ']';
|
||||
|
||||
Reference in new issue
Block a user