Spaces:
Sleeping
Sleeping
| # Pythonise Exercice — v2 | |
| App Flask à deux modes : | |
| - **Pythonisation** d'exercices PyxiScience MyST (valeurs fixes → randomisées) ; | |
| - **Déclinaisons** (2026-07) : génération de versions **QCM** (MCQ) et/ou | |
| **QAT** (FGQ, champs libres ordonnés) d'un exercice, randomisées elles aussi. | |
| `:originalExerciseId:` systématique (= `:id:` du QST source, vide sinon) ; | |
| distracteurs MCQ en **grille miroir** (variations symétriques — la bonne | |
| réponse ne se devine jamais par la forme ; règle enseignante 2026-07-02). | |
| Un bouton **⏹ Arrêter** (tous modes) annule le job en cours : arrêt coopératif | |
| au prochain point d'étape (`POST /api/jobs/<id>/cancel`), résultats partiels | |
| conservés et téléchargeables. | |
| Pipeline async : analyse LLM + notions + RAG (parallèles, **partagés entre QCM | |
| et QAT**) → génération par paires → audit LLM → filets déterministes → **porte | |
| harnais** (100 graines + contrôles étendus déclinaisons : unicité des options | |
| MCQ sur toutes les graines, 1 seule bonne réponse, « None » en dernier, arité | |
| FGQ, `:solution:` JSON) ; verdict exposé dans l'UI. | |
| ## Architecture (refonte 2026-06) | |
| ``` | |
| run.py Lancement : python run.py (ou python -m app) | |
| app/ | |
| ├── __init__.py create_app() + logging + .env | |
| ├── config.py TOUTE la config : modèles, PYTHON_FENCE_BACKTICKS=4, | |
| │ ANALYSIS_MODEL_IDX, RAG_TOP_K, USE_REASONING, | |
| │ DEFAULT_LANG, JOB_TTL, chemins, prix | |
| ├── server.py routes Flask (/, /health, /api/models, /api/jobs) | |
| ├── keys.py chargement des clés API (.env) | |
| ├── pipeline/ | |
| │ ├── orchestrator.py chef d'orchestre (1 exercice) | |
| │ ├── analyze.py analyse + notions + RAG en PARALLÈLE | |
| │ ├── generate.py découpage + génération par paires | |
| │ ├── audit.py audit LLM ≤2 itérations + filet de sécurité patches | |
| │ ├── postprocess.py filets déterministes (auto-lift {{}}, ${}, fences 4, | |
| │ │ renommage camelCase, décimales/langue, dédoublonnage) | |
| │ ├── solutions.py substitution des solutions validées (règle 8.1) | |
| │ ├── translate.py langue cible fr/en/both (prose seule, Python masqué) | |
| │ ├── prompts.py tous les prompts (v1 archivée dans knowledge/) | |
| │ └── fewshots.py sélection d'un exemple canonique par type détecté | |
| ├── rag/ functions.py (FAISS) · formatter.py · notions.py · | |
| │ catalogue.py (catalogue curé injecté par domaine) | |
| ├── llm/ client.py (OpenRouter + reasoning opt.) · cost.py | |
| ├── validation/ harness.py (porte qualité) · sandbox.py (exec sandboxée) | |
| ├── knowledge/ pythonisation_rules.md · rules_digest.py · | |
| │ fewshots/ (exemples VERTS) · prompts_v1_archive.md · | |
| │ functions_catalogue.md (catalogue curé des helpers) | |
| ├── corpus/ 5 fichiers de fonctions PyxiScience (RAG) | |
| └── web/templates/index.html UI (batch, langue, verdict harnais, coût) | |
| data/ notions.xlsx · faiss_cache/ | |
| tests/smoke.py smoke tests hors-ligne (LLM mocké) | |
| ``` | |
| ## Installation (WSL Ubuntu) | |
| ```bash | |
| python3 -m venv .venv && source .venv/bin/activate | |
| pip install -r requirements.txt # faiss-cpu + sentence-transformers ≈ 1.5 Go | |
| cp .env.example .env # renseigner OPENROUTER_API_KEY / OPENAI_API_KEY | |
| python run.py # http://127.0.0.1:5000 | |
| ``` | |
| Smoke tests (sans réseau) : `.venv/bin/python tests/smoke.py` | |
| Validation d'une sortie : `python ../.claude/skills/validation-harness/harness.py <fichier>.md --seeds 300` | |
| ### Endpoints | |
| | Méthode | URL | Description | | |
| |---------|------------------|--------------------------------------------------------| | |
| | GET | `/` | UI web | | |
| | GET | `/health` | sanity check | | |
| | GET | `/api/models` | roster des modèles + défauts | | |
| | POST | `/api/jobs` | démarre un job (1 fichier OU batch) → `{job_id}` | | |
| | GET | `/api/jobs/<id>` | suivi + résultats par fichier (harnais, coût, langue) | | |
| | GET | `/api/jobs/<id>/download` | ZIP de toutes les sorties `.md` + `_recapitulatif.md` | | |
| POST body : | |
| ```json | |
| { | |
| "files": [{"filename": "exo.md", "content": "<MyST>"}, ...], | |
| "lang": "fr | en | both", | |
| "level": "", | |
| "model_idx": 1, | |
| "mode": "pythonise | declinaisons", | |
| "types": {"qcm": true, "qat": true} | |
| } | |
| ``` | |
| (`mode` absent ⇒ `pythonise`, rétro-compatible ; `types` requis en mode | |
| `declinaisons`, au moins un `true` — les deux cochés ⇒ 2 fichiers par source, | |
| nommés `<source>_QCM.md` / `<source>_QAT.md`, avec UNE seule analyse partagée. | |
| Rétro-compat : `{"content": "...", "filename": "..."}` accepté pour 1 fichier.) | |
| Résultat par fichier : `exercise`, `analysis`, `notions`, `audit_patches`, | |
| `warnings`, **`harness` {ok, seeds, summary}**, **`lang` {source, target, action}**, | |
| **`cost` {usd, eur, requests}**, `duration_s`. | |
| **Téléchargement** (UI, panneau Résultat) : bouton **« Télécharger .md »** | |
| (sortie pythonisée du fichier affiché, suffixe `_pythonise.md` pour ne pas | |
| écraser la source) ; en **batch**, bouton **« Tout (.zip) »** → ZIP de toutes | |
| les sorties + un `_recapitulatif.md` (verdict harnais / warnings / coût par fichier). | |
| ## Politique de modèles & banc d'essai (2026-07) | |
| **4 politiques** (`policy` dans `POST /api/jobs`, sélecteur dans l'UI) : | |
| `auto` (défaut — pré-classifieur de difficulté, départ sur l'échelle par coût | |
| croissant, escalade d'un échelon à chaque échec harnais, plafonnée `best`) · | |
| `best` (qualité max) · `cheap` (le moins cher qui tient `SEUIL_VERT=0.90`) · | |
| `manual` (IDs explicites par rôle : `models {generate, audit, mecanique}`). | |
| Rôles : `generate` (+réparation), `audit`, `mecanique` (analyse, traduction, | |
| substitutions). Télémétrie de l'échelon gagnant dans `result.policy_telemetry`. | |
| **`claude-fable-5` est retiré de toutes les listes (choix volontaire).** | |
| **Clés API** : tout fonctionne avec la seule `OPENROUTER_API_KEY` (route de | |
| repli universelle). Clés directes optionnelles par fournisseur : | |
| `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, `XAI_API_KEY`, | |
| `DEEPSEEK_API_KEY`, `MOONSHOT_API_KEY`, `ZAI_API_KEY`, `MINIMAX_API_KEY`, | |
| `MISTRAL_API_KEY` — un modèle sans aucune clé est « non testé », jamais bloquant. | |
| **Banc d'essai** : | |
| ```bash | |
| python -m bench run # rôles/modèles dispo, corpus échantillonné | |
| python -m bench run --roles generate --models claude-sonnet-5,deepseek-v4-pro | |
| python -m bench run --exos all --types both --seuil 0.92 | |
| python -m bench run --dry-run # plomberie mockée (CI, aucune clé) | |
| python -m bench consolidate # fusionne les runs partiels/parallèles | |
| ``` | |
| Sorties : `bench/results/<ts>.json|.csv|_rapport.md` + mise à jour de | |
| `app/models/recommended.json` (protégé par `manual_override: true`). | |
| Un run filtré `--models` n'écrit jamais recommended.json ; `consolidate` | |
| fusionne tous les résultats (la cellule la plus récente gagne, restreinte | |
| à l'échantillon courant du rôle) puis réécrit la recommandation globale. | |
| Plusieurs bancs peuvent tourner en parallèle (un processus = un compteur | |
| de coûts isolé) ; ne pas lancer d'autre job LLM dans le même processus. | |
| Corpus : `bench/corpus/*.md` (ajouter un exercice = y déposer un `.md`). | |
| Prix : `app/models/prices.json` — **à re-vérifier avant prod, ça change | |
| chaque semaine**. | |
| ## Sécurité | |
| 🚨 **Les clés API dans `.env` étaient en clair dans le zip d'origine** — | |
| considère-les comme exposées et **rotate-les** : | |
| - OpenRouter : https://openrouter.ai/keys | |
| - OpenAI : https://platform.openai.com/api-keys | |
| La sandbox (`app/validation/sandbox.py`) exécute le code généré par le LLM | |
| avec builtins restreints, imports whitelistés et timeout (mono-poste : le | |
| modèle de menace est l'accident LLM, pas un adversaire). | |
| ## Notes | |
| - Premier lancement : reconstruit le cache FAISS si absent (≈ 30 s). | |
| - Modèles (IDs vérifiés sur OpenRouter 2026-07-02) : Opus 4.8, **Sonnet 5 | |
| (défaut)**, Haiku 4.5, Gemini 2.5 Pro, GPT-5.4 — catalogue complet (16 | |
| modèles) dans `app/models/catalog.py`. Fable 5 retiré volontairement. | |
| - L'étape d'analyse suit le modèle choisi par l'utilisateur | |
| (`ANALYSIS_MODEL_IDX=None` dans config.py pour ce comportement). | |
| - Mode batch : fichiers traités séquentiellement ; un échec n'arrête pas les | |
| autres ; récapitulatif VERT/ROUGE/erreurs + coût total. | |