Spaces:
Runtime error
Runtime error
Commit History
-
fix(chat): le flux fait ce que fait ask, avant que l'interface l'emprunte
POST /rag/ask/stream divergeait de /rag/ask : - il ignorait le document ouvert (law_id) et la reponse « article absent », et n'avait pas de recherche de repli ; - sans citation reconnue, il ne rendait aucune source ; - une reponse coupee par le budget n'etait pas signalee ; - un quota epuise ou une saturation arrivait comme une erreur quelconque. Il passe desormais par _retrieve_and_merge_context et _fallback_search, fabrique les memes sources de repli, ajoute la meme mention de troncature (generate_stream rapporte sa raison d'arret) et porte un error_code (quota, overloaded, server) : le statut HTTP d'un flux est fige a 200 des le premier octet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>5214b46
-
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: nom du fichier telecharge, image de production, flux non bloquant
Le fichier telecharge portait le nom brut du fichier aspire depuis prc.cm : "10691_decret-n-2026-164-du-4-mai-2026-portant-approbation-des-statuts-...". Le prefixe numerique est l'identifiant interne du site source et les tirets remplacent une ponctuation qui existait. Le nom propose est desormais construit sur le TITRE du document, accents compris — Content-Disposition les encode via filename* (RFC 5987) — en ne retirant que les caracteres interdits par les systemes de fichiers et en bornant la longueur. Dockerfile. poppler-utils et tesseract-ocr (avec les dictionnaires francais et anglais) sont installes : sans poppler, GET /laws/{id}/page/{n} — la visionneuse par images — repond 500 en production alors qu'il fonctionne en developpement, ou le binaire est sur la machine. TESSERACT_PATH pointe le binaire Linux. Les migrations sont jouees au demarrage : le schema du produit vit dans alembic (search_vector, index GIN, declencheurs, extensions) et non dans Base.metadata, donc un contene…4093877 -
fix(rag): le modele configure n'existait pas, et la recherche tombait au niveau loi
GEMINI_MODEL valait "gemini-3-flash", qui n'existe pas : l'API repond 404 NOT_FOUND. Le chat RAG ne pouvait donc produire AUCUNE reponse — chaque appel echouait avant meme d'atteindre le modele. Verifie contre ListModels sur ce compte, ou gemini-2.5-flash repond 404 lui aussi ; les modeles reellement disponibles pour generateContent sont gemini-3-flash-preview (retenu, le plus capable), gemini-3.1-flash-lite et gemini-2.5-flash-lite. Le chat repond desormais, avec ses sources. websearch_to_tsquery relie les mots par ET : "obligations dirigeants societe" exigeait les trois dans le MEME article, ce qu'aucun ne satisfaisait, alors que des dizaines d'articles parlent de societes. La recherche tombait donc directement au niveau LOI, et le RAG recevait un document entier sans identite d'article — la citation sortait sans numero et sans identifiant. Un second essai en OU est tente avant le repli niveau-loi ; ts_rank_cd continue de favoriser les articles portant le plus de termes, donc le rap…dfc6e36 -
feat(search): 3072 dimensions indexees via halfvec, recherche en deux etages
La migration precedente ramenait les embeddings a 1536 dimensions en partant du principe que 3072 etait inindexable. C'est vrai du type `vector`, plafonne a 2000 dimensions pour HNSW et IVFFlat. C'est FAUX du type `halfvec`, qui monte a 4000 — verifie sur ce serveur : pgvector 0.8.6, opclass hnsw/halfvec_cosine_ops presente, et pgvector.sqlalchemy expose HALFVEC. On garde donc la sortie native du modele, sans troncature : - la colonne repasse en vector(3072), fp32, valeurs exactes ; - l'index HNSW est pose sur l'EXPRESSION embedding::halfvec(3072), donc en fp16, ce qui le rend possible et divise sa taille par deux ; - la requete devient un deux-etages : parcours de l'index pour extraire des candidats sur-echantillonnes (x8, plancher 100, plafond 500), puis reclassement de ces candidats a la distance fp32 EXACTE. Le fp16 ne sert qu'a selectionner, jamais a classer. Quatre details portent le montage, et chacun a une variante fausse qui a l'air correcte : la distan…c6e37b4
-
fix(gemini): la generation bloquait la boucle d'evenements
GeminiService.generate est declaree async mais appelle client.models.generate_content, qui est SYNCHRONE : la boucle d'evenements etait gelee pendant tout l'aller-retour avec le modele — plusieurs secondes a chaque question posee au RAG, pendant lesquelles le serveur ne traitait plus aucune autre requete. Deport par asyncio.to_thread, comme le chemin des embeddings. generate_stream reste inchangee : la deporter demande un pont de file d'attente ou le passage a client.aio, ce qui est un autre chantier. Deux tests verifient que ni la generation ni l'encodage de la question ne s'executent sur le fil de la boucle. Suite : 491 passants, 9 ignores, 0 echec. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>ca74a6b -
test: couverture des comportements qui etaient silencieusement casses
69 tests nouveaux, chacun attache a une panne precise plutot qu'a une fonction. test_chunk_retrieval.py (12) — plusieurs articles d'une meme loi remontent ensemble (acte de deces du DISTINCT ON), l'ordre est celui de la pertinence (acte de deces du ORDER BY law_id), numero/section/page arrivent jusqu'au resultat, l'extrait est balise et centre sur le terme cherche, les filtres mordent sur la branche article, une ligne niveau-loi ne porte pas d'article_id, le semantique rend une ligne par article et des scores bornes, et les resultats document derives portent enfin leurs matched_articles et leur contenu. test_embedding_dimensions.py (11) — output_dimensionality est bien demande, les vecteurs sortent unitaires meme avec normalize=False (l'API ne normalise que la sortie pleine), une dimension inattendue ou un lot tronque levent au lieu de passer, et la cle de cache isole la dimension, le type de tache et la casse. Le test de lecture de cache insere une entree 3072 et verifie qu'elle est…cd71f3c