Spaces:
Sleeping
Sleeping
| # Sprint A5 (M-14) — anti-régression de performance OCR. | |
| # | |
| # Hebdomadaire (cron lundi 06:00 UTC) + manuel via workflow_dispatch. | |
| # **Pas** déclenché à chaque PR (coût Tesseract + stabilité statistique | |
| # CER nécessitent un corpus plus large que ce qu'on peut tolérer en PR | |
| # bloquante). Le but : détecter une dérive franche introduite par un | |
| # refactor de la normalisation, du runner, ou un upgrade de pytesseract. | |
| # | |
| # Sortie : un commentaire automatique sur l'issue #perf-baseline avec | |
| # le CER mesuré pour chaque doc + agrégat. Échec dur si CER moyen | |
| # > 15 % sur Tesseract (seuil large — détecte les régressions, pas | |
| # les variations normales). | |
| name: Perf regression (weekly) | |
| on: | |
| schedule: | |
| - cron: '0 6 * * 1' # Lundi 06:00 UTC | |
| workflow_dispatch: # Déclenchement manuel | |
| pull_request: | |
| paths: | |
| # Une PR qui touche le runner de benchmark, la normalisation ou | |
| # les adapters OCR déclenche aussi le check (cas où on veut | |
| # prouver qu'un refactor ne dégrade rien). | |
| - 'picarones/app/services/run_orchestrator.py' | |
| - 'picarones/app/services/run_orchestrator_execution.py' | |
| - 'picarones/app/services/run_orchestrator_helpers/**' | |
| - 'picarones/app/services/benchmark_service.py' | |
| - 'picarones/pipeline/executor.py' | |
| - 'picarones/pipeline/runner.py' | |
| - 'picarones/formats/text/normalization.py' | |
| - 'picarones/adapters/ocr/**' | |
| - '.github/workflows/perf_regression.yml' | |
| permissions: | |
| contents: read | |
| issues: write | |
| jobs: | |
| perf: | |
| name: CER regression check | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| cache: pip | |
| - name: Install Tesseract (Ubuntu) | |
| run: | | |
| sudo apt-get update -qq | |
| sudo apt-get install -y tesseract-ocr tesseract-ocr-fra tesseract-ocr-eng | |
| - name: Install Picarones | |
| run: | | |
| python -m pip install --upgrade pip setuptools wheel | |
| pip install -e ".[dev,web]" | |
| - name: Regenerate reference corpus (idempotent) | |
| run: python tests/fixtures/reference_corpus/_generate.py | |
| - name: Run benchmark on reference corpus | |
| id: bench | |
| run: | | |
| mkdir -p /tmp/perf_artifacts | |
| picarones run \ | |
| --corpus tests/fixtures/reference_corpus/ \ | |
| --engines tesseract \ | |
| --output /tmp/perf_artifacts/results.json \ | |
| --fail-if-cer-above 0.15 \ | |
| --no-progress | |
| - name: Generate report (lazy_images mode) | |
| if: always() | |
| run: | | |
| picarones report \ | |
| --results /tmp/perf_artifacts/results.json \ | |
| --output /tmp/perf_artifacts/perf_report.html \ | |
| --lazy-images || true | |
| - name: Upload artifacts | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: perf-${{ github.run_id }} | |
| path: /tmp/perf_artifacts/ | |
| retention-days: 30 | |
| - name: Comment on tracking issue (success) | |
| if: success() && github.event_name == 'schedule' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const data = JSON.parse( | |
| fs.readFileSync('/tmp/perf_artifacts/results.json', 'utf-8') | |
| ); | |
| const eng = data.engine_reports?.[0] || {}; | |
| const meanCer = eng.aggregated_metrics?.cer?.mean ?? 'n/a'; | |
| const body = `Hebdomadaire — Tesseract CER moyen: **${meanCer}** ` + | |
| `(commit \`${context.sha.slice(0,7)}\`, ` + | |
| `${data.engine_reports?.[0]?.document_results?.length ?? 0} docs).`; | |
| // Cherche l'issue de tracking, en crée une si absente. | |
| const issues = await github.rest.issues.listForRepo({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| labels: 'perf-baseline', | |
| state: 'open', | |
| }); | |
| let issueNumber; | |
| if (issues.data.length > 0) { | |
| issueNumber = issues.data[0].number; | |
| } else { | |
| const created = await github.rest.issues.create({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| title: '📈 Perf baseline (auto-tracking)', | |
| body: 'Issue de suivi du job hebdomadaire ' + | |
| '`perf_regression.yml`. Chaque exécution y commente ' + | |
| 'le CER moyen pour Tesseract.', | |
| labels: ['perf-baseline'], | |
| }); | |
| issueNumber = created.data.number; | |
| } | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issueNumber, | |
| body: body, | |
| }); | |
| - name: Comment on tracking issue (failure) | |
| if: failure() && github.event_name == 'schedule' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const issues = await github.rest.issues.listForRepo({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| labels: 'perf-baseline', | |
| state: 'open', | |
| }); | |
| if (issues.data.length > 0) { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issues.data[0].number, | |
| body: '❌ Échec hebdomadaire — CER > 15 % ou crash. ' + | |
| `Voir le run [${context.runId}]` + | |
| `(${context.serverUrl}/${context.repo.owner}/` + | |
| `${context.repo.repo}/actions/runs/${context.runId}).`, | |
| }); | |
| } | |