Download tests/test_enrich.py from CamiloS1111/ragsolver: direct link, hf CLI and curl.
- Browser
- Download file 4.67 kB
-
https://huggingface.co/CamiloS1111/ragsolver/resolve/main/tests/test_enrich.py
- Command line
-
hf download hf://CamiloS1111/ragsolver/tests/test_enrich.py
-
curl -L -o test_enrich.py https://huggingface.co/CamiloS1111/ragsolver/resolve/main/tests/test_enrich.py
4.67 kB
| """Lógica del enriquecedor que no depende de la red: cadena de modelos por cuota | |
| diaria, caché con modelo, y guardas de aceptación de la QA de fórmulas.""" | |
| from __future__ import annotations | |
| import json | |
| import pytest | |
| from ragsolver.rag import enrich | |
| from ragsolver.rag.enrich import DailyQuotaExhausted, Enricher | |
| def enricher(tmp_path, monkeypatch): | |
| monkeypatch.setenv("GEMINI_API_KEY", "test-key") | |
| e = Enricher(tmp_path / "enrich") | |
| e.llm.models = ["model-a", "model-b", "model-c"] | |
| e.llm._model_idx = 0 | |
| return e | |
| def _fake(enricher, monkeypatch, fn): | |
| """Reemplaza la llamada HTTP: fn(model, prompt, schema, images, system, max_tokens) -> texto.""" | |
| from ragsolver.llm import LLMResponse | |
| monkeypatch.setattr(enricher.llm, "_generate_with", lambda model, prompt, schema, images, system, max_tokens: LLMResponse(fn(model, prompt, schema), model)) | |
| def test_daily_quota_moves_to_next_model_and_records_it(enricher, monkeypatch): | |
| seen = [] | |
| def fake(model, prompt, schema): | |
| seen.append(model) | |
| if model == "model-a": | |
| raise DailyQuotaExhausted(model) | |
| return json.dumps({"first_line": "x = 1"}) | |
| _fake(enricher, monkeypatch, fake) | |
| assert enricher.split_solution(55, "texto", "p54", "p55") == "x = 1" | |
| assert seen == ["model-a", "model-b"] | |
| assert enricher.exhausted == ["model-a"] and enricher.model == "model-b" | |
| # la caché guarda el modelo que produjo el resultado y se reutiliza sin llamar | |
| rec = json.loads((enricher.cache_dir / "split.jsonl").read_text().splitlines()[0]) | |
| assert rec["model"] == "model-b" | |
| assert enricher.split_solution(55, "texto", "p54", "p55") == "x = 1" and seen == ["model-a", "model-b"] | |
| def test_all_models_exhausted_raises(enricher, monkeypatch): | |
| _fake(enricher, monkeypatch, lambda m, p, s: (_ for _ in ()).throw(DailyQuotaExhausted(m))) | |
| with pytest.raises(DailyQuotaExhausted): | |
| enricher.transcribe_region(_png(enricher.cache_dir)) | |
| assert enricher.exhausted == ["model-a", "model-b"] | |
| def test_qa_formulas_guards(enricher, monkeypatch): | |
| """Casos reales del PDF de ejemplo (docs/DECISIONS.md #19): qué acepta y qué no.""" | |
| cases = [ # (original, propuesta, inline, se_acepta) | |
| ("6 0 ^ { \\circ } . ~ { \\dot { \\omega } } \\mathrm { C u a l }", "60^{\\circ}", True, True), # basura absorbida | |
| ("^ { 1 , 4 , 9 , 1 6 , \\ldots \\ldots }", "1,4,9,16,......", True, True), # superíndice espurio | |
| ("x ^ { 2 } y ^ { 5 } z ^ { \\hat { 1 0 } } ?", "x ^ { 2 } y ^ { 5 } z ^ { 1 0 }", True, True), # acento espurio | |
| ("r ^ {2} + (r _ {l}) ^ {2}", "r ^ {2} + (r _ {1}) ^ {2}", False, True), # l -> 1: arreglo local | |
| ("= \\left( = a \\cdot 10^{3} + b", "abcd = \\left( = a \\cdot 10^{3} + b", False, True), # display puede agregar | |
| ("a = 9 ,", "a = 9,,", True, False), # sólo puntuación | |
| ("A B = 3", "AB = 3 \\text{ cm}", True, False), # inline absorbe texto vecino | |
| ("C _ { 1 0 0 } ^ { 2 0 0 } ~ \\mathrm { e s } ~ 6 1", "C _ { 1 0 0 } ^ { 2 0 0 }", True, False), # pierde la respuesta | |
| ("a ^ { 2 } + b ^ { 2 } \\mathrm { ~ y ~ } a + b ,", "a ^ { 2 } + b ^ { 2 }", True, False), # pierde matemática | |
| ("9 ( b c d a )", "( b c d a )", False, False), # borrar dígitos: conservador | |
| ("n \\in \\mathbb { Z }", "n ∈ ℤ", True, False), # unicode en vez de LaTeX | |
| ("\\underset { n } { ( 9 9 9 ) ^ { 2 } }", "\\raw { ( 9 9 9 ) ^ { 2 } }", True, False), # LaTeX destrozado | |
| ("S = n + \\dots + k", "S = n + ... + k", False, False), # cosmético | |
| ] | |
| _fake(enricher, monkeypatch, lambda m, p, s: json.dumps({"items": [{"i": i, "latex": c[1], "changed": True} for i, c in enumerate(cases)]})) | |
| out = enricher.qa_formulas(_png(enricher.cache_dir), [c[0] for c in cases], inline=[c[2] for c in cases]) | |
| assert [o["changed"] for o in out] == [c[3] for c in cases] | |
| assert out[6]["rejected"] == "AB = 3 \\text{ cm}" | |
| assert "rejected" not in out[5] # cosmético: ni cambio ni rechazo registrado | |
| def test_is_daily_quota_parses_google_error(): | |
| class R: | |
| def json(self): | |
| return {"error": {"details": [{"violations": [{"quotaId": "GenerateRequestsPerDayPerProjectPerModel-FreeTier"}]}]}} | |
| class R2: | |
| def json(self): | |
| return {"error": {"details": [{"violations": [{"quotaId": "GenerateRequestsPerMinutePerProjectPerModel"}]}]}} | |
| from ragsolver import llm | |
| assert llm._is_daily_quota(R()) is True | |
| assert llm._is_daily_quota(R2()) is False | |
| def _png(d): | |
| p = d / "x.png" | |
| p.write_bytes(b"\x89PNG\r\n\x1a\n") | |
| return p | |