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:
pr-daaif
2026-07-17 17:26:22 +01:00
parent 45ae491827
commit 28eecc1fb9
31 changed files with 1431 additions and 24 deletions

View File

@@ -60,3 +60,14 @@ export const BilanSuggestionsResponseSchema = z.object({
suggestions: z.array(BilanSuggestionSchema),
});
export type BilanSuggestionsResponse = z.infer<typeof BilanSuggestionsResponseSchema>;
// ————— Réindexation du corpus (bibliothèque = corpus, D3) —————
export const ReindexResultSchema = z.object({
documentsIndexed: z.number().int(),
/** Exclus du corpus (interrupteur) ou format non indexable (non-PDF). */
documentsSkipped: z.number().int(),
reportsIndexed: z.number().int(),
chunks: z.number().int(),
});
export type ReindexResult = z.infer<typeof ReindexResultSchema>;

View File

@@ -28,9 +28,18 @@ export const DocumentSchema = z.object({
workOrderReference: z.string().nullable(),
uploadedByName: z.string().nullable(),
createdAt: z.iso.datetime(),
/** R5 (D3) : corpus visible et réversible, document par document. */
inCorpus: z.boolean(),
indexedAt: z.iso.datetime().nullable(), // null = jamais indexé
chunkCount: z.number().int(),
});
export type DocumentDto = z.infer<typeof DocumentSchema>;
export const DocumentCorpusUpdateSchema = z.object({
inCorpus: z.boolean(),
});
export type DocumentCorpusUpdate = z.infer<typeof DocumentCorpusUpdateSchema>;
export const DocumentsResponseSchema = z.object({
documents: z.array(DocumentSchema),
});