ragsolver / scripts /check_no_leaked_content.py
CamiloS1111's picture
Upload folder using huggingface_hub
81f30c1 verified
Raw History Blame Contribute Delete
3.2 kB
"""Fase 2 del plan de corrección — comprobación por contenido (SEC-2): ningún
fragmento del corpus informal (texto de un documento con licencia desconocida,
`Document.redistributable=false`) debe aparecer en lo que se publica, ni en
git (docs/PLAN_CORRECCIONES.md Fase 2, tarea 2) ni en la copia que sube
`scripts/publish_hf.py` (tarea 3) — misma función, dos llamadores.
Uso: python scripts/check_no_leaked_content.py [--root .] [--informal data/corpus/informal.jsonl]
"""
from __future__ import annotations
import json
import subprocess
from pathlib import Path
def pdf_text_fingerprints(informal_jsonl: str | Path, n_chars: int = 60) -> list[str]:
"""Primeros `n_chars` del `content_md` de cada card/figura del corpus
informal: huellas cortas y específicas — alcanzan para detectar una fuga
sin que el propio script de comprobación termine guardando el contenido
protegido."""
path = Path(informal_jsonl)
if not path.exists():
return []
prints = []
for line in path.read_text(encoding="utf-8").splitlines():
if not line.strip():
continue
text = (json.loads(line).get("content_md") or "").strip()
if len(text) >= n_chars:
prints.append(text[:n_chars])
return prints
def find_leaks(fingerprints: list[str], files: list[Path]) -> dict[str, list[str]]:
"""{archivo: [huellas encontradas]} sólo para los archivos con alguna coincidencia."""
leaks: dict[str, list[str]] = {}
for f in files:
try:
text = f.read_text(encoding="utf-8", errors="ignore")
except OSError:
continue
hits = [fp for fp in fingerprints if fp in text]
if hits:
leaks[str(f)] = hits
return leaks
def git_committable_files(root: str | Path = ".") -> list[Path]:
"""Lo mismo que incluiría `git add .`: trackeados + no ignorados (respeta
`.gitignore`); usado por la tarea 2 (comprobación antes de un commit)."""
root = Path(root)
out = subprocess.run(["git", "-C", str(root), "ls-files", "-co", "--exclude-standard"], capture_output=True, text=True, check=True)
return [root / p for p in out.stdout.splitlines() if p.strip() and (root / p).is_file()]
def check(files: list[Path], informal_jsonl: str | Path = "data/corpus/informal.jsonl") -> dict[str, list[str]]:
"""Punto de entrada compartido: huellas del corpus informal contra `files`."""
return find_leaks(pdf_text_fingerprints(informal_jsonl), files)
if __name__ == "__main__":
import argparse
ap = argparse.ArgumentParser(description=__doc__)
ap.add_argument("--root", default=".")
ap.add_argument("--informal", default="data/corpus/informal.jsonl")
args = ap.parse_args()
files = git_committable_files(args.root)
leaks = check(files, args.informal)
if leaks:
print(f"FUGA: {len(leaks)} archivo(s) contienen texto del corpus informal:")
for f, hits in leaks.items():
print(f" {f}: {len(hits)} coincidencia(s)")
raise SystemExit(1)
n_fp = len(pdf_text_fingerprints(args.informal))
print(f"OK: 0 coincidencias en {len(files)} archivos ({n_fp} huellas comprobadas)")