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
26 days ago
-
feat(personas): une seule liste des profils, alias sans accent, profil general
La liste des profils vivait en trois copies : le validateur du chat (schemas/rag.py), PersonaService.VALID_PERSONAS et les cles des prompts. Une valeur inconnue y recevait trois traitements : 422, 400, ou repli silencieux sur le prompt citoyen (« etudiant » sans accent). app/core/personas.py, sans import applicatif, porte desormais : - PERSONAS, les 4 profils proposes, « étudiant » en NFC ; - PROFIL_GENERAL (« general »), la valeur d'un utilisateur qui n'a pas repondu ; - normaliser_profil : casse, accents et forme NFD ignores, vide = general ; - normaliser_persona : les 4 seulement, pour un compte, qui ne garde jamais un refus. Aucun changement de comportement a cette etape. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>0a62da7
30 days ago
-
fix(limiteur): attente bornee, 429 retenus, mentions dans la langue de la reponse
Issus de la revue de la branche : - L'attente d'une place de generation (4 par modele) n'avait pas de limite : quand toutes etaient prises par des comparaisons longues, un message du chat attendait des minutes, sans secours ni 503. Elle est bornee par l'attente acceptee ; au-dela, le modele de secours. - Mistral : un Retry-After plus long que le budget, ou recu au dernier essai, n'etait pas inscrit au limiteur ; l'appel suivant retournait frapper le modele sanctionne. Il est inscrit avant de decider d'abandonner. - Un `repousser` (429) ne retenait pas les appels deja endormis sur un depart reserve : ils partaient en pleine penalite. Ils attendent l'echeance. - Le recalage sur les en-tetes de Groq s'inscrivait en queue de fenetre, apres des departs reserves pour plus tard, et le calcul du prochain depart faisait attendre trop (88 s au lieu de 59). Il s'inscrit a sa place. Groq corrige l'usage reel AVANT de recaler, sinon l'estimation masquait la consommation des…ecf0802 -
docs(config): .env.example aligne sur Settings, seed aux 14 domaines, README a jour
.env.example : - chaque reglage de Mistral et de Groq y figure, avec les plafonds verifies du palier gratuit ; MISTRAL_MODEL suit le defaut de config.py (14b) ; INTENT_CLASSIFIER n'annonce plus « local » ; - plus aucun commentaire en fin de ligne : docker --env-file le garde dans la valeur, et « groq # groq | local » etait refuse au demarrage ; - l'extraction des PDF annonce Docling, son moteur par defaut. tests/test_core/test_env_example.py verifie que chaque cle est un reglage (Settings ignore en silence une cle inconnue), qu'aucune ligne ne porte de commentaire, que les valeurs actives se chargent et que chaque reglage des fournisseurs est documente. scripts/seed_database.py semait cinq categories aux identifiants fixes et aux noms d'avant la migration 013 : sur une base migree, une quinzieme categorie, et des lois rattachees a l'identifiant 1, 2 ou 3, quel qu'il soit. Il cree les 14 domaines manquants par leur nom et rattache ses lois par le nom. Textes perimes : « 12 cat…deb8048 -
feat(limiteur): limiteur de debit partage pour les API externes
Les paliers gratuits sont etroits (Groq : 30 requetes et 8 000 jetons par minute, 1 000 requetes et 200 000 jetons par jour, par modele ; Mistral ministral-14b : 30 requetes par minute) et chaque depassement coutait une requete perdue plus une attente imposee. app/core/limiteur.py fait attendre AVANT d'envoyer : - debit par seau a jetons (GCRA), avec rafale ; - fenetres glissantes : jetons par minute, jetons d'entree par minute, requetes et jetons par jour ; - repousser() apres un 429, recaler() sur les en-tetes x-ratelimit-*, qui voient aussi les appels des autres processus ; - attente_max : au-dela, AttenteTropLongue sans rien consommer ; - un semaphore de concurrence par boucle d'evenements. Horloge et sommeil injectables : les 33 tests tournent sans aucun vrai sleep. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>9a31d98
-
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
-
feat: deployment on free tiers, object-storage documents, and email
Enables the project to run on a free hosting tier — ephemeral disk, managed Postgres behind a connection pooler, no outbound SMTP — and adds the password reset flow that was previously a dead end. Two blocking deployment bugs, both reproduced rather than deduced: DATABASE_URL with ?sslmode=require TypeError: connect() got an unexpected keyword argument 'sslmode' SQLAlchemy's asyncpg dialect forwards unknown query parameters straight to asyncpg.connect(), whose signature accepts neither sslmode nor channel_binding and has no **kwargs. The failure surfaces only on the first database access, long after an apparently successful start — and that is exactly the URL the "copy" buttons of managed Postgres providers hand out, and what .env.example recommended. Settings now refuse such a URL at load time, naming PGSSLMODE as the remedy. alembic/env.py, swapping asyncpg for psycopg2 but keeping the query string ProgrammingError: invalid dsn: invalid URI query parameter prepared_s…6429ad3
-
feat(auth): inscription publique, connexion Google, et conversations rattachees a un compte
L'utilisateur peut desormais creer un compte — nom, adresse, mot de passe — ou se connecter par Google, afin de retrouver ses conversations d'une visite a l'autre. PAS DE FIREBASE. Le navigateur obtient un jeton d'identite de Google, ce serveur le verifie avec google-auth, puis emet le JWT maison. Une dependance, zero paquet npm, et surtout UN SEUL fichier d'utilisateurs — Firebase en imposerait deux, a garder synchronises. L'ESCALADE DE PRIVILEGE EST FERMEE PAR CONSTRUCTION. `UserCreate` porte un champ `role` fourni par le client ; le reutiliser pour une route publique aurait laisse n'importe qui poster {"role": "superadmin"}. `SignupRequest` n'herite pas de `UserBase`, donc ce champ n'existe pas, et `extra="forbid"` fait de son envoi un 422 plutot qu'un silence — la tentative laisse une trace. LE TROU DE CONFIDENTIALITE EST FERME, et c'etait la condition pour promettre quoi que ce soit. GET et DELETE /rag/conversations/{sid} n'avaient aucune authentification ni controle d'apparten…d21ea1e
-
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