Spaces:
Runtime error
Runtime error
Commit History
24 days ago
-
feat(embeddings): voyage-4-large remplace EmbeddingGemma (API Voyage, halfvec 1024)
Mesure sur tout le corpus (32 303 morceaux) et le jeu relu de 102 questions : en recherche dense, MRR@20 0,758 contre 0,537 pour EmbeddingGemma en francais, 0,680 contre 0,537 en anglais. Par le chemin du chat lui-meme (mots-cles en plein texte, question entiere vectorisee, 8 extraits envoyes au modele), l'article attendu figure parmi les extraits 92 % du temps en francais contre 61 % avec Gemma (MRR@8 0,504 contre 0,336), et 87 % en anglais contre 56 % (0,538 contre 0,282). Vectoriser la question entiere plutot que ses mots-cles gagne a lui seul +0,087 de MRR@8 en francais [IC 95 % +0,035 ; +0,143] et +0,127 en anglais [+0,067 ; +0,191]. - embedding_service.py : VoyageProvider (httpx, connexion gardee : 0,42 s par question contre 1,14 s avec une connexion neuve). Son corps de requete reproduit les vecteurs deja calcules du corpus : un test voyage_live le prouve, cosinus >= 0,999 sur deux questions et deux documents. Lots de 900 textes et 90 000 jetons estimes, refus definit…57c8117
-
feat(extraction): Docling en local, OCR pleine page, cache reprenable
Docling remplace l'API Gemini, payante, comme extracteur par defaut (PDF_EXTRACTION_ENGINE=docling ; « gemini » reste disponible). - Chaque page est rasterisee (pypdfium2, 216 dpi) et passe par l'OCR pleine page : 42 % des pages n'ont pas de couche texte, et docling-parse rend NOIRES les 59 pages de scans masques (MRC) du corpus. - OCR RapidOCR sur torch, donc sur le GPU : mesure sur 97 pages contre Gemini et LlamaParse, il lit les numeros d'article la ou Tesseract en fausse (« ARTICLE 16 » lu « 13 »), a 3,8-4 s par page. - Conversion par lots de 8 pages, chaque lot mis en cache aussitot, brut : une interruption ne coute que le lot en cours, et ameliorer le nettoyage n'oblige jamais a refaire l'OCR. L'empreinte du cache porte les versions, les revisions des modeles et le rendu ; les lots se relisent quelle que soit leur taille. - Un lot en echec est rejoue ; au bout de TENTATIVES_MAX, il est repris page a page, et seule la page fautive est abandonnee et signalee. - F…69b13a2 -
fix(langue): fastText fonctionne de nouveau sous NumPy 2
FastText.predict (fasttext-wheel 0.9.2) appelle np.array(probs, copy=False), qui leve sous NumPy 2 : fastText echouait a chaque detection, seul langdetect repondait, et le consensus ne passait jamais a vrai. La liaison C++ model.f.predict rend une liste Python, sans numpy. NumPy passe en 2.x : opencv-python, que tire Docling via rapidocr, l'exige. Le plafond <2 ne servait qu'a fastText. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>57d3657 -
feat(embeddings): EmbeddingGemma en local, present mais pas encore par defaut
Un second fournisseur d'embeddings, gratuit et sans reseau : EmbeddingGemma execute par onnxruntime. Gemini reste le defaut ; la bascule viendra apres celle du schema. INT8 ET NON Q4, SUR MESURE. q4 est 5,6 fois plus rapide par requete et prend 3,4 fois moins de memoire, mais sur 180 articles reels du Code Minier et 15 questions, il change le premier resultat pour 2 questions sur 15 et ~13 % du top-10. L'int8, lui, colle au fp32 de reference. La fidelite prime pour un corpus juridique ; q4 reste l'option d'un hebergement contraint en memoire. TROIS PIEGES QUI CASSENT EN SILENCE, chacun verrouille par un test verifie par mutation : - la sortie lue PAR SON NOM. La sortie 0 du graphe est last_hidden_state ; la moyenner donne un vecteur de 768, unitaire, et quasi orthogonal au vrai (cosinus 0,0188 mesure) ; - les prefixes de tache, au caractere pres. Sans eux le classement change ; - les jetons speciaux lus comme du texte. Un « <image_soft_token> » laisse par l'OCR donne l'id 26214…a5f02bb
-
fix(qualite): ruff devient une vraie barriere, et trouve un schema mort et casse
Ruff tournait sans jeu de regles declare : 1332 signalements, dont 98 faux positifs sur le motif `Depends()` de FastAPI et 615 modernisations de style. Un controle que personne ne peut passer n'est pas un controle. Le jeu retenu ne garde que ce qui signale un defaut reel (F, E4, E7, E9, I, RUF100), avec des exceptions nommees la ou l'ordre des imports EST la protection : env.py doit poser sys.path avant d'importer les modeles, conftest doit surcharger DATABASE_URL avant d'importer l'application. Ce que le controle a alors trouve : - UN SCHEMA MORT ET CASSE. `LawDetailResponse` existait deja, exposant `List[ArticleResponse]`. Il n'etait branche sur aucune route, et il etait doublement inutilisable : ArticleResponse porte le CONTENU integral de chaque article — pour le Code Minier, 166 Ko servis deux fois — et son `number` etait plafonne a 20 caracteres alors que la colonne en fait 64 depuis qu'elle accueille « QUATRE-VINGT-DIX-SEPTIEME ». Le plafond du schema est corrige, et…7435134
-
fix: backfill relancable, boucle d'evenements, reglages morts
scripts/regenerate_embeddings.py reecrit. Il s'executait A L'IMPORT, sans main() ni garde __main__, avec LAW_ID = 3 code en dur, un appel API par article et un message final renvoyant vers un script Meilisearch supprime. Desormais : argparse (--all / --law-id / --reindex / --dry-run / --force), reprise par defaut sur embedding IS NULL avec curseur sur l'id (un OFFSET saute des lignes, celles traitees sortant du filtre au fur et a mesure), API batch, attente exponentielle plafonnee sur quota avec reprise du MEME lot, commit par lot, troncature des contenus trop longs, et ecriture via CAST(:embedding AS vector) sur une chaine — une liste Python passee a travers text() est adaptee en ARRAY, qui ne se caste pas en vector. --reindex reconstruit l'index HNSW en CONCURRENTLY : la migration le cree sur une table vide, la reconstruction en masse apres backfill donne un meilleur graphe. process_law._extract_pdf_text appelait asyncio.run() depuis un thread de l'executor : cela ne fonctionnait qu…202650d -
feat(search): retrieval au niveau chunk, embeddings 1536 indexables
La recherche travaillait au niveau document et ne pouvait pas etre indexee. 1. Niveau chunk. postgres_search_service appliquait un SELECT DISTINCT ON (law_id) ... ORDER BY law_id : les articles etaient ecrases a un par loi, et l'ordre transmis a la fusion RRF n'etait plus celui de la pertinence mais celui des identifiants. Cote semantique, la distance etait calculee par article puis passee a func.max() + GROUP BY Law.* : l'article gagnant etait jete, le resultat sortait avec matched_articles vide, highlights vide et content nul — soit aucun texte pour le prompt. Nouveau schema ChunkResult, une ligne par article, avec son numero, son titre, sa section, sa page et son contenu integral. SearchResult est inchangee champ pour champ et desormais DERIVEE des chunks : le front n'a rien a changer et recoit enfin des matched_articles remplis. SearchResponse.chunks est additif, avec un defaut, donc les reponses deja en cache se deserialisent encore. 2. Filtre…72be68d -
fix(validation): trois utilitaires de securite qui ne validaient rien
is_file_size_valid comparait des megaoctets arrondis a deux decimales. Tout fichier de moins de ~5 Ko donnait 0.0 et etait rejete comme "File is empty (0 bytes)" — un decret d'une page pese souvent moins. A l'autre bout, 50,004 Mo s'arrondissait a 50.0 et passait sous la limite. Les deux comparaisons portent desormais sur les octets. sanitize_sql_input interpolait les mots-cles bruts dans rf"\b{kw}\b". La liste contient "--", "/*", "*/", "xp_" et "sp_" : "*/" donnait "\b*/\b", motif invalide (re.error: nothing to repeat). La fonction levait pour TOUTE entree non vide — elle n'a jamais assaini quoi que ce soit. Trois formes de motif selon la nature du mot-cle, et re.escape. sanitize_filename ne retirait que les points en tete : "../../etc/passwd" ressortait en "_.._etc_passwd", qui contient encore la remontee que la fonction existe pour neutraliser. Toute suite de deux points est reduite a un point. sanitize_html retirait la balise <script> mais gardait son corps : "alert(1)" se retr…4806809 -
Etape 11 : authentification reelle branchee (demande n°2)
app/core/auth.py (188 lignes, JWT + bcrypt + roles) etait complet et importe par PERSONNE. Les 9 endpoints d'administration passaient par deux stubs renvoyant {"id": 1, "role": "admin"} : ils etaient ouverts a tous. BUG BLOQUANT decouvert en chemin : passlib 1.7.4 (derniere version, 2020) est incompatible avec bcrypt >= 4.1 — il lit bcrypt.__about__.__version__, supprime depuis. Avec bcrypt 5.x installe, hash_password echouait avec "password cannot be longer than 72 bytes" MEME pour 13 caracteres : l'authentification aurait ete inutilisable telle que declaree. -> bcrypt utilise directement, passlib retire des manifestes. Pre-hachage SHA-256 + base64 avant bcrypt : entree de longueur fixe, donc support des phrases de passe longues sans la troncature silencieuse a 72 octets de bcrypt (deux mots de passe partageant leurs 72 premiers octets etaient equivalents). Ajouts : - app/api/routes/auth.py : POST /login (formulaire OAuth2, l'URL exacte attendue par oauth2_scheme, donc le…da2ad52
-
Etape 0 : l'application et la suite de tests redemarrent
Trois blocages empechaient tout : l'app ne s'importait pas et pytest ne collectait rien. 1. SDK Gemini incompatible gemini_service.py importait google-genai (nouveau) et embedding_service.py google.generativeai (ancien), alors que seul l'ancien etait declare : ImportError au chargement de app.main. -> embedding_service.py porte sur google-genai (genai.Client / embed_content / result.embeddings[].values) ; requirements et pyproject alignes. 2. email-validator absent EmailStr est resolu a la construction des classes : sans lui, l'import de admin.py et schemas/user.py echouait. -> pydantic[email]. 3. SyntaxError dans la suite de tests test_ocr_service.py:42 : caracteres accentues dans un litteral bytes, ce qui faisait echouer la collecte de TOUTE la suite. -> str + .encode("latin-1"). Ameliorations au passage : - generate_embedding accepte task_type ; les requetes utilisent RETRIEVAL_QUERY au lieu de RETRIEVAL_DOCUMENT (la recherche est asymetrique). Le…0833e39
-
feat(migration): Replace Redis/Meilisearch/Celery with 100% native PostgreSQL FTS and FastAPI BackgroundTasks
c77abb9
-
chore(backend): remove heavy ML models and spacy to reduce image size
d2e4a4d
-
chore: initial standalone backend for JuriX API (Render)
9cf9a2a