Files
Alfredv2/alfred/app/static/js/dona-learning-v9-3.js
T
2026-10-31 11:04:45 +01:00

83 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* DONA_LEARNING_V93 : feedback explicite, corrections modérées, aucun entraînement implicite. */
(()=>{
'use strict';
if(window.DonaLearningV93)return;
const el=(tag,cls,txt)=>{const e=document.createElement(tag);if(cls)e.className=cls;if(txt!==undefined)e.textContent=String(txt);return e;};
const btn=(label,click,cls='dl93-button')=>{const b=el('button',cls,label);b.type='button';b.addEventListener('click',click);return b;};
const field=(label,input)=>{const x=el('label','dl93-field');x.append(el('span','',label),input);return x;};
const txt=(text,n=120)=>{const s=String(text||'');return s.length>n?s.slice(0,n)+'…':s;};
const api=async(url,body)=>{const o=body===undefined?{cache:'no-store',credentials:'same-origin'}:{method:'POST',headers:{'Content-Type':'application/json','X-Alfred-Request':'1'},credentials:'same-origin',body:JSON.stringify(body)};const r=await fetch(url,o);const data=await r.json().catch(()=>({}));if(!r.ok||!data.ok)throw Error(data.error||'Erreur HTTP '+r.status);return data;};
let last=null,dock,panel,dialog,menuTick=0,socketBound=false,submitting=false;
function notify(message,bad=false){const n=document.getElementById('dl93Status');if(n){n.textContent=message||'';n.classList.toggle('dl93-error',bad);}}
function showDock(){if(!last||!last.token)return;dock.hidden=false;dock.querySelector('.dl93-context').textContent=txt(last.heard||'Dernière demande',90);}
function setTurn(d){if(!d||!d.token)return;last={token:d.token,heard:String(d.heard||''),answer:String(d.answer||''),source:d.source||'voice'};showDock();notify('');}
function labelBtn(name,fn){return btn(name,fn,'dl93-tool');}
function createDock(){dock=el('div','dl93-dock');dock.id='dl93Dock';dock.hidden=true;dock.setAttribute('role','region');dock.setAttribute('aria-label','Évaluer la dernière réponse de Dona');
dock.append(el('span','dl93-context','Dernière réponse de Dona'),labelBtn('👍',()=>rateUp()),labelBtn('👎',()=>openCorrection()),labelBtn('Historique',()=>openPanel()));
const close=labelBtn('×',()=>{dock.hidden=true;});close.title='Masquer les évaluations';close.setAttribute('aria-label','Masquer les évaluations');dock.append(close);document.body.append(dock);
}
async function rateUp(){if(!last||submitting)return;submitting=true;try{await api('/api/dona/v93/learning/feedback',{token:last.token,rating:'up'});dock.hidden=true;last=null;openToast('👍 Réponse enregistrée.');}catch(e){openToast(e.message,true);}finally{submitting=false;}}
function openToast(message,bad=false){let box=document.getElementById('dl93Toast');if(!box){box=el('div','dl93-toast');box.id='dl93Toast';document.body.append(box);}box.textContent=message;box.classList.toggle('dl93-error',bad);box.hidden=false;setTimeout(()=>{box.hidden=true;},5500);}
function makeDialog(){dialog=el('div','dl93-overlay');dialog.id='dl93Correction';dialog.hidden=true;dialog.setAttribute('role','dialog');dialog.setAttribute('aria-modal','true');dialog.setAttribute('aria-label','Corriger une réponse Dona');
const sheet=el('div','dl93-sheet');
const h=el('div','dl93-top');h.append(el('h3','', 'Corriger Dona'),btn('×',()=>closeCorrection(),'dl93-close'));sheet.append(h);
const summary=el('div','dl93-summary');summary.id='dl93Summary';sheet.append(summary);
const kind=document.createElement('select');kind.id='dl93Kind';[['transcription','Vosk a mal transcrit'],['comprehension','Hermes a mal compris'],['execution','Mauvaise action / erreur d’outil']].forEach(([v,l])=>{const opt=el('option','',l);opt.value=v;kind.append(opt);});
sheet.append(field('Type d’erreur',kind));
const correction=document.createElement('textarea');correction.id='dl93CorrectionText';correction.maxLength=1800;correction.rows=4;
sheet.append(field('Ce que j’ai dit / ce qui était attendu',correction));
const explanation=document.createElement('textarea');explanation.id='dl93Note';explanation.rows=2;explanation.maxLength=800;explanation.placeholder='Précision facultative';sheet.append(field('Commentaire',explanation));
const note=el('p','dl93-hint','La correction restera en attente dans Apprentissage. Après validation : Vosk utilisera la correction pour la même phrase exacte ; Hermes recevra une indication pour cette demande précise. Aucune commande ne sera relancée automatiquement.');sheet.append(note);
kind.addEventListener('change',()=>{correction.placeholder=kind.value==='transcription'?'Écris exactement les mots que tu avais prononcés…':kind.value==='comprehension'?'Explique ce que Dona devait comprendre / faire…':'Indique la bonne action ou l’équipement visé…';note.textContent=kind.value==='execution'?'Ce signalement sera conservé dans l’historique. Aucune règle d’exécution ne sera appliquée automatiquement.':'La correction restera en attente jusqu’à sa validation dans Apprentissage. Seules les formulations identiques seront corrigées.';});
const actions=el('div','dl93-actions');actions.append(btn('Annuler',closeCorrection,'dl93-button dl93-neutral'),btn('Enregistrer 👎',submitBad));sheet.append(actions);
const state=el('p','dl93-state');state.id='dl93CorrectionStatus';state.setAttribute('role','status');sheet.append(state);
dialog.append(sheet);document.body.append(dialog);dialog.addEventListener('pointerdown',e=>{if(e.target===dialog)closeCorrection();});
}
function openCorrection(){if(!last){openToast('Aucune réponse récente.');return;}dialog.hidden=false;const raw=last.heard||'Transcription indisponible';document.getElementById('dl93Summary').replaceChildren(el('p','','Dona a entendu : '+txt(raw,500)),el('p','','Réponse : '+txt(last.answer,500)));const kind=document.getElementById('dl93Kind');kind.value='transcription';const c=document.getElementById('dl93CorrectionText');c.value='';c.placeholder='Écris exactement les mots que tu avais prononcés…';document.getElementById('dl93Note').value='';document.getElementById('dl93CorrectionStatus').textContent='';kind.dispatchEvent(new Event('change'));c.focus();}
function closeCorrection(){if(dialog)dialog.hidden=true;}
async function submitBad(){if(!last||submitting)return;const kind=document.getElementById('dl93Kind').value;const correction=document.getElementById('dl93CorrectionText').value;const note=document.getElementById('dl93Note').value;const error=document.getElementById('dl93CorrectionStatus');submitting=true;
try{await api('/api/dona/v93/learning/feedback',{token:last.token,rating:'down',kind,correction,note});closeCorrection();last=null;dock.hidden=true;openToast('👎 Correction enregistrée. Valide-la dans Apprentissage pour l’activer.');}
catch(e){error.textContent=e.message;error.classList.add('dl93-error');}
finally{submitting=false;}}
function makePanel(){panel=el('div','dl93-overlay');panel.id='dl93Panel';panel.hidden=true;panel.setAttribute('role','dialog');panel.setAttribute('aria-modal','true');panel.setAttribute('aria-label','Historique des corrections Dona');
const sheet=el('div','dl93-sheet dl93-history');const top=el('div','dl93-top');top.append(el('h3','','Apprentissage de Dona'),btn('×',closePanel,'dl93-close'));sheet.append(top);
const intro=el('p','dl93-hint','Historique local des réponses évaluées. Aucun son enregistré. Les règles doivent être approuvées manuellement et peuvent être retirées à tout moment.');sheet.append(intro);
const content=el('div','dl93-panel-body');content.id='dl93PanelContent';sheet.append(content);
const foot=el('div','dl93-actions');foot.append(btn('Actualiser',loadPanel,'dl93-button dl93-neutral'),btn('Fermer',closePanel));sheet.append(foot);
const state=el('p','dl93-state');state.id='dl93Status';state.setAttribute('role','status');sheet.append(state);panel.append(sheet);document.body.append(panel);panel.addEventListener('pointerdown',e=>{if(e.target===panel)closePanel();});
}
async function openPanel(){panel.hidden=false;await loadPanel();}
function closePanel(){if(panel)panel.hidden=true;}
function prettyKind(k){return({transcription:'Vosk / transcription',comprehension:'Compréhension Hermes',execution:'Exécution'})[k]||'Avis';}
function chip(s){const v={approved:'Règle active',pending:'À valider',recorded:'Enregistré',rejected:'Rejeté',revoked:'Règle retirée'};return el('span','dl93-chip',v[s]||s);}
function itemRow(item,content){const card=el('article','dl93-record');const top=el('div','dl93-record-head');top.append(el('strong','',item.rating==='up'?'👍 Correct':'👎 '+prettyKind(item.kind)),chip(item.status));card.append(top);
card.append(el('p','dl93-small','Entendu : '+txt(item.heard,260)));if(item.answer)card.append(el('p','dl93-small','Réponse : '+txt(item.answer,180)));
if(item.correction)card.append(el('p','dl93-small','Correction : '+txt(item.correction,260)));
if(item.note)card.append(el('p','dl93-small','Note : '+txt(item.note,180)));
if(item.status==='pending'){
const actions=el('div','dl93-actions');
actions.append(btn('✓ Valider la règle',async()=>{if(!confirm('Appliquer cette correction aux prochaines demandes identiques ?'))return;await change('/api/dona/v93/learning/'+item.id+'/approve');},'dl93-button'));
actions.append(btn('Rejeter',async()=>{await change('/api/dona/v93/learning/'+item.id+'/reject');},'dl93-button dl93-neutral'));card.append(actions);
}
const remove=btn('Supprimer cette évaluation',async()=>{if(confirm('Supprimer cette évaluation et sa règle éventuelle ?'))await change('/api/dona/v93/learning/'+item.id+'/delete');},'dl93-button dl93-neutral');card.append(remove);
content.append(card);}
async function change(url){try{await api(url,{});await loadPanel();}catch(e){notify(e.message,true);}}
async function loadPanel(){const content=document.getElementById('dl93PanelContent');if(!content)return;content.replaceChildren(el('p','','Chargement…'));notify('');try{
const data=await api('/api/dona/v93/learning');content.replaceChildren();content.append(el('h4','','Évaluations : '+data.stats.positive+' 👍 · '+data.stats.negative+' 👎'));
if(data.rules.length){const rules=el('section','dl93-rules');rules.append(el('h4','','Règles actives ('+data.rules.length+')'));
for(const r of data.rules){const row=el('div','dl93-rule');row.append(el('span','',prettyKind(r.kind)+' : '+txt(r.original,90)+' → '+txt(r.replacement,90)),btn('Retirer',async()=>{if(confirm('Supprimer cette règle ?'))await change('/api/dona/v93/learning/rules/'+r.id+'/remove');},'dl93-button dl93-neutral'));rules.append(row);}content.append(rules);}
if(!data.history.length)content.append(el('p','dl93-small','Aucune réponse évaluée pour le moment.'));
data.history.forEach(f=>itemRow(f,content));
}catch(e){content.replaceChildren(el('p','dl93-error',e.message));}}
function attachMenu(){const m=document.getElementById('dv92ToolsMenu');if(!m||m.hidden||m.querySelector('#dl93MenuEntry'))return;const b=btn('🎓 Apprentissage et corrections',()=>{m.hidden=true;document.getElementById('dv92Hamburger')?.setAttribute('aria-expanded','false');openPanel();},'dv92-menu-entry');b.id='dl93MenuEntry';m.append(b);}
function init(){if(document.getElementById('dl93Dock'))return;createDock();makeDialog();makePanel();document.addEventListener('click',e=>{if(e.target.closest('#dv92Hamburger'))window.setTimeout(attachMenu,10);});document.addEventListener('keydown',e=>{if(e.key==='Escape'){closeCorrection();closePanel();}});
const waitForSocket=()=>{const sock=window.alfredSocket;if(!sock?.on){if(++menuTick<80)setTimeout(waitForSocket,150);return;}if(socketBound)return;socketBound=true;
sock.on('dona_learning_turn',setTurn);
sock.on('dona_learning_stt',data=>{const n=document.getElementById('dl93Transcript');if(n){n.textContent='Vosk : '+data.raw+' → Correction : '+data.used;n.hidden=false;}});
sock.on('alfred_state',state=>{if(state?.mode==='thinking'){if(dock)dock.hidden=true;if(document.getElementById('dl93Transcript'))document.getElementById('dl93Transcript').hidden=true;}});
};waitForSocket();
const t=el('div','dl93-transcript');t.id='dl93Transcript';t.hidden=true;document.body.append(t);
window.DonaLearningV93={openPanel,openCorrection};}
if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',init,{once:true});else init();
})();