Spaces:
Runtime error
Runtime error
Commit History
23 days ago
-
fix(decoupage): un long chapeau d'article n'est plus tronque a 200 caracteres
Un article trop long se coupe a ses alineas (1), (2), (3). Le texte qui precede le premier alinea (le chapeau) n'etait garde qu'en prefixe de 200 caracteres devant chaque morceau : au-dela, il ne figurait nulle part en base. Ni la recherche ni le chat ne pouvaient le lire. Cas releve : loi 2010/012 sur la cybercriminalite. Les articles 69 a 74 al. 2, colles sur une ligne par l'OCR, formaient le chapeau d'un « article 69 » coupe aux alineas de l'article 74 : il en restait « …celui qui accede san ». Mesure sur jurix_dev : 58 paragraphes (environ 33 000 caracteres) perdus dans une vingtaine de lois, dont le Code penal, le Code de procedure penale, le Code du travail et les lois 2010/012 et 2010/013. Un chapeau long ouvre desormais le premier morceau EN ENTIER (aux paragraphes s'il depasse lui-meme la taille cible), et seul son debut est repete devant les morceaux suivants. Simulation sur les 2275 lois : 15 lois changent, la perte disparait dans 13 d'entre elles ; les autres decoupages s…a42c933
-
feat(decoupage): les grands tableaux coupes entre leurs lignes, le cachet mieux retire
Tableaux. Ils n'etaient jamais coupes : ceux des lois de finances faisaient des chunks jusqu'a 79 000 caracteres, dont l'embedding ne lisait que les 10 000 premiers (110 chunks du corpus). Un tableau markdown se coupe desormais entre deux lignes, jamais au milieu d'une ; chaque morceau repete l'en-tete du tableau et le debut de l'article. Une premiere ligne de donnees prise pour un en-tete n'est pas repetee. Listes nominatives. Le texte de nomination se cherche hors des tableaux : « PROMOTION DE LA FEMME », intitule de programme d'une loi de reglement, faisait d'un tableau budgetaire une liste de 35 personnes. Et des que les tableaux reconnus portent la majorite des lignes, tous les tableaux de l'article sont de la liste : ceux que la detection manquait restaient dans le texte vectorise (155 000 caracteres pour 4 229 gardiens de la paix). Audit sur les 2238 documents : chunks de plus de 10 000 caracteres de 110 a 1 ; plus gros texte vectorise de 136 601 a 15 931 caracteres ; un seul …911f84f -
fix(decoupage): l'article 1er illisible ne sort plus de l'index vectoriel
Le cachet et le sceau recouvrent souvent, dans la marge, le marqueur de l'article 1er. Illisible, l'article partait avec les visas (base legale), ou se collait a la formule d'execution quand c'est l'article 2 qui etait illisible — dans les deux cas hors de l'index vectoriel. Audit du decoupage sur les 2238 documents extraits : 53 n'avaient AUCUN vecteur. Apres correction : 1 (une page que l'OCR n'a pas su lire), et 83 articles reconnus en plus, aucun en moins. - Ce qui suit « DECRETE : », « ARRETE : » ou la formule de promulgation devient un chunk DISPOSITIF, traite comme un article ; a defaut de ces mots, ce qui suit le dernier visa, s'il fait une vraie phrase. - Un chunk n'est une formule d'execution que s'il ne reste presque aucun mot hors des paragraphes qui la portent. - Marqueurs ecorches releves sur tout le corpus : « ARTICIE », « ARTCLE », « RTICLE », « ARTTICLE », « Artice », « ARTI CLE1er », « Article ler », et prefixes parasites (« ■ ARTiCLE », « ww ARTICLE », « 1…f8209f9
-
feat(decoupage): les sorties Docling decoupees sans perte
Mesure sur les sorties Docling du corpus (banc OCR, puis pilote de 19 documents), chaque regle payee par un defaut constate. Nettoyage (markdown_cleanup) : - cachet tolerant a l'OCR (mots colles, lettres confondues), en capitales seulement : « une copie certifiee conforme » a fournir reste du texte ; - filigrane rendu en tableau d'une cellule, lambeaux du cachet ; - nettoyer_markdown met a plat titres et marqueurs decores (« ## ARTICLE », « - ARTICLE 12.- », « ARTIiCLE 81 ») : le sommaire de la page de lecture en voyait 155 sur 191, il en voit 189. Decoupe (text_chunker) : - marqueurs « ARTICLE_1er », « 2. ARTICLE 41.- », mot ecorche par l'OCR, ordinaux jusqu'a la centaine, en-tetes colles (« CHAPITREII ») ; - un renvoi en liste (« - article 12 de la loi ») ne coupe plus l'article. Raffinage (chunk_refiner) : - une liste nominative garde ses noms dans le contenu : elle n'etait enregistree nulle part ailleurs. Seul le vecteur prend la forme resumee, et une longue liste es…14eb55e
-
feat(extraction): remplacer LlamaParse par Gemini multimodal
La cle LlamaParse ayant ete retiree, l'ingestion de tout nouveau PDF levait et marquait le document `refused` — le bon echec, mais l'ingestion etait a l'arret. LE MOTIF DU CHANGEMENT EST LA PAGINATION, PAS LE PRIX. LlamaParse rend le document en un bloc sans signaler ses coupures de page ; `_split_markdown_pages` les reconstruisait en coupant sur les lignes de trois tirets, et sa propre docstring admettait la limite : « la pagination ne devient exacte qu'avec un moteur qui renvoie un numero de page explicite ». Mesure en base : les 710 articles portent tous `page_number = 1`, et sur les 27 lois, 271 pages de PDF ne donnent que 27 pages extraites — couverture moyenne 0,60. Un lien `?article=35` ouvre donc le PDF a la premiere page. MESURE AVANT D'ECRIRE, sur le Code Minier dont la sortie LlamaParse est en base : densite 2352 car/page contre 2405 — equivalent, rien n'est perdu marqueurs de page 1 par page contre 1 pour 69 pages lot pages 21 a 32 numerote 21…ca48b04 -
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 -
feat(recherche): le titre passe devant le corps du texte, et les fautes de frappe passent
Trois demandes utilisateur, quatre causes. 1. Chercher "nomination" remontait des documents sans ce mot dans leur titre, et le moins pertinent sortait premier. `articles.search_vector` ne contenait ni le titre de la loi ni celui de l'article ; `laws.search_vector` collait titre et contenu sans un seul `setweight` ; et la seule branche qui voyait `laws.title` etait gardee derriere `if not chunks:`, donc jamais executee. Mesure avant : le decret SOCADEL (titre sans le mot) sortait a 0,4000 devant cinq documents dont le titre le porte, a 0,2000. Apres ponderation : 2,10 contre 0,20. 2. "article 35 du code minier" ne pouvait pas aboutir : le decoupeur attendait `Article 35` la ou le markdown ecrit `**ARTICLE 1er.**-` et `**Article1er.-**`. 8 lois sur 27 avaient un numero reconnu, contre 27 sur 27 apres correction ; 127 numeros reels contre 599. Le Code Minier passe de 41 a 193 articles. 3. Aucune tolerance aux fautes : `word_similarity` sur titre deplie, s…893a679
-
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 -
Etape 14 : tests du code ajoute (54 nouveaux tests)
Le code ecrit pendant ce chantier n'avait aucun test. tests/test_api/test_auth_routes.py (16) — connexion JSON et formulaire, mot de passe faux, email inconnu avec message IDENTIQUE (verifie explicitement : les distinguer permettrait d'enumerer les comptes), last_login_at, /auth/me avec et sans jeton, jeton malforme, matrice d'autorisation 401/403/200. Un test parcourt les routes montees et echoue si un chemin sous /admin/ ou /ocr/ n'a aucune dependance d'authentification : c'est lui qui rattrapera le prochain endpoint ajoute sans garde. tests/test_services/test_llama_parse_service.py (18) — respx intercepte httpx, donc aucun appel reseau et aucun credit consomme. Couvre le nettoyage des cachets (bloc multiligne, cachet condense sur une ligne, en-tete legitime CONSERVE, filigrane, tableaux preserves, <sup> retire), le cache sha256 (verifie qu'un second appel ne repart pas sur le reseau), l'echec de job, l'absence de retry sur 401, et les quatre formes de reponse de l'API v2. tests/t…0e1604f -
Etape 8 : trois bugs du chunker, dont une perte silencieuse de contenu
1. PERTE DE CONTENU (le plus grave) SECTION_PATTERNS utilisait l'alternative de chiffres romains 'I{1,3}V?|IV|V?I{0,3}' — or V?I{0,3} peut matcher la CHAINE VIDE. 'TITRE\s+' suffisait donc a declencher une detection de section : un article intitule "Titre niveau 1" etait pris pour un en-tete, son contenu coupe a zero caractere, puis ecarte comme "trop court". Tout article dont le titre commence par Titre ou Chapitre etait PERDU, sans erreur ni trace. -> [IVXLC]{1,7}, qui ne peut pas etre vide. 2. NUMEROTATION HIERARCHIQUE FUSIONNEE Le groupe capturant s'arretait au premier nombre : '(\d+)(?:er|ère|ème)?(?:\.\d+)*' capturait '1' pour "Article 1.1". Les sous-articles fusionnaient donc avec leur parent — une erreur de citation sur une base juridique. -> '(\d+(?:\.\d+)*)(?:er|ère|ème)?'. Verifie : 1, 1.1 (parent 1), 1.1.1 (parent 1.1), 2. 3. TITRES JAMAIS RENSEIGNES _extract_title etait definie mais appelee NULLE PART : title etait passe en dur a…a00940a
-
chore: initial standalone backend for JuriX API (Render)
9cf9a2a