mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-12 01:37:06 +02:00
v0.9.3 : statut moteur plus précis + réglages d'interface
- statut : la pastille ne dit plus toujours « modèle incompatible » (prêt / chargement… / erreur / arrêté) ; le détail n'accuse l'incompatibilité que quand le journal le montre (quant/architecture), sinon renvoie au journal - panneau Actions : bouton « refresh » retiré ; mise à jour + exporter sur une ligne, bench + clear chat sur l'autre - modale preset : titre sans le mot « Preset » redondant (juste le nom) - mémoire : « auto (proactive) » → « auto »
This commit is contained in:
1 parent
e3957fa749
commit
d4989f2991
10 files changed
+54
-55
No files matched your search
+7
-13
@@ -1,19 +1,13 @@
|
||||
Les postes distants se pilotent maintenant depuis le chat, et l'envoi de fichiers à travers ajean.link est réparé.
|
||||
Quelques réglages d'interface pour une lecture plus claire, et un statut du moteur enfin honnête.
|
||||
|
||||
## Les postes distants à portée de main
|
||||
## Un statut du moteur plus juste
|
||||
|
||||
Un nouveau bouton apparaît dans la barre de saisie, juste à côté du trombone. Son icône dit **sur quelle machine l'IA agit** : un écran quand c'est ce serveur, des ondes wifi quand c'est un poste distant. Un clic ouvre une **fenêtre dédiée** où tu choisis la cible, vois tes postes appairés (avec leurs capacités et leur dossier autorisé) et en ajoutes de nouveaux.
|
||||
La pastille d'état affichait « modèle incompatible » dès qu'un chargement échouait — même quand la vraie cause était ailleurs. Désormais elle reste courte et neutre (**prêt**, **chargement…**, **erreur**, **arrêté**), et le message détaillé en dessous n'accuse le moteur d'incompatibilité **que lorsque le journal le montre vraiment** (quantification ou architecture non reconnue). Pour tout autre échec, il renvoie simplement au journal du moteur au lieu de donner une fausse piste.
|
||||
|
||||
Plus besoin d'aller fouiller dans les réglages : tout est là, au moment où on en a besoin.
|
||||
## Panneau « Actions » réorganisé
|
||||
|
||||
## Envoi de fichiers réparé sur ajean.link
|
||||
Le bouton « refresh », qui ne servait à rien, est retiré. Les actions sont regroupées deux par deux : **mise à jour** et **exporter** sur une ligne, **bench** et **effacer la conversation** sur l'autre.
|
||||
|
||||
Depuis l'accès distant, joindre un fichier à un message échouait. La cause : l'envoi ne passait pas par le tunnel chiffré de bout en bout, contrairement au téléchargement. C'est corrigé — l'envoi emprunte désormais le même chemin, et un gigaoctet traverse toujours par morceaux, sans que le relais ne voie rien.
|
||||
## Petits nettoyages
|
||||
|
||||
## Installer un poste, en une commande ré-exécutable
|
||||
|
||||
`ajean remote install …` peut maintenant être relancé tel quel : fournir un code d'appairage force toujours le (ré)appairage, et la commande retire proprement un service déjà en place au lieu d'échouer sur un fichier verrouillé. Fini le `logout` manuel entre deux essais.
|
||||
|
||||
## Mise à jour depuis l'interface, même sans root
|
||||
|
||||
Sur un serveur où le service tourne sous un compte non-root, le bouton « mettre à jour » de l'interface redémarre désormais correctement le service (via sudo non interactif) au lieu d'échouer en silence.
|
||||
Le titre de l'éditeur de preset n'affiche plus le mot « Preset » redondant (juste le nom), et le mode mémoire automatique s'appelle simplement « auto ».
|
||||
Binary file not shown.
Binary file not shown.
@@ -3,13 +3,13 @@
|
||||
"FileVersion": {
|
||||
"Major": 0,
|
||||
"Minor": 9,
|
||||
"Patch": 2,
|
||||
"Patch": 3,
|
||||
"Build": 0
|
||||
},
|
||||
"ProductVersion": {
|
||||
"Major": 0,
|
||||
"Minor": 9,
|
||||
"Patch": 2,
|
||||
"Patch": 3,
|
||||
"Build": 0
|
||||
},
|
||||
"FileFlagsMask": "3f",
|
||||
@@ -25,7 +25,7 @@
|
||||
"LegalCopyright": "Copyright (c) 2026 AJEAN contributors. MIT License.",
|
||||
"OriginalFilename": "ajean.exe",
|
||||
"ProductName": "AJEAN",
|
||||
"ProductVersion": "0.9.2",
|
||||
"ProductVersion": "0.9.3",
|
||||
"Comments": "https://github.com/nathaninline/ajean — projet open source (MIT)"
|
||||
},
|
||||
"VarFileInfo": {
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const Version = "0.9.2"
|
||||
const Version = "0.9.3"
|
||||
|
||||
// Main est le vrai main() du binaire (cmd/ajean ne fait que l'appeler).
|
||||
func Main() {
|
||||
|
||||
@@ -1249,7 +1249,7 @@ button:hover{border-color:var(--dim);color:var(--text)}
|
||||
<div class="slist">
|
||||
<label class="switchrow"><span>mode</span>
|
||||
<select id="mem-mode" onchange="setMemMode()" class="sctl">
|
||||
<option value="always">auto (proactive)</option>
|
||||
<option value="always">auto</option>
|
||||
<option value="ondemand">sur demande</option>
|
||||
<option value="off">désactivée</option>
|
||||
</select>
|
||||
@@ -1460,23 +1460,17 @@ button:hover{border-color:var(--dim);color:var(--text)}
|
||||
</details>
|
||||
<details id="upd-details"><summary><span class="stitle">Actions</span></summary>
|
||||
<div class="slist"><div class="rowbody">
|
||||
<!-- Ligne 1 : mise à jour + export du fil. Depuis la 0.8 la conversation vit
|
||||
dans ajean.db (bbolt, verrouillé tant que le service tourne) : sans le
|
||||
bouton exporter, plus personne ne peut relire/archiver ses échanges. -->
|
||||
<div class="row" style="margin:0">
|
||||
<button id="upd-check" onclick="checkUpdate()">↥ Vérifier les mises à jour</button>
|
||||
</div>
|
||||
<div id="upd-msg" class="muted"></div>
|
||||
<!-- Export du fil. Depuis la 0.8 la conversation vit dans ajean.db (bbolt,
|
||||
binaire et verrouillé tant que le service tourne) : sans ce bouton,
|
||||
plus personne ne peut relire ni archiver ses échanges, ce que
|
||||
permettait l'ancien conversation.json. -->
|
||||
<div class="row" style="margin-bottom:0">
|
||||
<button onclick="loadAll()">↻ refresh</button>
|
||||
<button id="btn-bench" onclick="runBenchUI()">bench</button>
|
||||
<button onclick="openExportModal()" title="choisir le format et ce qu'on emporte">⤓ exporter…</button>
|
||||
</div>
|
||||
<!-- « clear chat » est en DERNIER, et seul sur sa ligne : c'est la seule
|
||||
action destructive du panneau, elle ne doit pas se trouver sous le
|
||||
doigt qui visait « refresh ». -->
|
||||
<div id="upd-msg" class="muted"></div>
|
||||
<!-- Ligne 2 : bench + clear chat (seule action destructive, gardée en dernier). -->
|
||||
<div class="row" style="margin-bottom:0">
|
||||
<button id="btn-bench" onclick="runBenchUI()">bench</button>
|
||||
<button onclick="resetChat()">⌫ clear chat</button>
|
||||
</div>
|
||||
</div></div>
|
||||
@@ -2280,9 +2274,12 @@ async function loadStatus(){
|
||||
const el=document.getElementById('status-svc');
|
||||
// Trois états : service coupé (err) · service actif mais modèle pas encore
|
||||
// chargé (loading, llama-server renvoie 503) · modèle prêt (ok).
|
||||
let cls='err', txt=s.state;
|
||||
// Pastille COURTE et honnête. Le détail (cause exacte) va dans #model-err
|
||||
// dessous : la pastille ne doit pas affirmer « incompatible » quand l'échec
|
||||
// peut être tout autre chose.
|
||||
let cls='err', txt='arrêté';
|
||||
if(s.active && s.health){ cls='ok'; txt='prêt'; }
|
||||
else if(s.load_error){ cls='err'; txt='modèle incompatible'; }
|
||||
else if(s.load_error){ cls='err'; txt='erreur'; }
|
||||
else if(s.active){ cls='loading'; txt='chargement…'; }
|
||||
el.className='statuspill '+cls;
|
||||
el.innerHTML='<span class="dot"></span>'+txt;
|
||||
@@ -3020,7 +3017,7 @@ const KINDS = {
|
||||
// skills keep name-as-identity (param 'name').
|
||||
// newLabel / nameHint : « Nouveau Page » et « Nom du preset » dans l'éditeur de
|
||||
// mémoire venaient d'un libellé unique décliné mécaniquement.
|
||||
preset: {label:'Preset', newLabel:'Nouveau preset', nameHint:'Nom du preset',
|
||||
preset: {label:'', newLabel:'Nouveau preset', nameHint:'Nom du preset',
|
||||
param:'id', getUrl:'/api/preset', saveUrl:'/api/preset/save', delUrl:'/api/preset/delete', reload:()=>loadPresets()},
|
||||
mem: {label:'Page', newLabel:'Nouvelle page', nameHint:'Nom de la page',
|
||||
param:'name', getUrl:'/api/mem', saveUrl:'/api/mem/save', delUrl:'/api/mem/delete', reload:()=>loadMem()},
|
||||
@@ -3039,7 +3036,7 @@ async function openItem(kind, key){
|
||||
const K = KINDS[kind];
|
||||
const seq = ++openSeq;
|
||||
editingKind = kind; editingKey = key || '';
|
||||
document.getElementById('modal-title').textContent = key ? (K.label + ' · ' + key) : K.newLabel;
|
||||
document.getElementById('modal-title').textContent = key ? (K.label ? K.label + ' · ' + key : key) : K.newLabel;
|
||||
document.getElementById('m-name').value = key || '';
|
||||
document.getElementById('m-name').placeholder = K.nameHint;
|
||||
document.getElementById('m-content').value = '';
|
||||
@@ -3091,7 +3088,7 @@ async function openItem(kind, key){
|
||||
const d = await r.json();
|
||||
if(seq !== openSeq) return; // une autre ouverture a pris la main
|
||||
const display = d.name || key || '';
|
||||
document.getElementById('modal-title').textContent = key ? (K.label + ' · ' + display) : K.newLabel;
|
||||
document.getElementById('modal-title').textContent = key ? (K.label ? K.label + ' · ' + display : display) : K.newLabel;
|
||||
document.getElementById('m-name').value = display;
|
||||
document.getElementById('m-content').value = d.content || '';
|
||||
if(kind === 'preset'){
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
<div class="slist">
|
||||
<label class="switchrow"><span>mode</span>
|
||||
<select id="mem-mode" onchange="setMemMode()" class="sctl">
|
||||
<option value="always">auto (proactive)</option>
|
||||
<option value="always">auto</option>
|
||||
<option value="ondemand">sur demande</option>
|
||||
<option value="off">désactivée</option>
|
||||
</select>
|
||||
@@ -290,23 +290,17 @@
|
||||
</details>
|
||||
<details id="upd-details"><summary><span class="stitle">Actions</span></summary>
|
||||
<div class="slist"><div class="rowbody">
|
||||
<!-- Ligne 1 : mise à jour + export du fil. Depuis la 0.8 la conversation vit
|
||||
dans ajean.db (bbolt, verrouillé tant que le service tourne) : sans le
|
||||
bouton exporter, plus personne ne peut relire/archiver ses échanges. -->
|
||||
<div class="row" style="margin:0">
|
||||
<button id="upd-check" onclick="checkUpdate()">↥ Vérifier les mises à jour</button>
|
||||
</div>
|
||||
<div id="upd-msg" class="muted"></div>
|
||||
<!-- Export du fil. Depuis la 0.8 la conversation vit dans ajean.db (bbolt,
|
||||
binaire et verrouillé tant que le service tourne) : sans ce bouton,
|
||||
plus personne ne peut relire ni archiver ses échanges, ce que
|
||||
permettait l'ancien conversation.json. -->
|
||||
<div class="row" style="margin-bottom:0">
|
||||
<button onclick="loadAll()">↻ refresh</button>
|
||||
<button id="btn-bench" onclick="runBenchUI()">bench</button>
|
||||
<button onclick="openExportModal()" title="choisir le format et ce qu'on emporte">⤓ exporter…</button>
|
||||
</div>
|
||||
<!-- « clear chat » est en DERNIER, et seul sur sa ligne : c'est la seule
|
||||
action destructive du panneau, elle ne doit pas se trouver sous le
|
||||
doigt qui visait « refresh ». -->
|
||||
<div id="upd-msg" class="muted"></div>
|
||||
<!-- Ligne 2 : bench + clear chat (seule action destructive, gardée en dernier). -->
|
||||
<div class="row" style="margin-bottom:0">
|
||||
<button id="btn-bench" onclick="runBenchUI()">bench</button>
|
||||
<button onclick="resetChat()">⌫ clear chat</button>
|
||||
</div>
|
||||
</div></div>
|
||||
|
||||
@@ -3,9 +3,12 @@ async function loadStatus(){
|
||||
const el=document.getElementById('status-svc');
|
||||
// Trois états : service coupé (err) · service actif mais modèle pas encore
|
||||
// chargé (loading, llama-server renvoie 503) · modèle prêt (ok).
|
||||
let cls='err', txt=s.state;
|
||||
// Pastille COURTE et honnête. Le détail (cause exacte) va dans #model-err
|
||||
// dessous : la pastille ne doit pas affirmer « incompatible » quand l'échec
|
||||
// peut être tout autre chose.
|
||||
let cls='err', txt='arrêté';
|
||||
if(s.active && s.health){ cls='ok'; txt='prêt'; }
|
||||
else if(s.load_error){ cls='err'; txt='modèle incompatible'; }
|
||||
else if(s.load_error){ cls='err'; txt='erreur'; }
|
||||
else if(s.active){ cls='loading'; txt='chargement…'; }
|
||||
el.className='statuspill '+cls;
|
||||
el.innerHTML='<span class="dot"></span>'+txt;
|
||||
|
||||
@@ -74,7 +74,7 @@ const KINDS = {
|
||||
// skills keep name-as-identity (param 'name').
|
||||
// newLabel / nameHint : « Nouveau Page » et « Nom du preset » dans l'éditeur de
|
||||
// mémoire venaient d'un libellé unique décliné mécaniquement.
|
||||
preset: {label:'Preset', newLabel:'Nouveau preset', nameHint:'Nom du preset',
|
||||
preset: {label:'', newLabel:'Nouveau preset', nameHint:'Nom du preset',
|
||||
param:'id', getUrl:'/api/preset', saveUrl:'/api/preset/save', delUrl:'/api/preset/delete', reload:()=>loadPresets()},
|
||||
mem: {label:'Page', newLabel:'Nouvelle page', nameHint:'Nom de la page',
|
||||
param:'name', getUrl:'/api/mem', saveUrl:'/api/mem/save', delUrl:'/api/mem/delete', reload:()=>loadMem()},
|
||||
@@ -93,7 +93,7 @@ async function openItem(kind, key){
|
||||
const K = KINDS[kind];
|
||||
const seq = ++openSeq;
|
||||
editingKind = kind; editingKey = key || '';
|
||||
document.getElementById('modal-title').textContent = key ? (K.label + ' · ' + key) : K.newLabel;
|
||||
document.getElementById('modal-title').textContent = key ? (K.label ? K.label + ' · ' + key : key) : K.newLabel;
|
||||
document.getElementById('m-name').value = key || '';
|
||||
document.getElementById('m-name').placeholder = K.nameHint;
|
||||
document.getElementById('m-content').value = '';
|
||||
@@ -145,7 +145,7 @@ async function openItem(kind, key){
|
||||
const d = await r.json();
|
||||
if(seq !== openSeq) return; // une autre ouverture a pris la main
|
||||
const display = d.name || key || '';
|
||||
document.getElementById('modal-title').textContent = key ? (K.label + ' · ' + display) : K.newLabel;
|
||||
document.getElementById('modal-title').textContent = key ? (K.label ? K.label + ' · ' + display : display) : K.newLabel;
|
||||
document.getElementById('m-name').value = display;
|
||||
document.getElementById('m-content').value = d.content || '';
|
||||
if(kind === 'preset'){
|
||||
|
||||
@@ -102,8 +102,19 @@ func modelLoadError() string {
|
||||
if loaded || reason == "" {
|
||||
return ""
|
||||
}
|
||||
return "Le modèle n'a pas pu être chargé : " + reason +
|
||||
". Il est probablement incompatible avec le moteur sélectionné — choisis un autre backend pour ce modèle (édite le modèle → section Moteur)."
|
||||
// Message PRÉCIS selon la cause : n'affirmer l'incompatibilité que quand le
|
||||
// journal la montre vraiment (quant/architecture). Un échec générique de
|
||||
// chargement peut avoir bien d'autres causes (mémoire, fichier, tenseur) — on
|
||||
// renvoie alors vers le journal plutôt que d'accuser à tort le moteur.
|
||||
switch reason {
|
||||
case "format de quantification non reconnu par ce moteur",
|
||||
"type/architecture de modèle inconnu de ce moteur":
|
||||
return "Modèle incompatible avec le moteur : " + reason +
|
||||
". Choisis un autre backend (édite le modèle → Moteur)."
|
||||
default:
|
||||
return "Le modèle n'a pas pu être chargé (" + reason +
|
||||
"). Ouvre le journal du moteur pour le détail."
|
||||
}
|
||||
}
|
||||
|
||||
// handleServiceLog (GET /api/service/log) : dernières lignes du journal du
|
||||
|
||||
Reference in new issue
Block a user