Claude commited on
Commit
e0fd571
·
unverified ·
1 Parent(s): 0209857

rename: Scriptorium AI → IIIF Studio across entire codebase

Browse files

Replace all variants (Scriptorium AI, scriptorium-ai, ScriptoriumAI,
scriptorium.db, scriptoriumProcessing) with their IIIF Studio equivalents
(IIIF Studio, iiif-studio, IIIFStudio, iiif_studio.db, iiifStudioProcessing)
across 21 files. All 477 tests pass.

https://claude.ai/code/session_01WWohTtw2CxGRawmpH1tyrY

.github/workflows/deploy-hf.yml CHANGED
@@ -35,5 +35,5 @@ jobs:
35
  run: |
36
  git config user.email "github-actions[bot]@users.noreply.github.com"
37
  git config user.name "github-actions[bot]"
38
- git remote add hf https://Ma-Ri-Ba-Ku:${HF_TOKEN}@huggingface.co/spaces/Ma-Ri-Ba-Ku/scriptorium-ai
39
  git push hf main --force
 
35
  run: |
36
  git config user.email "github-actions[bot]@users.noreply.github.com"
37
  git config user.name "github-actions[bot]"
38
+ git remote add hf https://Ma-Ri-Ba-Ku:${HF_TOKEN}@huggingface.co/spaces/Ma-Ri-Ba-Ku/iiif-studio
39
  git push hf main --force
.huggingface/README.md CHANGED
@@ -1,5 +1,5 @@
1
  ---
2
- title: Scriptorium AI
3
  emoji: 📜
4
  colorFrom: blue
5
  colorTo: yellow
 
1
  ---
2
+ title: IIIF Studio
3
  emoji: 📜
4
  colorFrom: blue
5
  colorTo: yellow
CLAUDE.md CHANGED
@@ -1,11 +1,11 @@
1
- # Scriptorium AI — Instructions permanentes pour Claude Code
2
  ## Version 2.0 — mise à jour Sprint 2
3
 
4
  ---
5
 
6
  ## 1. Contexte du projet
7
 
8
- Scriptorium AI est une **plateforme générique** de génération d'éditions savantes augmentées
9
  pour documents patrimoniaux numérisés : manuscrits médiévaux, incunables, cartulaires,
10
  archives, chartes, papyri — tout type de document, toute époque, toute langue.
11
 
@@ -38,7 +38,7 @@ latin carolingien, XIe siècle). Le Beatus est un profil parmi d'autres — pas
38
 
39
  ```toml
40
  [project]
41
- name = "scriptorium-ai"
42
  version = "0.1.0"
43
  requires-python = ">=3.11"
44
 
@@ -73,7 +73,7 @@ asyncio_mode = "auto"
73
  ## 3. Arborescence du repo — structure canonique
74
 
75
  ```
76
- scriptorium-ai/
77
 
78
  ├── CLAUDE.md ← CE FICHIER — ne pas modifier sans instruction
79
  ├── STATUS.md ← état courant (mis à jour avant chaque session)
 
1
+ # IIIF Studio — Instructions permanentes pour Claude Code
2
  ## Version 2.0 — mise à jour Sprint 2
3
 
4
  ---
5
 
6
  ## 1. Contexte du projet
7
 
8
+ IIIF Studio est une **plateforme générique** de génération d'éditions savantes augmentées
9
  pour documents patrimoniaux numérisés : manuscrits médiévaux, incunables, cartulaires,
10
  archives, chartes, papyri — tout type de document, toute époque, toute langue.
11
 
 
38
 
39
  ```toml
40
  [project]
41
+ name = "iiif-studio"
42
  version = "0.1.0"
43
  requires-python = ">=3.11"
44
 
 
73
  ## 3. Arborescence du repo — structure canonique
74
 
75
  ```
76
+ iiif-studio/
77
 
78
  ├── CLAUDE.md ← CE FICHIER — ne pas modifier sans instruction
79
  ├── STATUS.md ← état courant (mis à jour avant chaque session)
Dockerfile CHANGED
@@ -1,9 +1,9 @@
1
- # Scriptorium AI — image de production (multi-stage)
2
  # Ce fichier est utilisé par HuggingFace Spaces (SDK docker, détection automatique).
3
  # Source unique — le fichier infra/Dockerfile a été supprimé pour éviter la divergence.
4
  #
5
  # Build depuis la racine du dépôt :
6
- # docker build -t scriptorium-ai .
7
 
8
  # ── Stage 1 : build du frontend React ────────────────────────────────────────
9
  FROM node:20-slim AS frontend-builder
 
1
+ # IIIF Studio — image de production (multi-stage)
2
  # Ce fichier est utilisé par HuggingFace Spaces (SDK docker, détection automatique).
3
  # Source unique — le fichier infra/Dockerfile a été supprimé pour éviter la divergence.
4
  #
5
  # Build depuis la racine du dépôt :
6
+ # docker build -t iiif-studio .
7
 
8
  # ── Stage 1 : build du frontend React ────────────────────────────────────────
9
  FROM node:20-slim AS frontend-builder
README.md CHANGED
@@ -1,5 +1,5 @@
1
  ---
2
- title: Scriptorium AI
3
  emoji: 📜
4
  colorFrom: blue
5
  colorTo: yellow
@@ -8,7 +8,7 @@ app_port: 7860
8
  pinned: false
9
  ---
10
 
11
- # Scriptorium AI
12
 
13
  Plateforme générique de génération d'éditions savantes augmentées pour documents
14
  patrimoniaux numérisés : manuscrits médiévaux, incunables, cartulaires, archives,
@@ -19,7 +19,7 @@ chartes, papyri — tout type de document, toute époque, toute langue.
19
  ## Structure du dépôt
20
 
21
  ```
22
- scriptorium-ai/
23
  ├── backend/ # API FastAPI + pipeline Python
24
  │ ├── app/
25
  │ │ ├── api/v1/ # endpoints REST (/api/v1/...)
@@ -41,7 +41,7 @@ scriptorium-ai/
41
 
42
  ```bash
43
  # 1. Cloner le dépôt
44
- git clone https://github.com/<org>/scriptorium-ai && cd scriptorium-ai
45
 
46
  # 2. Définir les variables d'environnement
47
  cp .env.example .env # puis renseigner les clés dans .env
 
1
  ---
2
+ title: IIIF Studio
3
  emoji: 📜
4
  colorFrom: blue
5
  colorTo: yellow
 
8
  pinned: false
9
  ---
10
 
11
+ # IIIF Studio
12
 
13
  Plateforme générique de génération d'éditions savantes augmentées pour documents
14
  patrimoniaux numérisés : manuscrits médiévaux, incunables, cartulaires, archives,
 
19
  ## Structure du dépôt
20
 
21
  ```
22
+ iiif-studio/
23
  ├── backend/ # API FastAPI + pipeline Python
24
  │ ├── app/
25
  │ │ ├── api/v1/ # endpoints REST (/api/v1/...)
 
41
 
42
  ```bash
43
  # 1. Cloner le dépôt
44
+ git clone https://github.com/<org>/iiif-studio && cd iiif-studio
45
 
46
  # 2. Définir les variables d'environnement
47
  cp .env.example .env # puis renseigner les clés dans .env
backend/app/api/v1/ingest.py CHANGED
@@ -159,8 +159,8 @@ async def _create_page(
159
 
160
  _MANIFEST_HEADERS = {
161
  "User-Agent": (
162
- "Mozilla/5.0 (compatible; ScriptoriumAI/1.0; "
163
- "+https://huggingface.co/spaces/Ma-Ri-Ba-Ku/scriptorium-ai)"
164
  ),
165
  "Accept": "application/ld+json,application/json,*/*",
166
  "Referer": "https://gallica.bnf.fr/",
 
159
 
160
  _MANIFEST_HEADERS = {
161
  "User-Agent": (
162
+ "Mozilla/5.0 (compatible; IIIFStudio/1.0; "
163
+ "+https://huggingface.co/spaces/Ma-Ri-Ba-Ku/iiif-studio)"
164
  ),
165
  "Accept": "application/ld+json,application/json,*/*",
166
  "Referer": "https://gallica.bnf.fr/",
backend/app/config.py CHANGED
@@ -41,7 +41,7 @@ class Settings(BaseSettings):
41
  prompts_dir: Path = _REPO_ROOT / "prompts"
42
 
43
  # ── Base de données ───────────────────────────────────────────────────────
44
- database_url: str = "sqlite+aiosqlite:///./scriptorium.db"
45
 
46
  # ── Fournisseurs IA (R06 — clés depuis l'environnement uniquement) ────────
47
  # Chaque clé est optionnelle. Le backend détecte automatiquement quels
 
41
  prompts_dir: Path = _REPO_ROOT / "prompts"
42
 
43
  # ── Base de données ───────────────────────────────────────────────────────
44
+ database_url: str = "sqlite+aiosqlite:///./iiif_studio.db"
45
 
46
  # ── Fournisseurs IA (R06 — clés depuis l'environnement uniquement) ────────
47
  # Chaque clé est optionnelle. Le backend détecte automatiquement quels
backend/app/main.py CHANGED
@@ -1,5 +1,5 @@
1
  """
2
- Application FastAPI — point d'entrée de Scriptorium AI.
3
 
4
  Tous les endpoints sont sous /api/v1/ (R10).
5
  CORS ouvert pour le développement local (origins=["*"]).
@@ -32,7 +32,7 @@ async def lifespan(application: FastAPI):
32
  # Ces logs apparaissent dans la console HuggingFace et permettent de
33
  # diagnostiquer instantanément tout problème de chemin en production.
34
  logger.info(
35
- "Démarrage Scriptorium AI — chemins configurés",
36
  extra={
37
  "profiles_dir": str(settings.profiles_dir),
38
  "profiles_dir_ok": settings.profiles_dir.is_dir(),
@@ -59,7 +59,7 @@ async def lifespan(application: FastAPI):
59
 
60
 
61
  app = FastAPI(
62
- title="Scriptorium AI",
63
  description="Plateforme générique de génération d'éditions savantes augmentées",
64
  version="0.1.0",
65
  lifespan=lifespan,
 
1
  """
2
+ Application FastAPI — point d'entrée de IIIF Studio.
3
 
4
  Tous les endpoints sont sous /api/v1/ (R10).
5
  CORS ouvert pour le développement local (origins=["*"]).
 
32
  # Ces logs apparaissent dans la console HuggingFace et permettent de
33
  # diagnostiquer instantanément tout problème de chemin en production.
34
  logger.info(
35
+ "Démarrage IIIF Studio — chemins configurés",
36
  extra={
37
  "profiles_dir": str(settings.profiles_dir),
38
  "profiles_dir_ok": settings.profiles_dir.is_dir(),
 
59
 
60
 
61
  app = FastAPI(
62
+ title="IIIF Studio",
63
  description="Plateforme générique de génération d'éditions savantes augmentées",
64
  version="0.1.0",
65
  lifespan=lifespan,
backend/app/services/export/alto.py CHANGED
@@ -170,7 +170,7 @@ def generate_alto(master: PageMaster) -> str:
170
  master.processing.processed_at.isoformat()
171
  )
172
  software = etree.SubElement(step, _a("processingSoftware"))
173
- etree.SubElement(software, _a("softwareCreator")).text = "Scriptorium AI"
174
  etree.SubElement(software, _a("softwareName")).text = (
175
  master.processing.model_display_name
176
  )
 
170
  master.processing.processed_at.isoformat()
171
  )
172
  software = etree.SubElement(step, _a("processingSoftware"))
173
+ etree.SubElement(software, _a("softwareCreator")).text = "IIIF Studio"
174
  etree.SubElement(software, _a("softwareName")).text = (
175
  master.processing.model_display_name
176
  )
backend/app/services/export/iiif.py CHANGED
@@ -51,7 +51,7 @@ def generate_manifest(
51
  Optionnelles : language (str), repository (str), shelfmark (str),
52
  date_label (str), institution (str)
53
  base_url: URL de base de la plateforme, sans slash final
54
- (ex. "https://scriptorium-ai.hf.space").
55
 
56
  Returns:
57
  dict sérialisable en JSON contenant le Manifest IIIF 3.0.
 
51
  Optionnelles : language (str), repository (str), shelfmark (str),
52
  date_label (str), institution (str)
53
  base_url: URL de base de la plateforme, sans slash final
54
+ (ex. "https://iiif-studio.hf.space").
55
 
56
  Returns:
57
  dict sérialisable en JSON contenant le Manifest IIIF 3.0.
backend/app/services/export/mets.py CHANGED
@@ -128,7 +128,7 @@ def generate_mets(
128
  # ── 1. metsHdr ──────────────────────────────────────────────────────────
129
  hdr = _el(root, f"{_M}metsHdr", {"CREATEDATE": now_iso})
130
  agent = _el(hdr, f"{_M}agent", {"ROLE": "CREATOR", "TYPE": "ORGANIZATION"})
131
- _el(agent, f"{_M}name", text="Scriptorium AI")
132
 
133
  # ── 2. dmdSec — Dublin Core ─────────────────────────────────────────────
134
  dmd = _el(root, f"{_M}dmdSec", {"ID": "DMD_1"})
@@ -152,15 +152,15 @@ def generate_mets(
152
  # ── 3. amdSec — techMD global ───────────────────────────────────────────
153
  amd = _el(root, f"{_M}amdSec")
154
  tech = _el(amd, f"{_M}techMD", {"ID": "AMD_1"})
155
- amd_wrap = _el(tech, f"{_M}mdWrap", {"MDTYPE": "OTHER", "OTHERMDTYPE": "ScriptoriumAI"})
156
  amd_data = _el(amd_wrap, f"{_M}xmlData")
157
 
158
  # Premier processing trouvé parmi les pages
159
  first_processing = next(
160
  (m.processing for m in pages if m.processing is not None), None
161
  )
162
- amd_root = etree.SubElement(amd_data, "scriptoriumProcessing")
163
- _el(amd_root, "generator", text="Scriptorium AI")
164
  _el(amd_root, "pageCount", text=str(len(pages)))
165
  _el(amd_root, "corpusSlug", text=corpus_slug)
166
  if first_processing:
 
128
  # ── 1. metsHdr ──────────────────────────────────────────────────────────
129
  hdr = _el(root, f"{_M}metsHdr", {"CREATEDATE": now_iso})
130
  agent = _el(hdr, f"{_M}agent", {"ROLE": "CREATOR", "TYPE": "ORGANIZATION"})
131
+ _el(agent, f"{_M}name", text="IIIF Studio")
132
 
133
  # ── 2. dmdSec — Dublin Core ─────────────────────────────────────────────
134
  dmd = _el(root, f"{_M}dmdSec", {"ID": "DMD_1"})
 
152
  # ── 3. amdSec — techMD global ───────────────────────────────────────────
153
  amd = _el(root, f"{_M}amdSec")
154
  tech = _el(amd, f"{_M}techMD", {"ID": "AMD_1"})
155
+ amd_wrap = _el(tech, f"{_M}mdWrap", {"MDTYPE": "OTHER", "OTHERMDTYPE": "IIIFStudio"})
156
  amd_data = _el(amd_wrap, f"{_M}xmlData")
157
 
158
  # Premier processing trouvé parmi les pages
159
  first_processing = next(
160
  (m.processing for m in pages if m.processing is not None), None
161
  )
162
+ amd_root = etree.SubElement(amd_data, "iiifStudioProcessing")
163
+ _el(amd_root, "generator", text="IIIF Studio")
164
  _el(amd_root, "pageCount", text=str(len(pages)))
165
  _el(amd_root, "corpusSlug", text=corpus_slug)
166
  if first_processing:
backend/app/services/ingest/iiif_fetcher.py CHANGED
@@ -13,8 +13,8 @@ _DEFAULT_TIMEOUT = 60.0 # secondes — les images IIIF haute résolution peuven
13
 
14
  _HEADERS = {
15
  "User-Agent": (
16
- "Mozilla/5.0 (compatible; ScriptoriumAI/1.0; "
17
- "+https://huggingface.co/spaces/Ma-Ri-Ba-Ku/scriptorium-ai)"
18
  ),
19
  "Accept": "image/jpeg,image/png,image/*,*/*",
20
  }
 
13
 
14
  _HEADERS = {
15
  "User-Agent": (
16
+ "Mozilla/5.0 (compatible; IIIFStudio/1.0; "
17
+ "+https://huggingface.co/spaces/Ma-Ri-Ba-Ku/iiif-studio)"
18
  ),
19
  "Accept": "image/jpeg,image/png,image/*,*/*",
20
  }
backend/pyproject.toml CHANGED
@@ -3,9 +3,9 @@ requires = ["setuptools>=61", "wheel"]
3
  build-backend = "setuptools.build_meta"
4
 
5
  [project]
6
- name = "scriptorium-ai-backend"
7
  version = "0.2.0"
8
- description = "Backend Scriptorium AI — plateforme générique d'éditions savantes augmentées"
9
  requires-python = ">=3.11"
10
  dependencies = [
11
  "fastapi>=0.111",
 
3
  build-backend = "setuptools.build_meta"
4
 
5
  [project]
6
+ name = "iiif-studio-backend"
7
  version = "0.2.0"
8
+ description = "Backend IIIF Studio — plateforme générique d'éditions savantes augmentées"
9
  requires-python = ">=3.11"
10
  dependencies = [
11
  "fastapi>=0.111",
backend/tests/test_export_alto.py CHANGED
@@ -459,10 +459,10 @@ def test_ocr_processing_absent_without_processing_info():
459
  assert len(_xpath(root, "//a:OCRProcessing")) == 0
460
 
461
 
462
- def test_software_creator_is_scriptorium_ai(master_text_only):
463
  root = _parse(generate_alto(master_text_only))
464
  creator = _one(root, "//a:processingSoftware/a:softwareCreator")
465
- assert creator.text == "Scriptorium AI"
466
 
467
 
468
  # ---------------------------------------------------------------------------
 
459
  assert len(_xpath(root, "//a:OCRProcessing")) == 0
460
 
461
 
462
+ def test_software_creator_is_iiif_studio(master_text_only):
463
  root = _parse(generate_alto(master_text_only))
464
  creator = _one(root, "//a:processingSoftware/a:softwareCreator")
465
+ assert creator.text == "IIIF Studio"
466
 
467
 
468
  # ---------------------------------------------------------------------------
backend/tests/test_export_iiif.py CHANGED
@@ -30,7 +30,7 @@ import pytest
30
  from app.schemas.page_master import EditorialInfo, EditorialStatus, OCRResult, PageMaster, ProcessingInfo
31
  from app.services.export.iiif import generate_manifest, write_manifest
32
 
33
- _BASE_URL = "https://scriptorium-ai.example.com"
34
  _IIIF_CONTEXT = "http://iiif.io/api/presentation/3/context.json"
35
 
36
 
 
30
  from app.schemas.page_master import EditorialInfo, EditorialStatus, OCRResult, PageMaster, ProcessingInfo
31
  from app.services.export.iiif import generate_manifest, write_manifest
32
 
33
+ _BASE_URL = "https://iiif-studio.example.com"
34
  _IIIF_CONTEXT = "http://iiif.io/api/presentation/3/context.json"
35
 
36
 
backend/tests/test_export_mets.py CHANGED
@@ -252,7 +252,7 @@ def test_metsHdr_agent_creator(beatus_pages, beatus_meta):
252
  root = _parse(generate_mets(beatus_pages, beatus_meta))
253
  agent = _one(root, "m:metsHdr/m:agent[@ROLE='CREATOR']")
254
  name = _one(agent, "m:name")
255
- assert name.text == "Scriptorium AI"
256
 
257
 
258
  def test_metsHdr_has_createdate(beatus_pages, beatus_meta):
 
252
  root = _parse(generate_mets(beatus_pages, beatus_meta))
253
  agent = _one(root, "m:metsHdr/m:agent[@ROLE='CREATOR']")
254
  name = _one(agent, "m:name")
255
+ assert name.text == "IIIF Studio"
256
 
257
 
258
  def test_metsHdr_has_createdate(beatus_pages, beatus_meta):
backend/tests/test_image_pipeline.py CHANGED
@@ -274,8 +274,8 @@ def test_fetch_iiif_image_success():
274
  "https://example.com/image.jpg",
275
  headers={
276
  "User-Agent": (
277
- "Mozilla/5.0 (compatible; ScriptoriumAI/1.0; "
278
- "+https://huggingface.co/spaces/Ma-Ri-Ba-Ku/scriptorium-ai)"
279
  ),
280
  "Accept": "image/jpeg,image/png,image/*,*/*",
281
  },
 
274
  "https://example.com/image.jpg",
275
  headers={
276
  "User-Agent": (
277
+ "Mozilla/5.0 (compatible; IIIFStudio/1.0; "
278
+ "+https://huggingface.co/spaces/Ma-Ri-Ba-Ku/iiif-studio)"
279
  ),
280
  "Accept": "image/jpeg,image/png,image/*,*/*",
281
  },
frontend/index.html CHANGED
@@ -3,7 +3,7 @@
3
  <head>
4
  <meta charset="UTF-8" />
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Scriptorium AI</title>
7
  </head>
8
  <body>
9
  <div id="root"></div>
 
3
  <head>
4
  <meta charset="UTF-8" />
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>IIIF Studio</title>
7
  </head>
8
  <body>
9
  <div id="root"></div>
frontend/package.json CHANGED
@@ -1,5 +1,5 @@
1
  {
2
- "name": "scriptorium-ai-frontend",
3
  "version": "0.1.0",
4
  "private": true,
5
  "scripts": {
 
1
  {
2
+ "name": "iiif-studio-frontend",
3
  "version": "0.1.0",
4
  "private": true,
5
  "scripts": {
frontend/src/lib/api.ts CHANGED
@@ -2,7 +2,7 @@ const BASE_URL: string = import.meta.env.VITE_API_URL ?? ''
2
 
3
  if (!BASE_URL && import.meta.env.PROD) {
4
  console.warn(
5
- '[Scriptorium] VITE_API_URL non défini en production. ' +
6
  'Les appels API utiliseront des chemins relatifs, ce qui peut échouer ' +
7
  'si le frontend n\'est pas servi par le même domaine que le backend.'
8
  )
 
2
 
3
  if (!BASE_URL && import.meta.env.PROD) {
4
  console.warn(
5
+ '[IIIF-Studio] VITE_API_URL non défini en production. ' +
6
  'Les appels API utiliseront des chemins relatifs, ce qui peut échouer ' +
7
  'si le frontend n\'est pas servi par le même domaine que le backend.'
8
  )
frontend/src/pages/Home.tsx CHANGED
@@ -68,7 +68,7 @@ export default function Home({ onOpenManuscript, onOpenPage, onAdmin }: Props) {
68
  <div className="min-h-screen bg-stone-50">
69
  <header className="bg-stone-900 text-stone-100 px-8 py-6 flex items-start justify-between">
70
  <div>
71
- <h1 className="text-2xl font-semibold tracking-tight">Scriptorium AI</h1>
72
  <p className="text-stone-400 text-sm mt-1">
73
  Plateforme de génération d'éditions savantes augmentées
74
  </p>
 
68
  <div className="min-h-screen bg-stone-50">
69
  <header className="bg-stone-900 text-stone-100 px-8 py-6 flex items-start justify-between">
70
  <div>
71
+ <h1 className="text-2xl font-semibold tracking-tight">IIIF Studio</h1>
72
  <p className="text-stone-400 text-sm mt-1">
73
  Plateforme de génération d'éditions savantes augmentées
74
  </p>