mirror of
https://github.com/siop-spelev/siop2.git
synced 2026-08-08 12:41:54 +00:00
feat(r5.3): écrans IA — assistant web, corpus administrable, suggestions OT/mobile
- Contrat (76 opérations) : Document expose inCorpus/indexedAt/chunkCount,
PATCH /documents/{id}/corpus (ASSETS.edit), POST /assistant/reindex
(bilan chiffré) ; ci-contract vérifie désormais aussi le client mobile.
- Web : page /assistant (chat sourcé — extraits exacts cités, Ouvrir vers
PDF authentifié ou fiche OT, avertissement permanent ; refus honnête
chiffré avec action utile) ; Bibliothèque = corpus (bandeau 09-08,
statut d'indexation par document, interrupteur d'exclusion PDF,
Réindexer tout) ; fiche OT : « Décrire pour suggérer » (Appliquer =
geste humain, liseré « suggéré » retiré au choix manuel).
- Mobile : chips de suggestion dans la clôture (un appui = un champ
pré-rempli, « réseau requis » hors-ligne — la file R4 n'en dépend pas).
- apps/ai : seuils AI_SEUIL_* configurables par env (CI + calibrage).
- CI e2e : service siop2-ai (embeddeur déterministe, seuils calibrés sur
mesures : match 0,66 vs bruit 0,11) + parcours R5 Playwright (PDF généré
xref valide → réindexation → réponse sourcée → refus → suggestion).
- Vérifié : 16/16 Playwright, 78 tests API, 23 pytest, 17 jest-expo ;
chaîne réelle au vrai modèle ONNX (web 7/7, mobile Expo web 6/6).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1016,3 +1016,91 @@ table {
|
||||
border: 1px solid var(--bordure);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
/* ═══ R5 · Assistant (chat sourcé — maquette-r5, écrans 1-2) ═══ */
|
||||
.chat { display: flex; flex-direction: column; gap: 12px; max-width: 760px; }
|
||||
.msg-q {
|
||||
align-self: flex-end; background: var(--primaire); color: #fff;
|
||||
border-radius: 14px 14px 4px 14px; padding: 10px 14px; max-width: 75%; font-size: 13.5px;
|
||||
}
|
||||
.msg-r {
|
||||
background: var(--surface); border: 1px solid var(--bordure);
|
||||
border-radius: 14px 14px 14px 4px; padding: 12px 14px; max-width: 88%;
|
||||
font-size: 13.5px; display: flex; flex-direction: column; gap: 10px;
|
||||
}
|
||||
.msg-r p b { color: var(--encre); }
|
||||
.cite {
|
||||
display: inline-flex; align-items: center; justify-content: center; min-width: 16px; height: 16px;
|
||||
border-radius: 5px; background: var(--primaire-doux); color: var(--primaire);
|
||||
font-size: 10.5px; font-weight: 800; vertical-align: 2px; margin: 0 1px;
|
||||
}
|
||||
.sources {
|
||||
display: flex; flex-direction: column; gap: 6px;
|
||||
border-top: 1px dashed var(--bordure); padding-top: 10px;
|
||||
}
|
||||
.source {
|
||||
display: flex; gap: 10px; align-items: flex-start; background: var(--surface-2);
|
||||
border-radius: 9px; padding: 8px 10px;
|
||||
}
|
||||
.source .no {
|
||||
flex: none; width: 18px; height: 18px; border-radius: 5px; background: var(--primaire-doux);
|
||||
color: var(--primaire); display: flex; align-items: center; justify-content: center;
|
||||
font-size: 11px; font-weight: 800;
|
||||
}
|
||||
.source b { font-size: 12.5px; }
|
||||
.source .ou { color: var(--encre-2); font-size: 11.5px; }
|
||||
.source .extrait {
|
||||
color: var(--encre-2); font-size: 12px; font-style: italic;
|
||||
border-left: 2px solid var(--safran); padding-left: 8px; margin-top: 3px;
|
||||
}
|
||||
.source .ouvrir {
|
||||
margin-left: auto; color: var(--primaire); font-weight: 700; font-size: 12px; white-space: nowrap;
|
||||
}
|
||||
.avert {
|
||||
display: flex; gap: 8px; align-items: center; background: var(--safran-doux); color: var(--alerte);
|
||||
border-radius: 9px; padding: 8px 10px; font-size: 12px; font-weight: 600;
|
||||
}
|
||||
.saisie-chat { display: flex; gap: 8px; max-width: 760px; }
|
||||
.saisie-chat input {
|
||||
flex: 1; border: 1.5px solid var(--bordure-forte); border-radius: 10px;
|
||||
background: var(--surface); padding: 11px 13px; font: inherit; color: var(--encre);
|
||||
}
|
||||
.refus {
|
||||
background: var(--surface); border: 1.5px dashed var(--bordure-forte); border-radius: 14px;
|
||||
padding: 12px 14px; max-width: 88%; font-size: 13.5px;
|
||||
display: flex; flex-direction: column; gap: 8px;
|
||||
}
|
||||
.refus b { color: var(--encre); }
|
||||
.refus .pourquoi { color: var(--encre-2); font-size: 12.5px; }
|
||||
|
||||
/* ═══ R5 · Suggestion de bilan (écran 3) ═══ */
|
||||
.zone-libre {
|
||||
width: 100%; min-height: 74px; border: 1.5px solid var(--bordure-forte); border-radius: 10px;
|
||||
background: var(--surface); padding: 10px 12px; font: inherit; font-size: 13px; color: var(--encre);
|
||||
}
|
||||
.suggestion {
|
||||
display: flex; gap: 10px; align-items: flex-start; border: 1.5px solid var(--primaire);
|
||||
background: var(--primaire-doux); border-radius: 10px; padding: 10px 12px;
|
||||
}
|
||||
.suggestion .ia { flex: none; font-size: 15px; }
|
||||
.suggestion b { font-size: 13px; }
|
||||
.suggestion .just { color: var(--encre-2); font-size: 12px; }
|
||||
.confiance {
|
||||
margin-left: auto; font-size: 10.5px; font-weight: 800; color: var(--primaire); white-space: nowrap;
|
||||
}
|
||||
.actions-sug { display: flex; gap: 8px; margin-top: 4px; flex-wrap: wrap; }
|
||||
/* Liseré « suggéré » : la valeur vient d'une suggestion APPLIQUÉE par l'humain */
|
||||
.champ-b[data-suggere] { position: relative; }
|
||||
.champ-b[data-suggere] select { border-color: var(--primaire); background: var(--primaire-doux); }
|
||||
.champ-b[data-suggere]::after {
|
||||
content: 'suggéré'; position: absolute; top: 14px; right: 8px; font-size: 9px; font-weight: 800;
|
||||
color: var(--primaire); background: var(--surface); padding: 0 5px; border-radius: 99px;
|
||||
border: 1px solid var(--primaire); pointer-events: none;
|
||||
}
|
||||
|
||||
/* ═══ R5 · Corpus (écran 5) ═══ */
|
||||
.st.ok { color: var(--st-termine); }
|
||||
.st.ok::before { background: var(--st-termine); }
|
||||
.st.exclu { color: var(--encre-3); }
|
||||
.st.exclu::before { background: var(--encre-3); }
|
||||
.interrupteur.corpus[aria-checked='true'] { background: var(--succes); }
|
||||
|
||||
Reference in New Issue
Block a user