Clafoutis / README.md
maribakulj
Merge branch 'main' into codex/fix-deployment-issue-with-hf_token-18yajz
ca901f3 unverified
---
title: Clafoutis MVP
emoji: 📚
sdk: docker
app_port: 7860
short_description: Prototype federated portal for cultural heritage resources.
---
# Clafoutis
Portail universel de recherche IIIF avec lecture dans Mirador et connecteurs extensibles.
## Objectif
Cette application permet de :
- rechercher des objets patrimoniaux dans plusieurs institutions ;
- agréger et normaliser les résultats dans un format commun ;
- afficher une galerie homogène de résultats ;
- ouvrir directement les ressources dans Mirador à partir de leurs manifests IIIF ;
- comparer plusieurs objets dans le même viewer ;
- exposer les fonctions principales via une API REST ;
- prévoir une couche MCP sans duplication de logique métier.
## Vision produit
Le produit est structuré en trois couches :
1. **Découverte** : recherche fédérée multi-sources ;
2. **Lecture** : visualisation et comparaison dans Mirador ;
3. **Interopérabilité** : API interne normalisée et couche MCP optionnelle.
Principe fondamental :
> Mirador est la couche de lecture, jamais la couche de recherche.
## Périmètre MVP
Le MVP doit inclure :
- recherche fédérée sur un petit nombre de sources ;
- schéma de données normalisé ;
- galerie de résultats ;
- ouverture simple et multiple dans Mirador ;
- import manuel d’un manifest IIIF ou d’une URL de notice ;
- description des sources disponibles et de leurs capacités ;
- robustesse aux échecs partiels.
Le MVP n’inclut pas :
- compte utilisateur ;
- annotation persistante ;
- index mondial exhaustif ;
- moissonnage global du web patrimonial ;
- recherche OCR universelle.
## Stack technique
### Frontend
- React
- TypeScript
- Tailwind CSS
- TanStack Query
- Zustand ou Redux Toolkit
- Mirador
### Backend
- Python 3.11+
- FastAPI
- Pydantic
- httpx async
### Déploiement
- Docker
- Hugging Face Spaces
## Architecture du projet
```text
app/
frontend/
src/
components/
pages/
hooks/
store/
lib/
types/
backend/
app/
api/
services/
connectors/
models/
mcp/
utils/
config/
tests/
unit/
integration/
docs/
Dockerfile
docker-compose.yml
README.md
```
## Concepts importants
### Schéma normalisé
Toutes les sources externes sont transformées dans un schéma commun de type `NormalizedItem`.
Le schéma normalisé est le cœur du projet : l’interface ne doit pas dépendre directement des payloads bruts des institutions.
### Connecteurs
Chaque source est branchée via un connecteur indépendant.
Chaque connecteur doit implémenter une interface commune, par exemple :
- `search(query, filters, page, page_size)`
- `get_item(source_id)`
- `resolve_manifest(item_or_url)`
- `healthcheck()`
- `capabilities()`
### Succès partiel
Si une source échoue, les autres doivent quand même répondre.
Le moteur fédéré ne doit jamais échouer globalement à cause d’un seul connecteur.
## Fonctionnalités prévues
- recherche simple multi-sources ;
- filtres par institution, type, langue, période, disponibilité IIIF ;
- galerie homogène ;
- ouverture d’un résultat dans Mirador ;
- comparaison de plusieurs manifests ;
- import manuel d’URL ;
- page Sources avec capacités déclaratives ;
- couche MCP optionnelle.
## Endpoints backend prévus
- `GET /api/health`
- `GET /api/sources`
- `POST /api/search`
- `GET /api/item/{id}`
- `POST /api/resolve-manifest`
- `POST /api/import`
### Heuristiques MVP de `/api/import`
Le connecteur générique `manifest_by_url` applique des heuristiques minimales et explicites :
1. **Manifest direct** : l’URL est considérée comme manifest si son chemin contient `manifest`
(ou se termine par `manifest.json`).
2. **Notice -> manifest** : si l’URL ne ressemble pas à un manifest, le backend tente des suffixes
courants, dans cet ordre :
- `/manifest`
- `/manifest.json`
- `/iiif/manifest`
- `/iiif/manifest.json`
Ces heuristiques sont volontairement simples au MVP et seront enrichies par source aux lots
connecteurs réels.
### Sécurité MVP import URL (validation + SSRF basique)
`/api/import` applique une validation stricte avant résolution :
- schémas autorisés : `http`, `https` uniquement ;
- rejet explicite de `localhost`/hôtes locaux ;
- rejet des IP privées/loopback/link-local/réservées/unspecified ;
- rejet des hôtes DNS qui résolvent vers ces plages privées/locales.
Limite connue MVP : cette protection SSRF reste basique et devra être durcie (allowlist,
résolution DNS contrôlée, protections réseau infra) avant production.
## Outils MCP prévus
- `search_items`
- `get_item`
- `resolve_manifest`
- `open_in_mirador`
- `list_sources`
## Installation locale
### Prérequis
- Python 3.11+
- Node.js 20+ ou version définie dans le projet
- npm, pnpm ou yarn
- Docker (optionnel mais recommandé)
### Backend
```bash
python -m venv .venv
source .venv/bin/activate
pip install -e '.[dev]'
uvicorn app.main:app --app-dir app/backend --reload
```
### Frontend
```bash
cd app/frontend
npm install --legacy-peer-deps
npm run dev
```
Par défaut, le frontend appelle `http://localhost:8000`.
Optionnel :
```bash
VITE_API_BASE_URL=http://localhost:8000 npm run dev
```
## Variables d’environnement
Créer un fichier `.env` à partir de `.env.example`.
Variables backend principales (préfixe `CLAFOUTIS_`) :
```env
CLAFOUTIS_DEBUG=false
CLAFOUTIS_APP_HOST=0.0.0.0
CLAFOUTIS_APP_PORT=7860
CLAFOUTIS_REQUEST_TIMEOUT_SECONDS=8
CLAFOUTIS_CORS_ALLOW_ORIGINS=["http://localhost:5173"]
CLAFOUTIS_SERVE_FRONTEND=true
CLAFOUTIS_FRONTEND_DIST_DIR=app/frontend/dist
CLAFOUTIS_GALLICA_USE_FIXTURES=true
CLAFOUTIS_BODLEIAN_USE_FIXTURES=true
CLAFOUTIS_EUROPEANA_USE_FIXTURES=true
CLAFOUTIS_EUROPEANA_API_KEY=
# Hugging Face Spaces: HF_TOKEN est aussi accepté en fallback pour Europeana
# (utile si votre secret Space est nommé HF_TOKEN).
CLAFOUTIS_ENABLE_CAPABILITY_PROBING=true
CLAFOUTIS_CAPABILITY_PROBE_USE_FIXTURES=true
CLAFOUTIS_CAPABILITY_PROBE_TIMEOUT_SECONDS=2
CLAFOUTIS_CAPABILITY_PROBE_CACHE_TTL_SECONDS=300
```
Pour le frontend en mode dev local (Vite séparé) :
```env
VITE_API_BASE_URL=http://localhost:8000
```
## Packaging Docker (lot démo Hugging Face Spaces)
Stratégie MVP :
- image **unique** ;
- build frontend React dans une étape Node ;
- copie des assets `dist` dans l’image runtime Python ;
- backend FastAPI sert API + assets frontend (SPA fallback) sur un **port unique** ;
- mode fixtures activable par variables d’environnement (par défaut recommandé pour démo).
### Build image
```bash
docker build -t clafoutis-mvp .
```
### Run local (démo)
```bash
docker run --rm -p 7860:7860 \
-e PORT=7860 \
-e CLAFOUTIS_GALLICA_USE_FIXTURES=true \
-e CLAFOUTIS_BODLEIAN_USE_FIXTURES=true \
-e CLAFOUTIS_EUROPEANA_USE_FIXTURES=true \
clafoutis-mvp
```
Puis ouvrir :
- UI : `http://localhost:7860`
- API health : `http://localhost:7860/api/health`
### Hugging Face Spaces (Docker)
1. Créer un Space de type **Docker**.
2. Pousser ce dépôt (avec `Dockerfile`) dans le Space.
3. Définir les variables du Space (Settings -> Variables), au minimum :
- `PORT=7860`
- `CLAFOUTIS_GALLICA_USE_FIXTURES=true`
- `CLAFOUTIS_BODLEIAN_USE_FIXTURES=true`
- `CLAFOUTIS_EUROPEANA_USE_FIXTURES=true`
4. Optionnel : ajouter `CLAFOUTIS_EUROPEANA_API_KEY` pour le mode live Europeana.
Le backend accepte aussi `HF_TOKEN` en fallback si votre secret Space porte ce nom.
Le point d’entrée est `scripts/start.sh`, qui démarre Uvicorn sur `HOST/PORT` compatibles Space Docker.
## Sources prévues pour le MVP
- Gallica / BnF
- Bodleian Digital
- Europeana
- connecteur générique `manifest-by-url`
## Connecteur Gallica (lot 5)
### Hypothèses de mapping `NormalizedItem`
- `source_item_id` : ARK extrait des identifiants Gallica (`ark:/...`) ;
- `id` global : `gallica:{source_item_id}` ;
- `title` : premier champ `dc:title` disponible ;
- `creators` : liste des `dc:creator` ;
- `date_display` : premier `dc:date` ;
- `object_type` : dérivé de `dc:type` via mapping simple (`manuscript`, `book`, `map`, `image`, `newspaper`, `other`) ;
- `record_url` : premier `dc:identifier` ;
- `manifest_url` : construit depuis l’ARK (`https://gallica.bnf.fr/iiif/{ark}/manifest.json`) ;
- `institution` : `Bibliothèque nationale de France`.
### Stratégie de résolution de manifest
1. si `item.manifest_url` est déjà présent, il est renvoyé ;
2. sinon, extraction d’un ARK depuis `record_url` (ou URL fournie) ;
3. construction déterministe de l’URL IIIF manifest Gallica.
### Robustesse / mode fallback
- Le connecteur tente un mode live SRU Gallica ;
- pour éviter de casser la suite en environnement instable, un mode fixtures est disponible (`CLAFOUTIS_GALLICA_USE_FIXTURES=true` au MVP, valeur par défaut) ;
- en cas d’échec live, le connecteur renvoie un succès dégradé avec données fixtures et `partial_failures` explicite.
### Limites connues (MVP)
- le parsing SRU est volontairement minimal et basé sur un sous-ensemble Dublin Core ;
- certains champs Gallica restent absents/incertains selon les notices ;
- la détection fine des types documentaires sera améliorée aux lots suivants.
## Connecteurs Bodleian et Europeana (lot 6)
### Bodleian — mapping `NormalizedItem`
- `source_item_id` : identifiant objet Bodleian ;
- `id` global : `bodleian:{source_item_id}` ;
- `title`, `creators`, `date_display` : extraits du payload source ou fixtures ;
- `record_url` : URL notice Bodleian (`/objects/{id}/`) ;
- `manifest_url` : prioritairement fourni, sinon construit via pattern
`https://iiif.bodleian.ox.ac.uk/iiif/manifest/{id}.json` ;
- `institution` : `Bodleian Libraries`.
### Europeana — mapping `NormalizedItem`
- `source_item_id` : `id` Europeana ;
- `id` global : `europeana:{source_item_id}` ;
- `title`, `creators`, `date_display` : extraits du payload source ou fixtures ;
- `record_url` : `guid` Europeana (ou URL item fixture) ;
- `manifest_url` :
- `edmIsShownBy` si manifest explicite ;
- sinon fallback pattern `https://iiif.europeana.eu/presentation/{item_path}/manifest` ;
- `institution` : `Europeana` ou institution partenaire fixture.
### Capacités et mode robustesse
- les deux connecteurs implémentent `search`, `get_item`, `resolve_manifest`, `capabilities`, `healthcheck` ;
- stratégie **fixture-first** activée par défaut au MVP (`CLAFOUTIS_BODLEIAN_USE_FIXTURES=true`, `CLAFOUTIS_EUROPEANA_USE_FIXTURES=true`) pour préserver la stabilité ;
- en mode live, les erreurs réseau/API retombent en mode dégradé avec `partial_failures` explicites ;
- Europeana live nécessite une clé API (`CLAFOUTIS_EUROPEANA_API_KEY`).
### Limites connues (MVP)
- endpoints live Bodleian/Europeana restent best-effort et peuvent évoluer ;
- mapping conservateur pour limiter les faux positifs ;
- enrichissement sémantique (types, droits, OCR, langue fine) prévu après lot 6.
## Principes de développement
- code modulaire ;
- typage strict ;
- séparation claire frontend / backend / connecteurs ;
- aucune duplication de logique métier entre REST et MCP ;
- Mirador utilisé uniquement comme couche de lecture ;
- tests unitaires minimum sur les connecteurs ;
- tests d’intégration minimum sur les endpoints critiques.
## Ordre de développement recommandé
1. socle backend ;
2. socle frontend ;
3. intégration Mirador minimale ;
4. import manuel ;
5. connecteurs réels ;
6. ranking, cache et robustesse ;
7. couche MCP ;
8. déploiement Docker / Hugging Face Spaces.
## État du projet
Statut actuel : en cours de développement.
## Priorité actuelle
- [ ] Backend socle
- [ ] Frontend socle
- [ ] Intégration Mirador
- [ ] Import manuel
- [ ] Connecteur Gallica
- [ ] Connecteurs Bodleian et Europeana
- [ ] MCP
- [ ] Docker / déploiement HF Spaces
## Documentation
Voir aussi :
- `AGENTS.md`
- `PLANS.md`
- `docs/specs.md`
## Contribution
Avant toute contribution :
- lire `AGENTS.md` ;
- respecter l’architecture du projet ;
- ne pas dupliquer la logique métier ;
- garder les connecteurs isolés ;
- documenter clairement tout nouveau module.