feat(r5.1): socle apps/ai — ingestion anonymisée + recherche sémantique

ADR-004 : embeddings locaux sur CPU (fastembed ONNX,
paraphrase-multilingual-MiniLM-L12-v2, 384 dims — les textes du client
ne quittent jamais le serveur), pgvector dans le Postgres existant
(RagChunk possédé par Prisma, migration r5_ia + état de corpus sur
Document), génération opt-in (mode extractif par défaut : la recette
passe sans clé API), service siop2-ai jamais exposé — joint par l'API
NestJS seule (X-Service-Token).

apps/ai (FastAPI + uv) : pipeline PDF MinIO → texte paginé (pypdf) →
anonymisation D4 (e-mails, téléphones marocains, noms connus de la
base, insensible casse/accents — fonction pure testée) → découpage
avec chevauchement (testé) → embeddings → RagChunk localisé (« p. 42 »,
« bilan du 17/07 »). Bilans codés clôturés ingérés. Exclusion de
corpus (D3) appliquée à l'ingestion ET à la lecture.

14 pytest + ruff, embeddeur déterministe en CI (aucun téléchargement),
job CI ai (uv), deploy en dépend. Vérifié en réel avec le vrai modèle :
corpus seedé réindexé en 7 s (PDF réel → 31 extraits paginés + 3
bilans), recherche sémantique concluante, e-mails → ⟨contact⟩,
0 identité dans les chunks (contrôle SQL).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
pr-daaif
2026-07-17 12:22:15 +01:00
parent 199fce69d0
commit 837dcba1db
22 changed files with 2564 additions and 4 deletions

40
apps/ai/pyproject.toml Normal file
View File

@@ -0,0 +1,40 @@
[project]
name = "siop-ai"
version = "0.1.0"
description = "SIOP V2 — service IA (R5) : ingestion anonymisée, recherche sémantique, assistant sourcé (ADR-004)"
requires-python = ">=3.11"
dependencies = [
"fastapi>=0.115",
"uvicorn[standard]>=0.32",
"pydantic-settings>=2.6",
"asyncpg>=0.30",
"pypdf>=5.1",
"minio>=7.2",
]
[project.optional-dependencies]
# Le vrai modèle (ONNX, CPU) — absent des tests/CI (embeddeur déterministe).
embeddings = ["fastembed>=0.4"]
[dependency-groups]
dev = [
"pytest>=8.3",
"pytest-asyncio>=0.24",
"httpx>=0.27",
"ruff>=0.8",
]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/siop_ai"]
[tool.pytest.ini_options]
testpaths = ["tests"]
asyncio_mode = "auto"
[tool.ruff]
line-length = 100
src = ["src", "tests"]