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:
@@ -46,6 +46,7 @@ import {
|
||||
} from './schemas/portail';
|
||||
import {
|
||||
AnalyticsSummarySchema,
|
||||
DocumentCorpusUpdateSchema,
|
||||
DocumentSchema,
|
||||
DocumentsResponseSchema,
|
||||
} from './schemas/documents';
|
||||
@@ -54,6 +55,7 @@ import {
|
||||
AssistantAnswerSchema,
|
||||
AssistantAskSchema,
|
||||
BilanSuggestionsResponseSchema,
|
||||
ReindexResultSchema,
|
||||
SuggestBilanSchema,
|
||||
} from './schemas/assistant';
|
||||
import {
|
||||
@@ -491,6 +493,19 @@ export const API_CONTRACT: ApiOperation[] = [
|
||||
404: { description: 'Inconnu' },
|
||||
},
|
||||
},
|
||||
{
|
||||
operationId: 'updateDocumentCorpus',
|
||||
method: 'patch',
|
||||
path: '/documents/{id}/corpus',
|
||||
summary: 'Inclure/exclure du corpus IA (D3 — réversible, effectif à la prochaine réindexation)',
|
||||
tags: ['documents'],
|
||||
pathParams: ['id'],
|
||||
request: { name: 'DocumentCorpusUpdate', schema: DocumentCorpusUpdateSchema },
|
||||
responses: {
|
||||
200: { description: 'Document mis à jour', name: 'Document', schema: DocumentSchema },
|
||||
404: { description: 'Inconnu' },
|
||||
},
|
||||
},
|
||||
{
|
||||
operationId: 'deleteDocument',
|
||||
method: 'delete',
|
||||
@@ -538,6 +553,17 @@ export const API_CONTRACT: ApiOperation[] = [
|
||||
503: { description: 'Service IA indisponible' },
|
||||
},
|
||||
},
|
||||
{
|
||||
operationId: 'reindexAssistant',
|
||||
method: 'post',
|
||||
path: '/assistant/reindex',
|
||||
summary: 'Réindexer le corpus (bibliothèque PDF + bilans clôturés, anonymisés à l’ingestion — D4)',
|
||||
tags: ['assistant'],
|
||||
responses: {
|
||||
200: { description: 'Bilan d’indexation', name: 'ReindexResult', schema: ReindexResultSchema },
|
||||
503: { description: 'Service IA indisponible' },
|
||||
},
|
||||
},
|
||||
{
|
||||
operationId: 'globalSearch',
|
||||
method: 'get',
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user