Spaces:
Runtime error
Runtime error
Claude Opus 5.5
feat(embeddings): voyage-4-large remplace EmbeddingGemma (API Voyage, halfvec 1024)
57c8117 Download scripts/eval/validate_slicing.py from rahim237cm/jurix-api: direct link, hf CLI and curl.
- Browser
- Download file 4.86 kB
-
https://huggingface.co/spaces/rahim237cm/jurix-api/resolve/main/scripts/eval/validate_slicing.py
- Command line
-
hf download hf://spaces/rahim237cm/jurix-api/scripts/eval/validate_slicing.py
-
curl -L -o validate_slicing.py https://huggingface.co/spaces/rahim237cm/jurix-api/resolve/main/scripts/eval/validate_slicing.py
4.86 kB
| """ | |
| Verifie que tronquer un vecteur 3072 puis le renormaliser donne bien le vecteur | |
| que l'API produit a la dimension inferieure. | |
| C'est l'hypothese qui permet de comparer 3072, 1536 et 768 sans re-embedder le | |
| corpus a chaque dimension : gemini-embedding-001 est entraine en Matryoshka, et | |
| output_dimensionality est cense n'etre qu'une troncature. Si l'hypothese est | |
| fausse, le balayage coute trois passes completes sur le corpus au lieu d'une. | |
| 40 appels d'API : 20 textes, a 3072 puis a la dimension cible. Le script | |
| interroge TOUJOURS Gemini, quel que soit EMBEDDING_PROVIDER : la propriete | |
| verifiee est celle de gemini-embedding-001. | |
| Usage: | |
| python -m scripts.eval.validate_slicing --dim 1024 --samples 20 | |
| """ | |
| import argparse | |
| import asyncio | |
| import logging | |
| import sys | |
| from typing import Optional, Sequence | |
| import numpy as np | |
| sys.path.insert(0, ".") | |
| from app.services.embedding_service import EmbeddingService, GeminiProvider | |
| logger = logging.getLogger("validate_slicing") | |
| TEXTS = [ | |
| "Les dirigeants sociaux sont responsables des fautes de gestion.", | |
| "La présente loi fixe le régime des sociétés commerciales au Cameroun.", | |
| "Le Président de la République est le Chef de l'État.", | |
| "Toute personne a droit à la reconnaissance de sa personnalité juridique.", | |
| "Les contrats légalement formés tiennent lieu de loi à ceux qui les ont faits.", | |
| "L'action en responsabilité se prescrit par trois ans.", | |
| "Est puni d'un emprisonnement de un à cinq ans quiconque détourne des fonds publics.", | |
| "The Commercial Code governs the incorporation of companies.", | |
| "Le capital social minimum est fixé par décret.", | |
| "La procédure de redressement judiciaire est ouverte au débiteur en cessation de paiement.", | |
| "Les fonctionnaires sont soumis au statut général de la fonction publique.", | |
| "Le mariage est célébré publiquement devant l'officier d'état civil.", | |
| "Toute décision de justice doit être motivée.", | |
| "Le contribuable dispose d'un délai de trente jours pour contester.", | |
| "L'expropriation pour cause d'utilité publique donne lieu à indemnisation.", | |
| "Les délais de recours courent à compter de la notification.", | |
| "La société anonyme est constituée entre deux associés ou plus.", | |
| "L'employeur est tenu d'assurer la sécurité de ses salariés.", | |
| "Les biens sont meubles ou immeubles.", | |
| "La nationalité camerounaise s'acquiert par filiation ou par naturalisation.", | |
| ] | |
| def main(argv: Optional[Sequence[str]] = None) -> int: | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| parser.add_argument("--dim", type=int, default=1024) | |
| parser.add_argument("--samples", type=int, default=20) | |
| parser.add_argument("--threshold", type=float, default=0.999) | |
| args = parser.parse_args(argv) | |
| if args.dim >= GeminiProvider.native_dim: | |
| parser.error(f"--dim doit etre inferieur a {GeminiProvider.native_dim}") | |
| logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s", | |
| datefmt="%H:%M:%S") | |
| texts = TEXTS[: args.samples] | |
| # Gemini explicitement, et la reference a SA dimension native. Laissee a | |
| # EMBEDDING_DIM (1024 depuis Voyage), elle comparerait 1024 a 1024 et | |
| # conclurait toujours « decoupage valide », sans rien avoir verifie. | |
| native = EmbeddingService(use_cache=False, provider=GeminiProvider()) | |
| native.EMBEDDING_DIM = GeminiProvider.native_dim | |
| reduced = EmbeddingService(use_cache=False, provider=GeminiProvider()) | |
| reduced.EMBEDDING_DIM = args.dim | |
| async def _run() -> int: | |
| similarities = [] | |
| for text_sample in texts: | |
| full = await native.generate_embedding_async( | |
| text_sample, task_type=EmbeddingService.TASK_DOCUMENT | |
| ) | |
| api_small = await reduced.generate_embedding_async( | |
| text_sample, task_type=EmbeddingService.TASK_DOCUMENT | |
| ) | |
| sliced = full[: args.dim] | |
| sliced = sliced / np.linalg.norm(sliced) | |
| similarities.append(float(np.dot(sliced, api_small))) | |
| worst = min(similarities) | |
| logger.info("Cosinus min %.6f, median %.6f sur %s textes", | |
| worst, float(np.median(similarities)), len(similarities)) | |
| if worst < args.threshold: | |
| logger.error( | |
| "Le decoupage local NE reproduit PAS la sortie de l'API a %s dimensions.\n" | |
| " Comparer les dimensions exige alors de re-embedder le corpus a chacune.", | |
| args.dim, | |
| ) | |
| return 1 | |
| logger.info( | |
| "Decoupage valide : les dimensions inferieures s'obtiennent localement, " | |
| "sans appel supplementaire." | |
| ) | |
| return 0 | |
| return asyncio.run(_run()) | |
| if __name__ == "__main__": | |
| sys.exit(main()) | |