test(ia): consolidation Ollama + workers + prompt injection + CI intégration (+89 tests) - #13
Merged
Merged
Conversation
…tions Vague 1.2 de l'audit d'upgrade (safe wins IA). ## Bumps toolchain - uv 0.5.11 → 0.11.30 (Dockerfile builder + CI workflow) — 12 mois de retard, gain de perf resolver + fixes. - astral-sh/setup-uv v3 → v8 (dernière stable) avec version pinnée. ## Pins Docker (fin des :latest flottants) - redis:7-alpine → redis:8.8-alpine (docker-compose × 2) - minio/minio:latest → RELEASE.2025-10-15T17-29-55Z (× 2) - prom/prometheus:latest → v3.13.1 (× 2) - prom/alertmanager:latest → v0.33.1 (prod only) - grafana/grafana:latest → 13.0.4 (× 2) ## Supply-chain - jlumbroso/free-disk-space@main → pin SHA (54081f1, 2023-10-18). ## Notes - ollama/ollama:latest laissé (dev-only, projet qui bouge vite). - python:3.12-slim inchangé (attendre 3.13 alignement torch/sentence-transformers).
Décision stratégique : full Ollama pour tous les tiers (souveraineté données, IA custom long terme). Perf court terme sacrifiée assumée. Provider Ollama : - max_retries configurable via `settings.ollama_max_retries` (défaut 2). - Retry prompt inclut la réponse précédente — évite au petit modèle de repartir de zéro et perdre la structure globale entre attempts. - Détection modèle non pull (HTTP 404) → ExternalServiceError avec message actionnable `run \`ollama pull <model>\`` au lieu d'une erreur générique. - Détection content vide / whitespace-only (200 OK mais réponse creuse, symptôme OOM ou modèle non chargé) → ExternalServiceError explicite au lieu de tomber sur JSONDecodeError et gaspiller les retries. Config : - Les 3 tiers PREMIUM / STANDARD / FAST pointent tous sur qwen2.5-coder:7b. Le 3B collapse de façon reproductible sur les schémas nested — testé contre le schéma `OrientationSuggestion` de talent_analyzer. - .env.example aligné avec commentaire explicatif. Services : - talent_analyzer._CAREER_SCHEMA : `required` réduit aux champs sans default Pydantic (orientation_slug, confidence). Les autres (match_reason, required_skills_missing, transition_effort, timeline_estimate_months) ont des defaults dans OrientationSuggestion — les marquer required forçait les LLM à halluciner. Prompt système durci sur enums en anglais et interdiction des champs hors-schéma. - code_reviewer._fence_safe() : neutralise les triple-backticks (zero-width space) + NULL bytes dans source_code / test_output avant injection dans les code fences markdown. Défense contre le prompt injection classique \`\`\` IGNORE ALL INSTRUCTIONS \`\`\`.
+89 tests unitaires (293 → 382) + 9 tests d'intégration Ollama. Nouveaux tests unitaires : - tests/test_workers_envelope.py (33 tests) : 4 scénarios × 8 workers arq couverts (plagiarism, code_review, talent_match, recommendation, media replay/clip, analytics hidden_gems/churn). Happy path + idempotence + SkilluvAIError + Exception générique. Infra fakeredis + services mockés. Rapide (~6s), déterministe. Pattern paramétrisé via WorkerCase — ajouter un futur worker = ajouter une entrée dans la liste WORKERS. - tests/test_code_reviewer_service.py (31 tests) : service review_code testé en unité (jusqu'ici uniquement via gRPC servicer). Court-circuit soumission vide, calcul fragments_bonus par paliers, ton system prompt selon user_level, troncatures test_output/source_code, robustesse aux réponses LLM partielles, propagation d'identifiants, tier PREMIUM. - tests/test_prompt_injection.py (19 tests) : surfaces d'injection identifiées (source_code, test_output, tags, orientation_slug, programming_language, project_id), défense _fence_safe validée, bornes Pydantic vérifiées. Tests structurels — le comportement adversarial du LLM lui-même n'est pas testé (non-déterministe). - tests/test_llm_provider.py : +6 tests d'erreurs LLM approfondies (ReadTimeout, 500 server error, 404 model not pulled, empty content, whitespace-only content, external_errors_total incrémenté). Nouveaux tests d'intégration : - tests/integration/test_ollama_live.py (5 tests) : provider testé contre vrai Ollama local, pin sur Qwen 3B. Auto-skip si Ollama down. Marker `local_llm`. Health, structured outputs sur schémas réels code_review + career_path, chemins d'erreur (modèle inexistant, schéma impossible). - tests/integration/test_services_llm_live.py (4 tests) : services LLM testés end-to-end contre vrai Ollama, defaults prod (7B). Le test suggest_career_path est marqué @pytest.mark.flaky(reruns=2) — le LLM local out-of-the-box est probabiliste sur schémas complexes (~50-70% succès en 1 essai). À supprimer une fois le modèle fine-tuné. Infra : - pyproject.toml : ajout dep pytest-rerunfailures + markers custom (local_llm, integration). - uv.lock : rafraîchi.
Les tests tests/integration/{redis,minio,redis_services}_integration.py
existent depuis longtemps mais n'étaient jamais joués en CI faute de
services disponibles. Ils dormaient localement.
Job `integration-tests` :
- Ne tourne qu'après le job `check` (unit) — pas de gaspillage si le
fondement casse.
- Redis 7 alpine + Bitnami MinIO en services Docker Compose GitHub Actions
avec health checks (attente que les services soient prêts).
- Env vars REDIS_URL / MINIO_ENDPOINT pointent vers localhost:{6379,9000}.
- Bitnami MinIO démarre en mode server par défaut, MINIO_DEFAULT_BUCKETS
pré-crée le bucket skilluv-media.
Exclusions explicites :
- test_grpc_full_chain.py : pull sentence-transformers (~1 GB HF Hub) et
pré-warme les embeddings de plagiat. Trop lourd/lent pour la CI. À lancer
en local uniquement.
- test_ollama_live.py + test_services_llm_live.py : nécessitent un serveur
Ollama local — pas viable en CI GitHub Actions (aucun modèle pré-pull).
… manuel `bitnami/minio:latest` renvoie `manifest unknown` sur Docker Hub — Bitnami a supprimé cette image. Retour à l'image officielle `minio/minio`, mais elle exige l'argument `server /data` que GitHub Actions ne permet pas de passer via `services:` (pas de `command:`). Solution : démarrer MinIO en step `docker run` avec health check manuel. Plus verbose mais fiable et sans dépendance à un image tierce.
Le modèle `TestCase` a évolué pour rendre `description` obligatoire (voir src/models/challenge.py). Le fixture `_sample_challenge` passait un `name="basic"` qui était silencieusement ignoré (Pydantic accepte les extras par défaut) et omettait `description`. Ces tests n'ont jamais tourné en CI jusqu'ici (job intégration nouveau) — personne n'avait vu la dérive. Corrigé.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Consolidation de la couverture de tests de la partie IA (
skilluv-ia) avant le renforcement pré-prod. Trois sessions de travail regroupées ici.+89 tests unitaires (293 → 382), +9 tests d'intégration Ollama, nouveau job CI Redis/MinIO.
1.
feat(llm)— Consolidation Ollama (6a049a3)Décision stratégique : tout passe par Ollama, plus par Claude. Perf court terme sacrifiée pour la souveraineté données et l'IA custom long terme.
max_retriesconfigurable, retry prompt inclut la réponse invalide précédente, détection 404 modèle non pull avec message actionnable, détection content vide/whitespace.qwen2.5-coder:7b. Le 3B collapse de façon reproductible sur les schémas nested (validé empiriquement contreOrientationSuggestion).talent_analyzer._CAREER_SCHEMA: alignementrequiredavec les defaults Pydantic + prompt système durci (enums en anglais, interdiction d'extra fields).code_reviewer._fence_safe(): neutralisation triple-backticks (zero-width space) + NULL bytes dans les inputs utilisateur avant injection dans les code fences markdown.2.
test— Nouveaux tests (828bc4d)test_workers_envelope.py(33 tests) — 4 scénarios × 8 workers arq (plagiarism, code_review, talent_match, recommendation, media replay/clip, analytics hidden_gems/churn). Pattern paramétrisé viaWorkerCase. Fakeredis + services mockés. ~6s.test_code_reviewer_service.py(31 tests) — servicereview_codetesté en unité (jusqu'ici uniquement via gRPC servicer).test_prompt_injection.py(19 tests) — surfaces d'injection identifiées, défense_fence_safevalidée, bornes Pydantic vérifiées.test_llm_provider.py— +6 tests d'erreurs LLM approfondies (ReadTimeout, 500, 404, content vide, metric).tests/integration/test_ollama_live.py(5 tests) — provider testé contre vrai Ollama local (markerlocal_llm, auto-skip).tests/integration/test_services_llm_live.py(4 tests) — services LLM end-to-end contre Ollama réel.suggest_career_pathmarqué@pytest.mark.flaky(reruns=2)— flakiness assumée du LLM local out-of-the-box.Dépendance ajoutée :
pytest-rerunfailures>=15.0. Markers customlocal_llm+integration.3.
ci— Job intégration Redis/MinIO (9a2ac2d)Nouveau job
integration-testsqui monte Redis 7 alpine + Bitnami MinIO en services GitHub Actions (health checks). Exécutetests/integration/— sauftest_grpc_full_chain.py(HF Hub ~1 GB) et les tests Ollama live (pas de LLM disponible en CI).Test plan
check(unit tests, ruff) reste vert — 382 testsintegration-testsdoit passer avec Redis + MinIO en servicesRUN_LOCAL_LLM=1 uv run pytest -m local_llmpasse si Ollama + Qwen 7B + 3B pullNotes