mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-20 05:28:15 +02:00
6a45f19831081cdbe6c8f2be920d0a23fdbb2a5e
23
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
01c11be4ae |
Correctifs repris d'AJEAN et d'OpenFox : LSP, shell, clé web, MCP, recall, compactage, relances
- LSP (lsp.go) : un signal resté dans le tampon par une republication tardive faisait rendre « aucun diagnostic » à l'édition suivante, avant même que le serveur ait vu la nouvelle version. Le canal est vidé, une publication d'une version antérieure est ignorée, et check attend 350 ms de calme après la dernière publication (liste vide puis analyse : c'est l'analyse qui compte), toujours borné par lspDiagWait. Au passage : interblocage latent levé (writeMsg → markDead reprenait mu, déjà tenu par check) — dead devient atomique. - run_shell : ErrWaitDelay (commande finie, process laissé en arrière-plan sur les tubes) rend la sortie au lieu de « WaitDelay expired » qui l'effaçait (AJEAN 7403a8a0). - Clé de pilotage web : lue depuis un cache à chaque requête au lieu d'ouvrir la base sous dbMu (AJEAN #120). Lectures réussies seulement, invalidé par toute écriture du bucket d'état, par la date/taille du fichier (autre process) et au bout d'une seconde. - MCP : l'appel suit le contexte du tour (le bouton stop l'annule), délai par serveur (callTimeoutSec) et allongé par l'argument timeout de l'outil, plafonné à 30 min ; un délai dépassé n'est plus relancé (OpenFox 2ef584b8). - Recall : les blocs d'une compaction s'archivent en une seule transaction au lieu d'une par bloc (AJEAN #130). - Compactage en cours de tour : après un essai qui n'a rien réduit, pas de nouvel essai avant +5 % de la fenêtre (sauf à 90 %), pour les tâches et sous-agents aussi ; la cause d'un résumé raté est journalisée (AJEAN 68659e73). - Motifs de relance : un motif vide (qui correspondait à tout) est écarté, une surcharge sans motif utilisable retombe sur l'embarqué, et un changement de retry_patterns agit sans redémarrer (OpenFox 8e9b4869, c52c5bf7). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
7a57322a62 |
Slots : SIDE_SLOT ouvre un second slot pour les travaux annexes, en opt-in
En --parallel 1, une vérification, un sous-agent, une tâche ou un bench prennent le slot de la discussion : son état part dans le cache RAM et en revient, ou se recalcule s'il n'y tient plus. Avec SIDE_SLOT=on (off par défaut), le moteur ouvre deux slots et la discussion garde le sien. Dimensionnement choisi pour qu'un débordement concurrent soit impossible par construction : cache KV NON unifié (--no-kv-unified) et -c 2×CTX, soit deux flux séparés de CTX jetons. Sous cache unifié, les deux slots partagent le pool et, plein, llama.cpp renvoie « Context size has been exceeded. » aux deux — que Loki prenait pour un débordement de la conversation. Chaque slot garde la fenêtre entière : ctxWindow et la compaction restent sur CTX, rien n'est raccourci. --cache-idle-slots ne vide les slots au repos que sous cache unifié : rien à régler. - lancement : refus (un slot, ligne d'avant, note au journal) si modèle + deux états dépassent 90 % de la VRAM, VRAM ou GGUF inconnus, poids sur CPU, moteur sans --no-kv-unified, PARALLEL≠2, ou -c/-np/-kvu/--no-kv-unified/ --kv-unified-per-slot/--cache-idle-slots dans EXTRA_ARGS (ou leurs LLAMA_ARG_*) ; note de VRAM en plus, avertissement si NGL imposé - routage id_slot seulement si /props annonce 2 slots d'au moins CTX jetons : 0 pour le tour, ses étapes, le préchauffage et le résumé en continuation ; 1 pour vérification, sous-agents, tâches, bench, résumé sur transcription - clients /v1 (proxy et relais) : corps réécrit en id_slot 1, quel qu'il soit - cohérence lot 1 : l'effacement de slot s'abstient (2 slots) ; une requête du slot 1 n'avance pas le numéro d'engineSlotHolds, la continuation de compaction reste possible après une vérification - « Context size has been exceeded. » sans nombre de jetons, deux slots en service : requête rejouée telle quelle, jamais compaction ni réduction - préchauffage permis sur les deux slots de SIDE_SLOT, vers le slot 0 - éditeur de preset : VRAM du second slot ou raison du refus - tests : ligne identique sans la clé, chaque conflit refuse sans changer la ligne, routage par nature, aucun id_slot sur un slot / preset externe / fenêtre partagée, rejeu sans compaction, proxy forcé, préchauffage Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
f74f64ed90 |
Cache : KEEP_TURN_IMAGES garde les images d'outils et NUDGE_IN_TOOL range le rappel de budget, en opt-in
Une capture ou une image vue par see_image ne vivait que le temps du tour : au tour suivant elle manquait à l'historique, le préfixe en cache divergeait à elle et toute la boucle d'outils qui suivait était recalculée. Sur Qwen3.5, le rappel de budget en message user faisait de même pour le tour en cours : le nouveau message devient la « dernière question » et tout le tour est rendu autrement. Deux clés, off par défaut et étiquetées zone grise ; sans elles, requêtes identiques à l'octet près (testé). - KEEP_TURN_IMAGES : image gardée par référence (chatimg, chiffrée si la mémoire l'est), relue à l'identique avant d'être gardée, vision active revérifiée à chaque tour ; preset externe seulement s'il déclare la vision - coût mesuré par le moteur (écart de prompt_tokens moins le texte ajouté) ; image non mesurable = éphémère comme avant ; total sous 10 % de la fenêtre, la première qui dépasse et les suivantes du tour restent éphémères - compaction, réduction forcée et début ou milieu de tour au seuil retirent ces images d'abord (légende et imageLostMarker gardés), le résumé ne suit que s'il reste nécessaire ; clé retirée ou vision perdue : retirées au tour - relais marqué ImgRelay (persisté, retiré à l'envoi) : ni demande réinjectée ni preuve de demande servie, fil rouvert compris ; une image non gardée n'entre plus dans l'historique par une compaction en cours de tour - discussion seulement (ni tâche, ni sous-agent, ni vérification) ; fichier d'image rafraîchi à chaque usage, l'élagage de 24 h ne le prend pas - NUDGE_IN_TOOL : rappel de budget au bout du dernier résultat d'outil, même message persisté ; moteur local, sonde de gabarit « préfixe instable » pour ce modèle ; sinon, ou forme inattendue, message à part comme avant Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
2097c9f915 |
Compaction : COMPACT_CONTINUATION résume dans le prolongement du prompt en cache, en opt-in
Le résumé d'une compaction part aujourd'hui dans une requête à part — prompt du résumeur et transcription des anciens tours — que le moteur local calcule à froid : des dizaines de secondes sur un 27B, des minutes sur un MoE, à chaque compaction. Nouvelle clé COMPACT_CONTINUATION (off par défaut) : quand le slot porte encore le prompt de la discussion, la requête de résumé est celle du tour telle qu'elle est partie, suivie d'une seule demande de résumé ; le moteur ne calcule qu'elle. Sans la clé, requêtes de compaction identiques à l'octet près (prompt du résumeur comparé à une copie figée, testé). - vue MODÈLE pour tout ce qui est rangé : bornes, archives recall, demande réinjectée, garantie de réduction et sortie ne changent pas ; la vue d'envoi (turnViewDry, wireMessages, buildChatPayload) ne sert qu'à la requête, rien d'injecté n'entre dans l'historique (testé) - frontière recalée sur la vue envoyée (messages non système un pour un, vérifiés rôle par rôle), désignée par le nombre de messages gardés et le début du premier ; tout résumer avant, dater l'avancement à la frontière - mêmes règles de résumé (+ mode Code), même budget, température 0.2 sans l'échantillonnage du preset, enable_thinking=false ajouté aux arguments du tour, reasoning_effort du tour, tool_choice « none », sans flux - messages utilisateur de fin hors de la vue : pas en cache, et pas deux `user` d'affilée pour les gabarits stricts - slot vérifié : tampon posé par une étape de tour acceptée (llm_slots.go, sous le verrou du compteur en vol), perdu dès qu'une autre requête part — vérification, sous-agent, tâche, préchauffage, résumé, bench, /v1 — ou que le modèle ou la fenêtre changent ; relu à l'envoi - marge en jetons réels (dernier compte + non vu + demande + budget + 5 %) - repli sur la transcription au moindre écart : refus du moteur, réseau, appel d'outil émis (tool_calls décodés) ou écrit en texte, raisonnement seul, résumé vide ; refus du gabarit ou appel d'outil = suspendue pour le modèle jusqu'au redémarrage, deux échecs de suite aussi - avec la clé : pas de résumé quand même un vide ne réduirait pas de 20 % (borne exacte, avant tout archivage) ; après un refus faute de réduction, pas de nouvel essai avant +10 % de contexte, jamais à 90 % de la fenêtre, oublié quand le contexte baisse - réactif, fenêtre pleine, bouton manuel, tâches, sous-agents, terminal, preset externe : chemin d'avant ; télémétrie kind=compact avec la discussion Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
4932999d5c |
Contexte : PROJ_SNAPSHOT fige le bloc projet par discussion, ses changements arrivent en mise à jour, en opt-in
Le contexte du projet (description, index mémoire, trackers, AGENTS.md) part en tête du premier message utilisateur, reconstruit à chaque tour : une page créée ou une valeur de tracker notée faisait recalculer toute la conversation derrière lui, 10 à 45k tokens. Nouvelle clé PROJ_SNAPSHOT (off par défaut) : avec on, chaque discussion garde une copie datée du bloc, renvoyée à l'octet près, et les changements arrivent en tête du message suivant dans un bloc <context_update from="loki">, rangé dans l'historique. Sans la clé, la requête est identique à l'octet près (testé contre l'ancien assemblage). - en-têtes figés « as of <date> » pour l'index mémoire et les trackers, sans « answer straight from this » ; ligne fixe du système, seulement avec la clé - mises à jour par type : +/~/- par page (clé « ](fichier) »), une ligne complète par tracker (clé = slug), texte COMPLET pour la description, AGENTS.md, ou un index dont la prose a changé — jamais de diff de prose - état annoncé structuré et persisté (ProjSnap, omitempty), instantané pris sur marqueur explicite : la première page d'un projet vide arrive en mise à jour, le premier message ne bouge pas - rafraîchi (blocs retirés de tout l'historique) quand le prompt change de toute façon : compaction début/fin/manuelle, compaction ou réduction en cours de tour (bloc vivant aussitôt, rangé comme instantané : pas de recalcul de plus), système ou outils modifiés, redémarrage de Loki, réglage moteur ou modèle, projet, nom, mode mémoire, mode Code, dépôt, textes des en-têtes ; et au-delà d'un seuil de mises à jour accumulées - mise à jour posée sous c.mu avec l'epoch, après la compaction de début de tour ; texte ou multimodal ; retirée du titre, de l'export JSON, de l'entrée du résumeur et de la tâche du vérificateur - loadFrom, Reset et le chargement remettent l'instantané à zéro ; clé retirée = nettoyage ; agent off = rien du projet ; tâches et presets externes inchangés ; un sous-agent ne voit pas le bloc du tour - ordre des trackers déjà déterministe (lot 1, test existant) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
7c9aa04fd9 |
Raisonnement : REASONING_ECHO renvoie au moteur local la réflexion du modèle, en opt-in
Les gabarits Qwen3.5/3.6 rendent <think>…</think> pour chaque message assistant après la dernière question : c'est leur format entraîné. Loki ne gardait que le texte et les appels, donc à chaque étape d'une boucle d'outils le modèle relisait ses étapes précédentes avec des blocs vides, et le moteur recalculait le dernier message. Nouvelle clé REASONING_ECHO (off par défaut) : avec on, le raisonnement séparé par le serveur est gardé avec chaque message et renvoyé au même modèle. Sans la clé, rien n'est capturé ni envoyé : la requête est identique à l'octet près (testé). - Message.ReasoningContent (reasoning_content) + ReasoningModel, persistés ; seulement depuis reasoning_content du flux, jamais depuis le découpage « </think> » fait chez nous (rendu en double) - un seul point de sortie (echoMessages) : étiquette toujours retirée, raisonnement retiré pour une API externe, un autre modèle, un message sans texte ni appel, ou si la sonde dit que le gabarit ne le rend jamais - comptage : estimateTokens/compactBounds comptent ce qui part et que le gabarit rend (passé seulement si la sonde dit qu'il le garde, ou l'ignore) ; ctxAfter ne retire plus un raisonnement renvoyé - débordement : relance d'abord sans raisonnement, avant toute compaction ; shrinkToFit retire le raisonnement le plus ancien avant tout le reste ; le torse compacté le perd comme le résumé - erreur de gabarit (raise_exception, thinking, Failed to parse messages) : relance une fois sans raisonnement avant tool_choice none / outils coupés ; retenu pour le modèle si la relance passe - runBuilderTurn applique enfin NewHistory comme generate (compaction perdue pendant une passe de correction) ; forwardStream affiche la bannière - sonde de gabarit : nouveau verdict renders_reasoning - REASONING_PRESERVE (lot 1) inchangée, son interaction documentée Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
9263df404d |
Cache : corrections de relecture — un rappel de loki ne laisse plus deux messages user d'affilée
Persister les rappels ouvrait trois façons de coller deux `user` dans l'historique, celles-là même qu'appendNudge voulait éviter : sur un gabarit à alternance stricte, chaque tour suivant aurait échoué. - Fin de tour : un rappel resté sans réponse (stop, erreur) ou suivi d'un ajout en cours de réponse est retiré avant persistance (dropStrayNudges), comme avant sa persistance. - Compaction : la queue ne commence plus sur un rappel ; la vraie demande réinjectée devant elle lui était collée. Elle recule d'un groupe d'outils. - isLokiInjected ne se contente plus du préfixe « [system] » : un message de l'utilisateur qui commence ainsi n'est plus pris pour un rappel (sa demande était sautée par la compaction, le vérificateur et le titre). - Relance tool_choice « none » : pas de repli après un stop, qui partait sur un contexte annulé et affichait une erreur. - Tests : appel par le protocole et refus 4xx sous « none » (et rien d'exécuté), frontière de compaction, nettoyage de fin de tour. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
38b488515a |
Cache : les rappels de loki et la relance après un 500 ne recalculent plus la conversation
Le rappel de budget d'outils et la relance « pensé sans agir » partaient au moteur sans entrer dans l'historique : au tour suivant, le fil divergeait juste avant eux et toute la boucle d'outils qui suivait était recalculée. Le modèle relisait en prime une histoire qu'il n'avait jamais vue. La relance après un 500 (appel d'outil mal parsé) retirait les outils et modifiait le message système : tout le prompt repartait de zéro, deux fois. - Rappels persistés tels qu'envoyés (appendNudge), sauf derrière un autre message user : éphémères là, pour ne pas casser à chaque tour les gabarits à alternance stricte. - Ils restent reconnaissables (isLokiInjected) : la compaction ne les réinjecte pas comme demande en cours et ne les prend pas pour la preuve qu'une demande a été servie ; la réduction forcée coupe d'abord aux vraies demandes ; le vérificateur, l'export, le titre et le résumeur les sautent ou les présentent comme note du système. - Relance après un 500 sur le llama-server local : mêmes outils, même message 0, tool_choice « none » et la consigne au bout du dernier message, jamais persistée. Nouveau refus, appel tenté malgré tout ou réponse vide : repli une fois sur le chemin historique. Preset externe inchangé. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
72ba8e0fb6 |
Compaction : corrections de relecture — une balise </think> citée n'ampute plus le résumé
cleanSummary coupait toujours au DERNIER </think>. Un résumé de session Code qui cite la balise (un parseur, un gabarit — sessions sur Loki même) perdait donc tout ce qui la précédait, sans erreur ni trace : exactement la perte que le commit précédent voulait supprimer. - Le raisonnement ne se retire qu'en tête : un bloc <think>…</think> qui ouvre la réponse (plusieurs à la suite au besoin), ou un </think> sans <think> avant lui (gabarit qui ouvre le bloc dans le prompt). - Un bloc cité en milieu de texte reste tel quel. - La coupe par le filet (API qui ignore max_tokens) est tracée elle aussi. - Tests : CTX fixé (le filet en dépend), réponse du faux moteur derrière un mutex, cas de balises citées et de blocs multiples. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
e14ead98c4 |
Compaction : le résumé n'est plus amputé de son état d'avancement
Le résumé de compaction était coupé net à 2200 caractères (≈ 550 tokens) alors que max_tokens l'autorise jusqu'à ~1600 tokens. Le prompt du résumeur place l'ÉTAT D'AVANCEMENT en dernier : c'était donc exactement ce dont l'agent a besoin pour reprendre qui tombait, et les tokens décodés au-delà étaient jetés. - La borne passe à compactSummaryBudget()×6 caractères : un simple filet pour une API qui ignorerait max_tokens, qui borne déjà la sortie. - finish_reason=length : le texte est gardé, marqué « […] » et tracé dans le journal ([compact] résumé coupé par max_tokens…). - Un <think> ouvert en tête et jamais refermé, ou un contenu vide à côté d'un reasoning_content, est une erreur : l'appelant retombe sur le torse dégraissé au lieu d'installer du raisonnement brut comme mémoire. - Tests : table de cleanSummary, et un résumé de 7000 caractères qui revient intact du moteur. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
26fc348506 |
Contexte : corrections de relecture — la garde de marge ne compacte plus en boucle, et le preset externe compte comme avant
La garde de marge de génération réclamait 1,2 × la plus longue génération récente, sans plafond. Une étape de 40 k sur une fenêtre de 65 k (ou de 20 k sur 32 k, sans budget de raisonnement) demandait plus de place que l'historique fraîchement compacté n'en laisse : la garde repartait à chaque étape, et chaque fois résumait le résumé précédent. - Garde : jamais avant la moitié de la fenêtre. Au-delà, le rejeu sur « length » reste le filet. - Preset externe : le complément « messages arrivés depuis la mesure » ne s'applique plus. Son CtxUsed garde le raisonnement, qui couvrait déjà ce complément ; l'ajouter le faisait compacter plus tôt qu'avant. - Journal [ctx] : l'estimation d'avant compaction (début de tour, en tour) n'est plus comparée au compte d'une requête compactée, ce qui donnait de faux écarts. - Tests : génération énorme juste après compaction, seuil de 50 % pour toutes les fenêtres, comptage externe contre local. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
d6023ef8a0 |
Contexte : le raisonnement jamais renvoyé ne compte plus, et la compaction ne part plus trop tôt
CtxUsed valait usage.prompt_tokens + généré, raisonnement compris. Or Loki ne renvoie jamais ce raisonnement au modèle (Message n'a pas de champ pour lui) : la requête suivante pesait 1 à 8 k jetons de moins que le compte, et la compaction, qui perd de l'information, partait vers 44-47 k au lieu de 49 k sur une fenêtre de 65 k. Rien ne change dans les requêtes : seul le compte qui décide de compacter. - Comptage : seulement les morceaux reasoning_content séparés par le llama-server local, remis à zéro à chaque tentative. Le <think> découpé chez nous reste dans le message renvoyé pendant le tour, il n'est pas retiré ; l'API externe garde l'ancien calcul. Un morceau vaut au plus un jeton : on ne peut que sous-estimer, le sens sans danger. - Début de tour : le message utilisateur (et la file) arrivé depuis la dernière mesure s'ajoute en estimation, comme en cours de tour. - Vérificateur : sa trace isolée écrasait CtxUsed avec sa petite taille, et la fin de tour décidait sur ce chiffre-là. Plus maintenant ; la jauge de l'UI ne bouge pas non plus et suit le même calcul que le serveur. - Marge de génération : le compte gonflé offrait une marge par accident. Elle est remplacée par une vraie garde — compacter si 1,2 × la plus longue génération récente ne tient plus dans la fenêtre. Plancher et marge seuls ne devancent jamais le seuil de 75 %. - Fenêtre pleine en plein raisonnement (finish « length » au-delà du seuil) : compaction puis étape rejouée, au lieu du nudge « arrête de raisonner ». - Journal [ctx] : estimation contre compte réel au-delà de 2 % d'écart, chaque « length » et chaque nudge, pour vérifier qu'ils n'augmentent pas. - Le seuil reste à 75 % : la réserve fixe proposée (toolResultMax/3 + 6 k) laissait trop peu de place sans budget de raisonnement ni max_tokens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
18a723a671 |
Télémétrie : corrections de relecture — compaction aussi tolérante qu'avant, pas d'alerte de cache sur une API externe, seuil qui suit EXTRA_ARGS
La relecture du relevé par complétion a trouvé quatre écarts, tous d'affichage ou de robustesse ; rien ne touche aux requêtes ni au contexte. - Compaction : la réponse lue d'un bloc passait de Decoder à Unmarshal, plus strict (un octet parasite après le JSON faisait échouer une compaction qui passait). Retour au Decoder, et perfWire fait de même. - API externe : son cache suit ses propres règles, sans slot ni points de reprise à surveiller ici. Plus de ligne ambre « recalculés » pour elle ; le relevé reste dans l'anneau. - Seuil d'alerte : même priorité qu'au lancement — -cms d'EXTRA_ARGS, puis LLAMA_ARG_CHECKPOINT_MIN_SPACING_NT, puis CKPT_MIN_STEP ; -ub d'EXTRA_ARGS avant UBATCH. - UI : « recalculés après sous-agent, client /v1 » au lieu des identifiants internes (subagent, foreign). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
80b7793773 |
Télémétrie : chaque complétion dit enfin ce qu'elle a repris du cache, recalculé et accepté du brouillon
Jusqu'ici, impossible de savoir si une ligne volatile s'était glissée dans le prompt, si une mise à jour du moteur avait cassé les points de reprise ou si l'acceptation du MTP s'était effondrée : le chunk final de llama.cpp porte ces chiffres (cache_n, draft_n, cached_tokens), on les jetait. Lecture seule : aucun champ ajouté aux requêtes, et le comptage du contexte (usage.prompt_tokens → CtxUsed → compaction) ne change pas. - perf_log.go : décodage à part et tolérant (perfWire) — un compteur mal typé ne jette plus le chunk final ni ne fait échouer une compaction ; cached_tokens, strictement entier dans streamChunk, y déménage aussi. - Inconnu n'est pas 0 : pointeurs, et l'UI garde « prefill N tok » quand le moteur ne dit rien de son cache. - Nature (main, subagent, verify, task, compact, bench, foreign) portée par le contexte, pas par Caps ; TTFT au premier delta de tout type. - Perte de cache = total précédent − cache_n, seulement quand les messages prolongent strictement ceux de la complétion précédente de la même discussion et de la même nature (empreintes cumulées) ; jamais sur un flux coupé. Ce qui s'est intercalé (sous-agent, client /v1, bench) est nommé. - Anneau de 5000 entrées en mémoire, sans texte ; GET /api/perf/summary (derrière la clé) : taux de cache, recalcul par tour et par nature, médianes pp/tg par profondeur, acceptation du brouillon. - Ligne [perf] sur stderr seulement avec LOKI_PERF_LOG. - UI : « prefill X nouveaux / Y en cache », ambre au-delà d'un seuil relevé sur un modèle hybride (espacement des points de reprise). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
3565b594ea |
Moteur : cache de prompts dimensionné et travaux annexes isolés — la conversation n'est plus recalculée après un vérificateur ou un sous-agent
llama-server garde en RAM hôte une copie exacte des états de slot qu'il quitte (KV, état récurrent, points de reprise, brouillon MTP) et la recharge octet pour octet. Mais son défaut de 8 Gio ne tient pas une conversation de 30 à 65 k jetons à côté de l'état d'un vérificateur, d'un sous-agent, d'une tâche ou d'un bench : à la requête suivante il évince la conversation pour sauver l'annexe, et tout est recalculé (30 à 80 s sur un 27B). Rien de ce que voit le modèle ne change ici : seul le bruit de découpage des lots, comme le cache_prompt actuel. - CACHE_RAM (Mio ; vide/auto, nombre passé tel quel, -1, 0) → --cache-ram, seulement si l'aide du moteur le connaît. Loki se tait devant -cram d'EXTRA_ARGS et LLAMA_ARG_CACHE_RAM. L'auto ne descend JAMAIS sous le défaut : il agrandit seulement un modèle tout-GPU (VRAM NVIDIA connue, aucun poids ni KV sur CPU, NGL complet), d'après l'état mesuré dans le GGUF (2,5 × KV à CTX + état récurrent et points de reprise des hybrides), plafonné à 30 % de la RAM (limite cgroup comprise) et à la moitié de la RAM libre. - --slot-save-path LOKI_HOME/slots (0700, purgé au lancement) uniquement si le moteur est en boucle locale ou protégé par clé, et si le dossier existe. Les proxys /v1 et du relais refusent désormais toute action /slots (405). - engineSideJob efface le slot 0 APRÈS le sous-agent, la passe de vérification, la tâche planifiée et le bench (pas après la compaction : rien à y gagner). Synchrone, borné à 2 s, et seulement si : moteur local, build ≥ 8660 lu dans /props, total_slots == 1, slot 0 au repos, aucune requête de Loki en vol (compteur tenu par le chat, les résumés, le bench et les proxys). CACHE_ISOLATE=off le coupe. - usage.prompt_tokens_details.cached_tokens : si la conversation revient avec moins de la moitié en cache, conseil (une fois) de relever CACHE_RAM. - Éditeur de preset : champ « Cache de prompts » à côté d'UBATCH, avec la valeur auto calculée par le serveur (/api/preset/cacheram). - Lecteur GGUF : embedding_length, head_count et dimensions ssm.*. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
15495431fc |
Mode Code : le compactage garde les fichiers touchés, les erreurs et les critères
Repris du COMPACTION_PROMPT d'OpenFox. Après un compactage, le builder réécrivait des fichiers déjà faits et retombait dans des erreurs déjà résolues : le résumé, pensé pour le chat, ne gardait ni l'un ni l'autre. Et les critères, qui ne vivent pas dans le fil (seuls les appels à l'outil criteria y passent), disparaissaient avec le torse résumé. - En mode Code, le résumé garde aussi chaque fichier créé ou modifié, les erreurs rencontrées et leur résolution, et les commandes de build/test. - L'état des critères est rendu au builder dans le message de résumé — un message de toute façon neuf, donc sans cache invalidé en plus. (Au passage : gofmt sur code_git.go, mal aligné au commit précédent.) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
8171899ac1 |
Contexte : fini le 400 « dépasse la fenêtre » qui bloquait la discussion
Repris d'AJEAN 0.17.5. Sur une fenêtre de 65k, un tour qui lit plusieurs gros fichiers d'un coup passait de 60 % à plus de 130 % sans jamais compacter, et l'erreur remontait telle quelle. - Le test de compactage en cours de tour compte aussi les résultats d'outils de l'étape, que le moteur n'a pas encore vus. Sans usage côté serveur, l'estimation porte toujours sur tout l'historique. - Tout résultat d'outil est borné à 30 000 caractères dans la vue du modèle, avec une mention qui l'invite à cibler. L'interface garde le résultat complet (« voir plus »). - Dernier recours quand le moteur refuse encore le prompt et que le compactage n'a rien pu faire : shrinkToFit tronque les gros résultats d'outils puis retire les plus vieux échanges, vers 60 % de la fenêtre corrigés par la taille réelle lue dans l'erreur. Deux essais au plus. - Tâches planifiées : la note de tâche passe en tête du message utilisateur au lieu du système. Le préfixe reste celui du chat et le cache de prompt de llama-server sert aux deux (l'amont mesurait 12 s de recalcul pour un « salut » après une tâche). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
199d7f1e99 |
Chat : compactage sans redite, file multi-appareils, bascule par identifiant
Repris d'AJEAN 0.17.4. - Compactage : la dernière demande du torse n'est plus réinjectée quand la queue contient déjà un message utilisateur (le modèle répondait une seconde fois à une vieille question), ni quand le résumé a échoué (elle est déjà dans le torse dégraissé et réapparaissait après ses propres résultats d'outils). - Le texte écrit avant un appel d'outil n'est plus rangé deux fois dans l'historique du modèle (message tool_calls ET réponse finale) : du contexte gaspillé à chaque tour d'outil. - Deux appareils qui envoient en même temps : le second message part en file au lieu d'un 409. Une tâche planifiée garde le refus. - Un raisonnement qui reprend après du texte de réponse ouvre une nouvelle bulle sous la réponse au lieu de remplir l'ancienne, repliée au-dessus. - Bascule de preset par identifiant : le numéro seul visait un autre preset quand la liste avait bougé sur un autre appareil. Le numéro reste accepté. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
2db0ecd431 |
Presets externes : vision, compactage, bench et badge pour une API distante
La reprise faite en parallèle sur l'autre branche (b667fd4, jamais poussée) couvrait des trous de celle-ci. Ses ajouts, posés sur la version en place : - chat_template_kwargs, propre à llama.cpp, ne part plus vers une API distante — ni au chat ni au résumé de compactage. Une API stricte (OpenAI) répond 400 à un argument inconnu : toute compaction échouait. - Vision : case « le modèle accepte les images » (EXTERNAL_VISION) dans la fenêtre API externe. Elle remplace, en externe, MMPROJ et la sonde /props : un modèle distant multimodal ne pouvait jamais recevoir d'image. - Le benchmark refuse de tourner sur un preset externe : healthCheck le dit prêt sans moteur, et la mesure tapait un port arrêté ou un moteur resté en vie, attribuée à tort à ce preset. - Le badge de modèle des réponses porte le nom du modèle distant. - Éditer le preset en service l'applique aussitôt (SavePresetApplying). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
97378da7af |
Agent : trois filets de secours qui faisaient plus de mal que de bien
Repris d'AJEAN 0.15.5.
COMPACTAGE DE SECOURS
Sur n'importe quel refus du moteur — appel d'outil mal formé, modèle en
cours de chargement, erreur de template — runChat résumait ~75 % de la
conversation avant de rejouer, même quand elle tenait en trois messages.
contextOverflow ne laisse passer que les vrais débordements : libellés de
llama.cpp (« exceeds the available context size ») et des API
OpenAI-compatibles (context_length_exceeded), ou, à défaut de libellé, une
conversation déjà à 90 % de la fenêtre.
ARGUMENTS D'OUTIL ILLISIBLES
Un JSON tronqué était neutralisé en {} (indispensable : le template les
re-parse à chaque requête) PUIS exécuté tel quel, d'où des erreurs
trompeuses (« fichier manquant », « commande vide ») qui envoyaient le
modèle sur une fausse piste. L'appel n'est plus exécuté ; le modèle reçoit
une erreur qui dit exactement quoi renvoyer. Des arguments VIDES restent
valides (outil sans paramètre).
RELANCE SANS OUTILS
La consigne imposait le français. Elle demande désormais la langue de
l'utilisateur.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
b5904907e8 |
Reprise réseau, presets externes, see_image
Trois apports repérés chez AJEAN (v0.13.8) et OpenFox (2.0.118), portés et adaptés à Loki. ## Reprise réseau du tour (llm_retry_net.go) La requête de complétion partait une fois : un Do() qui échoue ou un statut d'erreur tuait le tour. Les messages d'erreur le disaient eux-mêmes — « réessaie dans quelques secondes » — autrement dit on demandait à l'utilisateur de refaire à la main ce que le code pouvait faire seul. Une tâche planifiée tombée pendant un redémarrage du moteur échouait pour de bon, sans personne pour recliquer. Trois reprises consécutives, 0,8 → 1,6 → 3,2 s, plafonnées, interruptibles par un /stop. La règle de sûreté ne souffre pas d'exception : on ne rejoue que TANT QU'AUCUN OCTET N'A ÉTÉ DIFFUSÉ, sinon la moitié de la réponse déjà chez l'utilisateur serait dupliquée. Le compteur repart à zéro dès qu'une réponse arrive. 500 n'est pas un statut de reprise : c'est ce que llama.cpp rend pour un appel d'outil malformé ou un prompt trop long, deux échecs déterministes que les filets sémantiques traitent déjà. Restent les codes qui disent « pas maintenant » : 429, 502, 503, 504. ## Presets externes (backend_external.go, web_external.go) Un preset avec EXTERNAL=1 route le chat vers une API OpenAI-compatible distante (OpenAI, Groq, OpenRouter, un vLLM sur une autre machine) au lieu du llama-server local. C'est un preset COMME UN AUTRE : même liste, même bascule, même prompt système par preset. La différence ne vit qu'à deux endroits — l'inférence (resolveChatEndpoint) et la bascule, qui arrête le moteur local au lieu de le redémarrer. La clé du serveur local ne part jamais chez un tiers : chaque endpoint porte la sienne. La clé du preset n'est jamais renvoyée en clair à l'interface, et un champ vide ne l'efface pas — il faut y avoir touché. Une fenêtre dédiée plutôt que l'éditeur habituel : un modèle distant n'a ni quantification, ni couches GPU, ni moteur. Un bouton teste la connexion avant d'enregistrer, et rend le message de l'API plutôt que le JSON brut. Le résumé de compactage part au même endroit que le chat : le laisser taper le moteur local aurait cassé toute compaction sur un preset externe. ## see_image (chat_vision_tool.go) Loki savait voir une pièce jointe et une capture qu'il venait de prendre, mais pas un fichier qui dort sur le disque : « regarde ~/photos/bug.png » n'avait aucune réponse, `read` rendant des octets binaires. L'outil charge l'image et la réinjecte dans un message utilisateur multimodal — même chemin que les pièces jointes. Même règle ÉPHÉMÈRE que les captures (et non celle de l'amont, qui persiste l'image) : l'image va dans le tour en cours, pas dans l'historique. Un base64 persisté repartirait à chaque tour et finirait par dépasser la fenêtre pour de bon. Le marqueur de perte à la compaction existait déjà mais n'offrait qu'un recours, « reprends la capture » — ce qui enverrait photographier une page web alors que l'image perdue est un PNG du disque. Formulation généralisée. ## Au passage toolCallLabel est extrait de runChat. Cette table nom d'outil → argument a une double fonction — libellé affiché ET argument principal — donc un outil absent s'exécute sur une chaîne vide : see_image répondait « chemin de fichier manquant » quoi qu'on lui passe, sans que rien d'autre ne bronche. Une table pareille se teste. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0129sffVC43rezAXUMQuzUog |
||
|
|
17f8fa8f34 |
Mémoire longue (recall), projets et trackers
Trois apports repris d'AJEAN 0.12.9 → 0.13.5, adaptés au fork. MÉMOIRE LONGUE DE LA CONVERSATION (chat_recall.go) Le compactage résumait, donc perdait. Chaque gros bloc du torse est désormais ARCHIVÉ verbatim sous un identifiant court (r7…) dans bbolt AVANT d'être résumé ; le résumé cite l'id, et le modèle le rappelle avec recall(id) ou le retrouve par mots-clés avec recall_search. Le contexte reste plat, l'archive grossit sur disque. En mode code, un fichier lu ou un diff produit tôt dans la session n'est plus perdu au compactage suivant. Au passage : garde-fou anti-résumé-dégénéré, budget de résumé indexé sur la fenêtre, queue ramenée à 20 % (compacter plus large ne coûte plus de perte), et fin de l'épinglage du 1er message user — le modèle répondait à l'ancienne demande au lieu de continuer la tâche en cours. PROJETS (projects.go) Un projet cloisonne une mémoire, ses discussions et ses trackers. La couture est memoryDir(), qui pointe sur le projet actif : tout le code mémoire en hérite sans le savoir. Migration automatique au premier démarrage (memory/*.md → memory/generale/, discussions et tâches orphelines rattachées). Une tâche planifiée vise un projet et l'exécution le force, pour qu'une veille n'écrive pas dans la mémoire du chantier affiché à l'écran. TRACKERS (tracker.go) 3e type de mémoire : les données datées qui s'accumulent. On ne les lit jamais en entier — consultation par niveaux (vue d'ensemble → année → mois → événements), et la dernière valeur de chaque tracker est donnée d'emblée au modèle, qui répond sans appeler l'outil. Aussi : index MEMORY.md tenu par le CODE et injecté en tête de conversation avec la description du projet (le modèle ne peut plus le désynchroniser) ; prompt système rattaché au PRESET et non plus global — stocké en base, pas dans le .env, qui ne saurait pas porter un texte multiligne. Deux correctifs du lot précédent voyagent ici, faute de pouvoir séparer les fichiers : msgText signale la présence d'une image au compactage, et le garde-fou « pensé sans agir » passe à deux relances (nudgeCount/maxNudges). Le paquet UI est regénéré dans le commit suivant, qui touche les mêmes sources. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
de6551a153 |
Rebaptise AJEAN en Loki (fork, lignée conservée)
- module github.com/R0m1k3/Loki, cmd/loki, internal/loki (package loki) - LOKI_HOME, LOKI_MODEL_DIRS, LOKI_SERVICE, LOKI_DL_CONNS ; /etc/loki ; units loki-engine / loki-ui ; binaire et aide CLI - updateRepo pointe sur R0m1k3/Loki (l'auto-update ne tirera plus les binaires AJEAN amont) Conservé à l'identique : le domaine ajean.link (service de tunnel amont), les littéraux de migration 0.7.x (migrate_07.go), RELEASE_NOTES.md et LICENSE (historique et licence de l'amont). go build/vet/test : verts. |