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(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
-
fix(embeddings): un quota journalier ne doit pas etre rejoue
Google renvoie 429 aussi bien pour un quota par minute que par jour, et suggere un retryDelay d'une trentaine de secondes dans les deux cas. Seul le quotaId les distingue : EmbedContentRequestsPerDayPerUserPerProjectPerModel-FreeTier, limite 1000 Pour un quota journalier, ce delai est trompeur : les trois tentatives brulent une minute avant d'echouer de toute facon, puis le lot suivant recommence, et ainsi de suite pour tout le corpus. Constate en ingerant le corpus reel — chaque document perdait une trentaine de secondes en reprises vouees a l'echec. QuotaExhaustedError est levee immediatement, sans reprise. Le pipeline la distingue d'une panne : le document reste correctement decoupe et indexe en plein texte, seuls les vecteurs manquent, et ils se rattrapent plus tard. Le script de backfill s'arrete net en annoncant combien d'articles restent et quand le quota se reinitialise, au lieu d'echouer lot apres lot. Quatre tests : reconnaissance du quota journalier, non-reconnaissance…b9769d8 -
fix(gemini): aucun delai maximal sur les appels, blocage indefini possible
genai.Client etait construit sans http_options, donc sans le moindre delai. Constate en conditions reelles pendant l'ingestion du corpus : le processus est reste 40 MINUTES endormi sur une lecture de prise reseau au milieu d'un lot d'embeddings — vivant, 16 secondes d'UC en une heure, aucune trace. Comme aucune exception n'est levee, la boucle de reprise du service ne se declenche jamais : le blocage est silencieux et sans fin. settings.GEMINI_TIMEOUT_S (120 s) est applique aux deux clients, celui des embeddings et celui de la generation. Un appel qui depasse leve desormais, ce qui reactive les reprises deja en place. Deux tests verifient que le delai est bien pose sur chaque client. Ajout de scripts/ingest_corpus.py, qui a servi a trouver le probleme : ingestion d'un echantillon graine du corpus prc.cm par la chaine complete (copie, extraction LlamaParse, decoupage, embeddings, tsvector). Echantillonnage stratifie a 70 % de textes normatifs : un tirage uniforme donne 52,6 % de decr…7dc0e8b -
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