ig-classifier / app.py
bravo-pena's picture
Upload app.py with huggingface_hub
4010d75 verified
Raw
History Blame Contribute Delete
3.88 kB
from __future__ import annotations
import os
import tempfile
from pathlib import Path
import gradio as gr
from ig_classifier.model_hub import load_model, model_version_string
from ig_classifier.pipeline import run_file
MODEL_ID = os.environ.get("IG_CLASSIFIER_MODEL", "bravo-pena/ig-classifier-1.0")
_MODEL = None
_META: dict = {}
DEPTH_CHOICES = [
("1 — Rule type (AGG / BOU / CHO / INF / PAY / POS / SCO)", 1),
("2 — Subtype", 2),
("3 — Category", 3),
("4 — Full detail (recommended)", 4),
]
def get_model():
global _MODEL, _META
if _MODEL is None:
_MODEL, _META = load_model(MODEL_ID)
return _MODEL, _META
def classify(file_obj, technical_obj, depth):
if file_obj is None:
raise gr.Error("Upload the Paso 1 Excel first (the IS Identifier output).")
input_path = Path(file_obj.name)
if input_path.suffix.lower() != ".xlsx":
raise gr.Error("Expected a .xlsx file — the Excel produced by IS Identifier (Paso 1).")
technical_path = None
if technical_obj is not None:
technical_path = Path(technical_obj.name)
if technical_path.suffix.lower() != ".json":
raise gr.Error("The technical sidecar must be the *_technical.json file from Paso 1.")
model, meta = get_model()
with tempfile.TemporaryDirectory() as tmpdir:
output_path = Path(tmpdir) / f"{input_path.stem}_paso2.xlsx"
try:
written = run_file(
input_path=input_path,
output_path=output_path,
model=model,
model_version=model_version_string(MODEL_ID, meta),
depth=int(depth),
technical_path=technical_path,
)
except ValueError as exc:
raise gr.Error(
f"{exc} — is this the Paso 1 Excel (sheet 'segments' with AIM candidates)?"
) from exc
final_path = Path(tempfile.gettempdir()) / written.name
final_path.write_bytes(written.read_bytes())
return str(final_path)
with gr.Blocks(title="IG Classifier — Paso 2") as demo:
gr.Markdown("# IG Classifier — Paso 2 (interim model)")
gr.Markdown(
"Upload the Excel produced by "
"[IS Identifier (Paso 1)](https://huggingface.co/spaces/bravo-pena/is-identifier) "
"and download it back with the official Rules-taxonomy applied to every "
"institutional-statement candidate: TYPE / TAXON / LINK.typ with "
"per-class probabilities (e.g. `PAY 81% | CHO 12% | BOU 4%`).\n\n"
"Sheets: `planilla` (team-style wide, one row per segment), `aims` "
"(one row per AIM with probabilities), `schema`, `summary`. Amber rows "
"carry `needs_review` flags.\n\n"
"⚠️ **Interim model**: trained while the annotation base is still being "
"completed (several taxa have very few examples). Depth 1–2 aggregates "
"are reliable; deeper taxa are suggestions to review, not final codes."
)
file_input = gr.File(label="Paso 1 Excel (.xlsx)", file_types=[".xlsx"])
technical_input = gr.File(
label="Optional: Paso 1 technical sidecar (*_technical.json) — improves "
"verb normalization",
file_types=[".json"],
)
depth_input = gr.Dropdown(
label="Taxonomy depth",
choices=[label for label, _ in DEPTH_CHOICES],
value=DEPTH_CHOICES[3][0],
)
run_button = gr.Button("Run Paso 2", variant="primary")
output_file = gr.File(label="Excel result (Paso 2)")
def _run(file_obj, technical_obj, depth_label):
depth = dict(DEPTH_CHOICES)[depth_label]
return classify(file_obj, technical_obj, depth)
run_button.click(_run, inputs=[file_input, technical_input, depth_input],
outputs=output_file)
if __name__ == "__main__":
demo.launch()