File size: 5,499 Bytes
81f30c1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
"""Fase 6 / Objetivo 7 — publicar la demo como Space de Gradio en HF Hub.

El Space sirve la pestaña **Buscar** sobre el índice publicado por `scripts/publish_hf.py`
(se descarga al arrancar vía `RAGSOLVER_HF_DATASET`); **Resolver** necesita Lean 4 + Mathlib
y una API key, así que en el Space muestra un aviso (sólo local). Sube `app.py`, el paquete
`ragsolver/`, `configs/`, `requirements.txt` y la tarjeta del Space, y con ellos el resto del repositorio que pide
la propuesta (docs, notebooks, tests, scripts, dependencias, LICENSE, CITATION), tras la misma comprobación de
contenido del PDF que `publish_hf.py` (SEC-2).

Desde 2026 los Spaces de Gradio en `cpu-basic` piden suscripción PRO (HTTP 402): con `--model` se sube lo mismo
como repositorio de modelo (gratis) y la demo se corre en local (`bash scripts/demo_up.sh`).

Uso: HF_TOKEN=... python scripts/publish_space.py --repo <usuario>/ragsolver --dataset <usuario>/ragsolver-index [--model] [--dry-run]
"""

from __future__ import annotations

import argparse
import json
import os
import sys
import tempfile
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent))
from check_no_leaked_content import check as _check_leaks  # noqa: E402

GRADIO = "6.28.0"  # la versión con la que se probó demo/app.py

REQUIREMENTS = """pydantic>=2.9
pyyaml>=6.0
lancedb>=0.17
pyarrow>=17.0
pandas>=2.2
sentence-transformers>=3.3
transformers>=4.51
torch>=2.4
accelerate>=1.0
huggingface_hub>=0.26
langgraph>=0.2
httpx
tenacity
"""

SPACE_YAML = """---
title: RagSolver
emoji: 🧮
colorFrom: indigo
colorTo: green
sdk: gradio
sdk_version: {gradio}
python_version: "3.12"
app_file: app.py
pinned: false
license: apache-2.0
---
"""
MODEL_YAML = """---
license: apache-2.0
tags: [lean4, mathlib, retrieval, rag, theorem-proving, olympiad, multi-agent]
datasets: [{dataset}]
---
"""
CARD = """# RagSolver — búsqueda de lemas de Mathlib (Lean 4, v4.28.0-rc1)

Recuperación híbrida (Qwen3-Embedding-0.6B + BM25, RRF) sobre {n_full} declaraciones de Mathlib
informalizadas por LeanSearch v2, con citas (nombre, módulo, enlace a la documentación).
Índice: [`{dataset}`](https://huggingface.co/datasets/{dataset}). El modo *Resolver* (agentes +
verificación con el kernel de Lean) requiere una instalación local: `docs/install.md`. {demo}

Este repositorio contiene el proyecto completo ([GitHub](https://github.com/camiloAndres11/RagSolver)):
`ragsolver/` (agentes, orquestador, recuperación, Lean), `configs/`, `docs/` (arquitectura, instalación, guía de
uso, interfaces, reproducción de cada ablación, informes de resultados e informe final), `notebooks/`, `tests/`,
`scripts/`, `pyproject.toml` + `uv.lock`, `LICENSE` (Apache-2.0) y `CITATION.cff`.
"""


def main() -> None:
    ap = argparse.ArgumentParser()
    ap.add_argument("--repo", required=True, help="<usuario>/ragsolver (Space)")
    ap.add_argument("--dataset", required=True, help="<usuario>/ragsolver-index (publicado con publish_hf.py)")
    ap.add_argument("--model", action="store_true", help="repositorio de modelo en vez de Space (sin PRO)")
    ap.add_argument("--dry-run", action="store_true")
    args = ap.parse_args()

    n_full = "278 160"
    variables = {"RAGSOLVER_HF_DATASET": args.dataset, "RAGSOLVER_MATHLIB_TABLE": "mathlib_full", "GRADIO_SERVER_NAME": "0.0.0.0"}
    files = {"app.py": "demo/app.py", **{f: f for f in ("LICENSE", "CITATION.cff", "pyproject.toml", "uv.lock")}}
    folders = {"ragsolver": "src/ragsolver", "configs": "configs", **{d: d for d in ("docs", "notebooks", "tests", "scripts")}}
    upload = [Path(f) for f in files.values()] + [f for d in folders.values() for f in Path(d).rglob("*") if f.is_file() and "__pycache__" not in f.parts]
    leaks = _check_leaks(upload)
    if leaks:
        raise SystemExit("SEC-2: texto del corpus informal en lo que se iba a subir — publish abortado:\n" + "\n".join(leaks))
    print(json.dumps({"space": args.repo, "variables": variables, "files": files, "folders": folders}, indent=2, ensure_ascii=False))
    if args.dry_run:
        return
    from huggingface_hub import HfApi

    api = HfApi(token=os.environ.get("HF_TOKEN"))
    repo_type = "model" if args.model else "space"
    api.create_repo(args.repo, repo_type=repo_type, exist_ok=True, **({} if args.model else {"space_sdk": "gradio"}))
    demo = "La demo (`app.py`, Gradio) se corre en local: `bash scripts/demo_up.sh` (`docs/user_guide.md`)." if args.model else ""
    card = (MODEL_YAML if args.model else SPACE_YAML).format(dataset=args.dataset, gradio=GRADIO) + CARD.format(n_full=n_full, dataset=args.dataset, demo=demo)
    with tempfile.TemporaryDirectory() as tmp:
        (Path(tmp) / "README.md").write_text(card, encoding="utf-8")
        (Path(tmp) / "requirements.txt").write_text(REQUIREMENTS, encoding="utf-8")
        for name in ("README.md", "requirements.txt"):
            api.upload_file(path_or_fileobj=str(Path(tmp) / name), path_in_repo=name, repo_id=args.repo, repo_type=repo_type)
    for dst, src in files.items():
        api.upload_file(path_or_fileobj=src, path_in_repo=dst, repo_id=args.repo, repo_type=repo_type)
    for dst, src in folders.items():
        api.upload_folder(folder_path=src, path_in_repo=dst, repo_id=args.repo, repo_type=repo_type, ignore_patterns=["__pycache__/*", "*.pyc"])
    for k, v in ({} if args.model else variables).items():
        api.add_space_variable(args.repo, k, v)
    print("ok")


if __name__ == "__main__":
    main()