Spaces:
Runtime error
Runtime error
Commit History
21 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
23 days ago
-
feat(chat): chaque extrait dit sa date et si un texte plus recent l'a change
Le corpus garde chaque texte dans sa version publiee : le Code electoral de 2012 tel quel, et a cote la loi de 2026 qui en reecrit des articles. La recherche remontait souvent l'ancien article, et le chat le citait comme la regle en vigueur (« l'article 38 de la Loi N°2004/004 » pour le constat de la vacance de la presidence, reecrit en 2026). Archiver les textes anciens n'etait pas la reponse : la plupart de leurs articles restent applicables. - app/services/liens_entre_textes.py : 67 liens verifies (texte touche, texte recent, nature, articles, portee), chacun avec sa preuve citee : Constitution, Code electoral, Conseil constitutionnel, Cour supreme, traite OHADA, Code penal, Code civil (etat civil, suretes, vente et bail commerciaux, prescription...), liberte d'association, cybersecurite, foncier, incitations a l'investissement, lois de finances. Releves en base, contre-verifies, relus par une revue adversariale ; les liens ecartes le sont avec leur raison. - app/serv…c193c4e
24 days ago
-
fix(ingestion): l'annee du numero est celle de l'acte, pas d'un texte modificatif
« Decret N°94/199 du 07 octobre 1994 portant…, modifie et complete par le decret n°2000/287 du 12 octobre 2000 » etait date du 12 octobre 2000 : le numero cherche dans tout le titre etait celui du texte modificatif, et la vraie date, trop loin de son annee, etait rejetee. Le numero est desormais lu avant l'objet de l'acte. Sur les 2287 titres des deux index, seule cette date change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>34508a7 -
feat(ingestion): textes venus d'ailleurs que prc.cm, Actes uniformes OHADA
Le corpus complementaire (Actes uniformes OHADA, Code CIMA, lois que prc.cm ne sert pas) passe par la meme ingestion que le reste. - L'index peut donner la reference du texte (« OHADA-AUA-2017 », « LOI-1992-007 ») ; sans elle, `PRC-{doc_id}` comme avant. Un Acte uniforme ne pretend plus venir de la Presidence. - « Acte uniforme » devient un type d'acte, en dernier dans la recherche sur tout le titre : « Decret portant ratification de l'Acte uniforme… » reste un decret. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>c148f31
26 days ago
-
fix(deploiement): corrections de la seconde revue
Revue adversariale du deploiement, trouvailles verifiees une a une. - demarrer.sh : un psql en echec dans la condition d'un if passait pour une base sans table, donc a restaurer. Une mise a jour pouvait ainsi faire DROP DATABASE de la production, puis restaurer le corpus du premier jour, sans un compte. La reponse doit etre t ou f, sinon arret. Le dump initial est renomme apres une restauration reussie : plus aucune mise a jour ne peut le rejouer. Un temoin fait reprendre une restauration interrompue. Une sauvegarde precede chaque mise a jour. - ADMIN_PASSWORD : docker compose interprete $, # et les guillemets ; « Jurix2026$admin » arrivait sous la forme « Jurix2026 ». Refuses. - create_admin.py promouvait superadmin un compte ordinaire deja inscrit a ADMIN_EMAIL, avec son propre mot de passe, alors que le site est public avant la creation de l'administrateur. Refus sans --promouvoir, qui remplace alors le mot de passe. Le test qui devait prouver « aucun acces a …7cf23f7
29 days ago
-
fix(deploiement): trois pannes trouvees par l'essai de bout en bout
Essai complet de demarrer.sh dans une copie isolee (https://localhost) : base neuve, migrations depuis zero, relance a l'identique, restauration d'un dump sur un schema deja cree, sauvegarde. - demarrer.sh s'arretait sur une base neuve, avant toute restauration : PostgreSQL resout `laws` a l'analyse, meme dans la branche non prise du CASE. Deux requetes a la place. Le premier deploiement aurait echoue. - Les redirections de l'API (barre oblique finale) partaient vers http:// : uvicorn ne croyait pas les en-tetes X-Forwarded-* de Caddy, et le navigateur bloque une redirection vers http depuis une page https. FORWARDED_ALLOW_IPS="*" : sans danger, seul Caddy joint l'API. - Un ADMIN_PASSWORD hors politique (sans majuscule) faisait echouer create_admin.py, qui affichait l'erreur Pydantic entiere, mot de passe en clair compris. Le script n'affiche plus que le champ et la regle, et demarrer.sh verifie la politique des l'etape 1, avant vingt minutes de construction. Aussi …6e91d61
30 days ago
-
fix(reclassement): la simulation montre tout ce que --force ecrira
Issus de la revue de la branche : - `appliquer --dry-run` sans --force n'inscrivait pas dans changements.csv les categories deja posees que le verdict contredit : sur le corpus, ou toutes les lois en ont une, on relisait un fichier presque vide avant un --force qui les reecrivait toutes. changements.csv liste desormais toute loi dont la categorie changerait, avec sa suite (appliquee, ou gardee faute de --force), et le README recommande `--dry-run --force`. - Une relance `--incertains` apres un arret sur quota repayait les lois deja retentees avec un extrait : elle les saute. - Une ligne de journal coupee par un arret brutal absorbait la ligne suivante : le journal est termine par un saut de ligne avant d'ecrire. - Le journal est indexe par l'identifiant de la loi : chaque verdict porte desormais sa reference, et celui d'une autre loi (base reconstruite) n'est ni repris ni applique. - L'anomalie « Classement indisponible » posee par le pipeline est effacee une foi…f508b99 -
feat(reclassement): reclassement par lots en deux temps, reprenable
scripts/maintenance/reclassify_domains.py est reecrit : - `classer` interroge Groq par lots (40 lois par requete, titres seuls ; puis les incertains avec l'article premier, 15 par requete) et ecrit les verdicts dans data/reclassement/verdicts.jsonl : domaine, secondaires, confiance, modele, version des consignes, jetons. Rien n'est ecrit en base. Une relance saute les lois deja classees ; une ligne tronquee par une coupure est ignoree. Quota epuise : arret propre, code 4, avec l'heure de reprise ; panne : code 3 ; une saturation courte est attendue sur place. - `appliquer` ecrit categorie, confiance et suggestions, par paquets de 200. Un verdict « a revoir » ou sous le seuil (--seuil, 0,6) n'est jamais applique : il part dans a_revoir.csv. Sans --force, une categorie deja posee est gardee. --dry-run n'ecrit rien en base et produit le rapport, changements.csv et a_revoir.csv, a relire avant d'appliquer pour de bon. L'ancienne version rangeait en Droit Administr…43190dd -
feat(groq): classement des lois par lots, sans repli silencieux, et suite isolee du reseau
GroqService (app/services/groq_service.py) : - chaque appel passe par le limiteur de son modele (les quotas Groq sont par modele) ; un 429 est respecte (Retry-After) puis rejoue ; une attente imposee de plus de GROQ_ATTENTE_MAX_429_S, ou un message de quota journalier, ferme le modele jusqu'a l'echeance (GroqQuotaError, sans requete) ; 5xx et pannes reseau : 4 essais ; - erreurs typees au lieu de None : GroqLimiteError, GroqQuotaError, GroqIndisponibleError, GroqReponseInvalideError ; - schema JSON strict, max_completion_tokens, temperature 0, reflexion coupee (qwen : « none ») ou reduite et masquee (gpt-oss) ; - la sonde de sante lit /models/{modele} au lieu de generer. Classement des lois (legal_domain_classifier.py) : - par LOTS de documents numerotes, codes courts en sortie : 2 226 lois une par une demandaient 11 jours de quota ; un lot incomplet est redemande (les manquants, ou le lot coupe en deux), un document qui manque encore est « a revoir » ; - modele prop…1a3d3f5
-
fix(ingestion): les doublons se reconnaissent sur tout le corpus, pas sur le lot
La reindexation passe par lots de 50 documents (--doc-id). Les doublons (meme PDF sous deux numeros d'acte) etaient determines sur la seule selection : PRC-10016, jumeau du 10018 reste dans un autre lot, a passe pour unique, est parti en traitement sans fichier, et son echec a arrete la reindexation entiere. Le canonique se choisit desormais sur le corpus complet, quelle que soit la selection traitee. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>2d6e405 -
fix(vecteurs): l'index HNSW se reconstruit dans un conteneur Docker
En parallele, pgvector construit le graphe en memoire partagee dynamique, dans /dev/shm, que Docker limite a 64 Mo par defaut. Sur le corpus (20 394 vecteurs), REINDEX echouait : « could not resize shared memory segment ... No space left on device ». La construction se fait desormais en serie, dans la memoire du processus : 8 s pour le corpus. Un REINDEX CONCURRENTLY interrompu laissait une copie invalide de l'index (_ccnew), maintenue a chaque ecriture : elle est supprimee sur echec. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>5a82781
-
feat(ingestion): le corpus en deux passes reprenables
Passe 1, scripts/extraire_corpus.py : Docling seul, sans base. Le travail se fait dans un processus enfant (spawn) recycle tous les 25 documents ou au-dela de 5 Go : la memoire de Docling grossit au fil des pages. Le superviseur compte une tentative au lot pendant lequel l'enfant meurt, tue un enfant bloque, s'arrete sur une panne generale (initialisation, echecs en serie) ou si .env et les paquets ont change depuis le lancement — sans quoi l'enfant ecrirait dans un autre cache et la boucle ne finirait pas. Arret propre par data/ingestion/STOP ; suivi dans progression.json. Passe 2, scripts/ingest_corpus.py : ne relit que le cache, jamais Docling. Reprend les lois pending, processing et refused ; une panne de base est rejouee avec attente croissante au lieu de compter chaque document en echec. Les PDF identiques sous deux numeros d'acte sont indexes une fois, l'autre signale. Type et date de l'acte relus dans le titre, l'annee controlee par le numero d'acte. Pipeline (process_law) : …1be6163 -
feat(embeddings): schema en 768 dimensions, index HNSW sur la colonne
La colonne articles.embedding passe de vector(3072) a vector(768) : la sortie native d'EmbeddingGemma, et une dimension que gemini-embedding-001 produit aussi. Un seul schema pour les deux fournisseurs. Le fournisseur par defaut reste gemini : ce commit ne change que le schema et ce qui en depend. LA MIGRATION DETRUIT TOUS LES VECTEURS (c4d5e6f7a8b9). C'est inevitable : un vecteur 3072 n'a pas d'equivalent dans l'espace d'un autre modele. Ici les bases etaient vides. Une base peuplee devra etre regeneree ensuite, et comme le conteneur lance `alembic upgrade head` a chaque demarrage, deployer ce code declenche la purge. La procedure est a ecrire avant tout deploiement. L'index HNSW se pose directement sur la colonne, sans le detour par une expression halfvec. Les anciens index sont supprimes AVANT l'ALTER : laisse en place, l'index halfvec survit a la conversion et ne sert plus a rien. UN PIEGE TROUVE EN CHEMIN, mesure par EXPLAIN. La recherche garde deux etages : l'index fournit des…195c1c3