Spaces:
Sleeping
Sleeping
File size: 5,925 Bytes
e7aef18 da2e821 e7aef18 51fd2dd e7aef18 46966eb e7aef18 da2e821 e7aef18 | 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 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | """
Gradio demo for the SLM-264M data transformer fine-tune.
Pattern: code lives here (in the Space), weights live in the model repo and are
pulled at startup with huggingface_hub. You bundle model.py alongside this file.
Files this Space expects:
- app.py (this file)
- model.py (YOUR architecture file — copy it from your training repo)
- requirements.txt
- README.md (Space card, has the sdk: gradio frontmatter)
Weights + tokenizer are downloaded from MODEL_REPO below. If that repo is private,
add an HF_TOKEN secret to the Space (Settings -> Variables and secrets).
"""
import os
import torch
import gradio as gr
from tokenizers import Tokenizer
from huggingface_hub import hf_hub_download
import spaces
# ---- point these at your fine-tuned transform model repo -------------------
# MODEL_REPO = os.environ.get("MODEL_REPO", "uday210/slm-264m-transform") # <-- EDIT
# CKPT_FILENAME = os.environ.get("CKPT_FILENAME", "slm-264m-transform.pt") # <-- EDIT
MODEL_REPO = os.environ.get("MODEL_REPO", "uday210/slm-transform-264m")
CKPT_FILENAME = os.environ.get("CKPT_FILENAME", "slm-transform-final.pt")
TOKENIZER_REPO = os.environ.get("TOKENIZER_REPO", "uday210/slm-264m-base") # <-- ADD
TOKENIZER_FILENAME = os.environ.get("TOKENIZER_FILENAME", "tokenizer.json")
# ---- prompt template: MUST match how the fine-tune was trained -------------
# Your notes show the inference format as:
# "Convert this CSV to JSON:\n<data>\nOutput:\n"
# If your training pairs used a different wording, change it here or the model
# will be prompted off-distribution.
PROMPT_TEMPLATE = "Convert this {src} to {tgt}:\n{data}\nOutput:\n"
BLOCK_SIZE = 2048
EOT_TOKEN = "<|endoftext|>"
device = "cuda" if torch.cuda.is_available() else "cpu"
if device == "cpu":
torch.set_num_threads(os.cpu_count() or 4)
# ---------------------------------------------------------------------------
# Load architecture + weights
# ---------------------------------------------------------------------------
from model import GPT # noqa: E402 (your import-safe model.py)
_token = os.environ.get("HF_TOKEN") # only needed if the model repo is private
ckpt_path = hf_hub_download(MODEL_REPO, CKPT_FILENAME, token=_token)
# tokenizer is bundled in the Space, next to app.py
tok_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), TOKENIZER_FILENAME)
ckpt = torch.load(ckpt_path, map_location=device, weights_only=False)
model = GPT(ckpt["config"])
model.load_state_dict(ckpt["model"])
model.eval().to(device)
tok = Tokenizer.from_file(tok_path)
EOT_ID = tok.token_to_id(EOT_TOKEN)
# ---------------------------------------------------------------------------
# Generation (greedy by default — this task is deterministic transformation)
# ---------------------------------------------------------------------------
@spaces.GPU
@torch.no_grad()
def generate(prompt: str, max_new_tokens: int = 512, temperature: float = 0.0):
ids = tok.encode(prompt).ids
x = torch.tensor(ids, dtype=torch.long, device=device)[None]
start = len(ids)
for _ in range(max_new_tokens):
logits, _ = model(x[:, -BLOCK_SIZE:])
logits = logits[:, -1, :]
if temperature and temperature > 0:
probs = torch.softmax(logits / temperature, dim=-1)
nxt = torch.multinomial(probs, num_samples=1)
else:
nxt = logits.argmax(dim=-1, keepdim=True)
x = torch.cat([x, nxt], dim=1)
if EOT_ID is not None and nxt.item() == EOT_ID:
break
# stream the decoded suffix so far
yield tok.decode(x[0, start:].tolist())
yield tok.decode(x[0, start:].tolist())
def transform(data, src_fmt, tgt_fmt, max_new_tokens, temperature):
data = (data or "").strip()
if not data:
yield "Paste some input data first."
return
if src_fmt == tgt_fmt:
yield "Source and target format are the same — pick different formats."
return
prompt = PROMPT_TEMPLATE.format(src=src_fmt, tgt=tgt_fmt, data=data)
for partial in generate(prompt, int(max_new_tokens), float(temperature)):
yield partial
# ---------------------------------------------------------------------------
# UI
# ---------------------------------------------------------------------------
FORMATS = ["CSV", "JSON", "XML"]
EXAMPLE_CSV = "name,city,age\nalice,london,31\nbob,paris,47\ncarol,tokyo,29"
with gr.Blocks(title="SLM-264M Data Transformer", theme=gr.themes.Soft()) as demo:
gr.Markdown(
"# 🔄 SLM-264M Data Transformer\n"
"A 264M-parameter small language model, trained from scratch and fine-tuned to "
"convert between **CSV, JSON, and XML**. Runs on CPU — expect a few tokens/sec.\n\n"
"*Note: numbers can occasionally be miscopied; text and structure are reliable.*"
)
with gr.Row():
with gr.Column():
data_in = gr.Textbox(
label="Input data", value=EXAMPLE_CSV, lines=8,
placeholder="Paste CSV, JSON, or XML here…",
)
with gr.Row():
src = gr.Dropdown(FORMATS, value="CSV", label="From")
tgt = gr.Dropdown(FORMATS, value="JSON", label="To")
with gr.Accordion("Advanced", open=False):
max_tok = gr.Slider(64, 1024, value=512, step=64, label="Max new tokens")
temp = gr.Slider(
0.0, 1.0, value=0.0, step=0.1,
label="Temperature (0 = greedy, recommended for exact transforms)",
)
go = gr.Button("Transform", variant="primary")
with gr.Column():
out = gr.Textbox(label="Output", lines=14, show_copy_button=True)
go.click(transform, [data_in, src, tgt, max_tok, temp], out)
data_in.submit(transform, [data_in, src, tgt, max_tok, temp], out)
if __name__ == "__main__":
demo.launch() |