File size: 7,562 Bytes
3c726a6
f9f640c
3c726a6
 
f9f640c
3c726a6
f9f640c
3c726a6
 
 
 
 
 
 
 
 
 
f9f640c
3c726a6
 
 
f9f640c
3c726a6
 
 
 
 
f9f640c
3c726a6
a5510c5
 
 
3c726a6
 
a5510c5
3c726a6
a5510c5
3c726a6
 
a5510c5
3c726a6
 
a5510c5
3c726a6
 
a5510c5
3c726a6
 
 
 
 
 
 
 
f9f640c
3c726a6
a5510c5
 
3c726a6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a5510c5
 
 
 
3c726a6
 
 
 
a5510c5
3c726a6
a5510c5
3c726a6
 
 
a5510c5
3c726a6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a5510c5
 
 
 
3c726a6
 
 
 
 
a5510c5
 
3c726a6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a5510c5
 
 
 
 
 
3c726a6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# STATUS.md — Sprint 2 : Pipeline page unique

## Sprint : 2 — Session A
## Objectif du sprint : 1 image → 1 master.json valide via Google AI

---

## Ce qui est fait (Sprint 1 ✓)
- [x] Repo GitHub structuré, arborescence complète
- [x] Schémas Pydantic : corpus_profile.py, page_master.py, annotation.py
- [x] 4 profils JSON (medieval-illuminated, medieval-textual, early-modern-print, modern-handwritten)
- [x] 9 templates de prompts versionnés
- [x] 54 tests pytest passants (26 schemas + 28 profiles)
- [x] pyproject.toml configuré
- [x] 6 secrets GitHub en place :
      GOOGLE_AI_STUDIO_API_KEY, VERTEX_API_KEY, VERTEX_PROJECT_ID,
      VERTEX_LOCATION, VERTEX_SERVICE_ACCOUNT_JSON, AI_PROVIDER

---

## Contexte important pour ce sprint

### Providers Google AI disponibles
Trois options configurées, priorité :
- AI_PROVIDER=vertex_api_key → clé AQ.Ab... (Vertex Express, production)
- AI_PROVIDER=google_ai_studio → clé AIza... (gratuit, développement)
- AI_PROVIDER=vertex_service_account → JSON credentials (institutions)

### Format de clé Vertex non confirmé
La clé Vertex commence par AQ.Ab (format OAuth2 Vertex Express).
La syntaxe SDK exacte pour ce format N'EST PAS encore validée.
La Session A commence par ce test — avant tout le reste.

### Images test disponibles
Pas d'images locales. On travaille avec des URLs IIIF directes.

URL Beatus haute résolution (profil medieval-illuminated) :
  https://gallica.bnf.fr/iiif/ark:/12148/btv1b52505441p/f233/full/full/0/native.jpg

URL Beatus basse résolution (même folio, qualité réduite — test confidence) :
  https://gallica.bnf.fr/iiif/ark:/12148/btv1b52505441p/f233/full/600,/0/native.jpg

URL second corpus — Grandes Chroniques de France (profil medieval-textual) :
  https://gallica.bnf.fr/iiif/ark:/12148/btv1b84472995/f16/full/full/0/native.jpg

Pourquoi tester deux résolutions du Beatus :
  Les deux images doivent produire un master.json valide.
  La basse résolution doit retourner un score confidence plus faible
  et potentiellement déclencher le statut needs_review si < flag_below (0.4).
  Cela valide que les seuils du profil fonctionnent correctement.

---

## Session A — Connexion Google AI + listage modèles

### Objectif
Valider que les 3 providers fonctionnent et lister les modèles disponibles.
Aucun traitement d'image. Aucun master.json. Juste la connexion.

### Tâches dans l'ordre

1. Créer backend/app/services/ai/__init__.py (vide)

2. Créer backend/app/services/ai/client.py
   → factory get_ai_client() avec les 3 options
   → Option B (vertex_api_key) : tester les deux syntaxes possibles
     et documenter celle qui fonctionne dans un commentaire

3. Créer backend/app/services/ai/models.py
   → fonction list_available_models(client) → list[dict]
   → filtrer sur les modèles qui supportent generateContent + vision
   → retourner : id, display_name, supports_vision

4. Créer backend/tests/test_ai_connection.py
   → test_option_a_google_ai_studio() : connexion + list_models
   → test_option_b_vertex_api_key() : connexion + list_models
   → test_option_c_vertex_service_account() : connexion + list_models
   → Chaque test affiche les modèles disponibles dans les logs

5. Lancer pytest test_ai_connection.py
   → documenter dans DECISIONS.md la syntaxe exacte validée pour AQ.Ab

### Critère de done Session A
Les 3 tests de connexion passent.
On sait quelle syntaxe fonctionne pour la clé AQ.Ab.
La liste des modèles disponibles est affichée pour chaque provider.

### Ne pas faire en Session A
- Aucun traitement d'image
- Aucun appel de prompt
- Aucune ingestion de corpus

---

## Session B — Ingestion + préparation image

### Objectif
Ingérer une image depuis une URL IIIF et produire un dérivé web prêt pour l'IA.

### Tâches dans l'ordre

1. Créer backend/app/services/ingest/__init__.py
2. Créer backend/app/services/ingest/image_loader.py
   → load_from_url(url) → image bytes + dimensions
   → load_from_file(path) → image bytes + dimensions
   → Pillow pour lire et redimensionner
3. Créer backend/app/services/image/__init__.py
4. Créer backend/app/services/image/processor.py
   → make_derivative(image_bytes, max_size=1500) → JPEG bytes
   → get_dimensions(image_bytes) → (width, height)
5. Tester sur les 3 URLs dans l'ordre :
   - Beatus haute résolution :
     https://gallica.bnf.fr/iiif/ark:/12148/btv1b52505441p/f233/full/full/0/native.jpg
   - Beatus basse résolution :
     https://gallica.bnf.fr/iiif/ark:/12148/btv1b52505441p/f233/full/600,/0/native.jpg
   - Grandes Chroniques :
     https://gallica.bnf.fr/iiif/ark:/12148/btv1b84472995/f16/full/full/0/native.jpg
   → vérifier que les 3 images se téléchargent et se redimensionnent
   → vérifier que les dimensions sont bien extraites pour chaque cas
6. Ajouter tests/test_image_processing.py

### Critère de done Session B
Les 3 URLs produisent chacune un JPEG dérivé de 1500px max.
Les dimensions sont correctement extraites pour chaque image.
La basse résolution Beatus produit bien une image plus petite en entrée.

---

## Session C — Premier appel IA + master.json

### Objectif
1 image → 1 appel Google AI → 1 master.json valide.
C'est le cœur du Sprint 2.

### Tâches dans l'ordre

1. Créer backend/app/services/ai/prompt_loader.py
   → load_and_render(template_path, context_dict) → str
   → remplace {{profile_label}}, {{language_hints}}, {{script_type}}

2. Créer backend/app/services/ai/pipeline.py
   → analyze_page(image_bytes, corpus_profile, model_id) → PageMaster
   → Appelle le prompt primary_v1.txt du profil
   → Stocke ai_raw.json (brut) + master.json (validé Pydantic)
   → Lève une erreur explicite si le JSON retourné est invalide

3. Tester sur les 3 images dans l'ordre :
   a. Beatus haute résolution + profil medieval-illuminated
      → master.json valide, confidence attendue > 0.6
   b. Beatus basse résolution + profil medieval-illuminated
      → master.json valide, confidence attendue plus faible
      → vérifier que editorial.status = "needs_review" si confidence < 0.4
   c. Grandes Chroniques + profil medieval-textual
      → master.json valide, extensions sans iconography
      → valide la généricité (zéro logique Beatus dans le code)

4. Vérifier pour chaque master.json :
   → ai_raw.json bien séparé
   → processing.provider = "vertex_api_key"
   → schema_version = "1.0"
   → bbox toutes en format [x, y, w, h] avec w > 0 et h > 0

5. Ajouter tests/test_pipeline.py

### Critère de done Session C
3 master.json valides produits (Beatus HR + Beatus BR + Grandes Chroniques).
La basse résolution déclenche bien un score de confidence plus faible.
Les Grandes Chroniques ne contiennent pas de bloc iconography dans extensions.
pytest 100% sur tous les fichiers de test.
ai_raw.json et master.json bien séparés dans data/ pour chaque page.

---

## Critère de fin du Sprint 2
- [ ] 3 providers connectés et testés
- [ ] Syntaxe AQ.Ab documentée dans DECISIONS.md
- [ ] Pipeline page unique fonctionnel
- [ ] 3 master.json valides (Beatus HR + Beatus BR + Grandes Chroniques)
- [ ] La basse résolution produit un confidence plus faible que la haute résolution
- [ ] Règle de généricité respectée (zéro logique hardcodée Beatus)
- [ ] pytest 100%

---

## Ne pas faire dans ce sprint
- Aucune API FastAPI
- Aucune interface web
- Aucun ALTO / METS / IIIF
- Aucun traitement en lot
- Passes dérivées (traduction, commentaire) : Sprint 3