Spaces:
Running on Zero
Running on Zero
File size: 7,577 Bytes
309aac0 fce6c24 309aac0 fce6c24 ac82d4d 309aac0 fce6c24 309aac0 fce6c24 309aac0 fce6c24 309aac0 fce6c24 309aac0 fce6c24 309aac0 fce6c24 309aac0 fce6c24 309aac0 fce6c24 ac82d4d fce6c24 ac82d4d fce6c24 ac82d4d fce6c24 ac82d4d fce6c24 ac82d4d fce6c24 309aac0 409f522 309aac0 409f522 309aac0 409f522 309aac0 fce6c24 309aac0 0cf2e33 309aac0 fce6c24 | 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 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | """min-spark-preview — a research demo for Meiosis, PICO release 01.
A 5.76M-parameter looped-hybrid language model. The signature mechanic: one
weight-shared body block runs `K` times per token (the "effort"). More passes
sharpen grammar; fewer favor commonsense.
Runs on ZeroGPU (free for the creator; visitors consume their own quota). The
model is tiny so cold-start weight streaming is near-instant.
"""
from __future__ import annotations
import spaces # MUST precede any torch / CUDA-touching import (ZeroGPU hijack)
import torch
import gradio as gr
from loader import load_model, load_tokenizer, generate
# ── load at module scope, .to("cuda") eagerly so the hijack packs weights ────
# "cuda" as a STRING (never an int device id) — ZeroGPU re-allocs device ids.
print("Loading Meiosis on ZeroGPU ...")
DEVICE = "cuda"
MODEL = load_model(DEVICE)
TOKENIZER = load_tokenizer()
_PARAMS = sum(p.numel() for p in MODEL.parameters())
print(f" {round(_PARAMS/1e6, 2)}M params ready on {DEVICE}")
# effort label → loop count. The Radio VALUES are what the API/MCP sees.
EFFORT_CHOICES = [("Low (commonsense)", 2),
("Med (grammar)", 3),
("High (deeper grammar)", 4)]
def _estimate_duration(prompt, effort, max_new, temperature, top_k):
# Tiny model on an RTX PRO 6000: cold-start weight stream (~1-2s) + ~0.5s
# per 128 tokens. Declare the realistic worst case; cap polite.
return min(60, 8 + int(max_new) * 0.1)
@spaces.GPU(duration=_estimate_duration)
def run_generate(prompt: str, effort: int, max_new: int,
temperature: float, top_k: int):
"""Generate text from a prompt. `effort` is the loop count K (2/3/4): more
passes sharpen grammar, fewer favor commonsense. Streams token-by-token."""
k = int(effort) if effort in (2, 3, 4) else 3
if not str(prompt).strip():
yield "", "<div class='status'>Type a prompt to start.</div>"
return
text = ""
count = 0
tps = 0.0
for chunk, count, tps in generate(
MODEL, TOKENIZER, prompt, loops=k, max_new=int(max_new),
temperature=float(temperature), top_k=int(top_k), device=DEVICE):
text += chunk
yield text, f"<div class='status'>K={k} · {count} tok · {tps:.1f} tok/s</div>"
yield text, f"<div class='status'>K={k} · {count} tok · {tps:.1f} tok/s · done</div>"
CSS = """
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap');
:root{
--bg:#0A1128; --surface:#101A3A; --surface2:#16224A;
--hair:#22315E; --text:#E8EDF7; --muted:#7D8DB0;
--blue:#3E7BFA; --blue-bright:#5E96FF; --blue-dim:#2A4A8F;
--r:10px;
}
*{box-sizing:border-box;}
body, .gradio-container{ background:var(--bg) !important; }
.gradio-container{
max-width:820px !important; margin:0 auto !important;
padding:32px 20px 40px !important;
font-family:'Inter',system-ui,sans-serif !important; color:var(--text) !important;
}
/* hide Gradio chrome: footer, settings, API/MCP links */
footer, .gradio-container footer, #footer{ display:none !important; }
.head{ margin-bottom:20px; }
.title{ font-weight:600; font-size:26px; letter-spacing:-0.01em; color:var(--text); line-height:1.1; }
.title em{ font-style:normal; color:var(--blue-bright); }
.sub{ font-family:'IBM Plex Mono',monospace; font-size:11px; letter-spacing:0.16em;
text-transform:uppercase; color:var(--muted); margin-top:5px; }
.field-h{ font-family:'IBM Plex Mono',monospace; font-size:10.5px; letter-spacing:0.16em;
text-transform:uppercase; color:var(--muted); margin:18px 0 8px; }
/* prompt */
.gradio-container textarea{
background:var(--surface) !important; border:1px solid var(--hair) !important;
border-radius:var(--r) !important; color:var(--text) !important;
font-family:'Inter' !important; font-size:15px !important; line-height:1.5 !important;
}
.gradio-container textarea:focus{ border-color:var(--blue) !important; box-shadow:none !important; }
/* effort radio — even pill row */
#effort, #effort .wrap, #effort .wrap > label{ width:100%; }
#effort .wrap{ display:flex !important; gap:8px !important; }
#effort label{
flex:1 1 0; display:flex !important; align-items:center !important; justify-content:center !important;
background:var(--surface) !important; border:1px solid var(--hair) !important;
border-radius:var(--r) !important; padding:11px 8px !important; cursor:pointer;
font-size:13px !important; color:var(--muted) !important; text-align:center;
white-space:nowrap; transition:border-color .15s, background .15s, color .15s;
}
#effort label:hover{ border-color:#33508f !important; }
#effort label.selected{
background:var(--surface2) !important; border-color:var(--blue) !important;
color:var(--blue-bright) !important; box-shadow:0 0 0 1px var(--blue) inset !important;
}
#effort input{ display:none !important; }
/* sliders */
.gradio-container .form label, .label-wrap label{
color:var(--muted) !important; font-size:12px !important; letter-spacing:0.02em !important;
}
input[type=range]{ accent-color:var(--blue); }
/* generate button */
button#gen{
background:var(--blue) !important; color:#fff !important; border:none !important;
border-radius:var(--r) !important; font-weight:600 !important; font-size:14px !important;
margin-top:6px;
}
button#gen:hover{ background:var(--blue-bright) !important; }
/* output */
#out textarea, #out{
background:var(--surface) !important; border:1px solid var(--hair) !important;
border-radius:var(--r) !important; color:var(--text) !important;
font-size:15.5px !important; line-height:1.6 !important;
}
.status{ font-family:'IBM Plex Mono',monospace; font-size:11.5px; color:var(--muted);
margin-top:8px; min-height:16px; }
.foot{ margin-top:26px; border-top:1px solid var(--hair); padding-top:12px;
font-family:'IBM Plex Mono',monospace; font-size:10.5px; color:#54648c;
display:flex; justify-content:space-between; flex-wrap:wrap; gap:8px; }
.foot a{ color:var(--muted); text-decoration:none; border-bottom:1px dotted var(--hair); }
.foot a:hover{ color:var(--text); }
"""
HEADER_HTML = """
<div class="head">
<div class="title">min-spark <em>preview</em></div>
<div class="sub">Minima Labs</div>
</div>
"""
with gr.Blocks(css=CSS, title="min-spark · Meiosis preview") as demo:
gr.HTML(HEADER_HTML)
gr.HTML('<div class="field-h">Effort — loops per token</div>')
effort = gr.Radio(choices=EFFORT_CHOICES, value=3, elem_id="effort",
show_label=False, container=False)
gr.HTML('<div class="field-h">Prompt</div>')
prompt = gr.Textbox(value="", placeholder="Once upon a time",
lines=2, show_label=False, container=False)
with gr.Row():
max_new = gr.Slider(8, 256, value=128, step=8, label="Max tokens")
temperature = gr.Slider(0.1, 1.6, value=0.8, step=0.05, label="Temperature")
top_k = gr.Slider(0, 200, value=50, step=5, label="Top-k (0 = off)") # infer.py default
gen_btn = gr.Button("Generate", elem_id="gen", variant="primary")
out = gr.Textbox(value="", elem_id="out", show_label=False, container=False,
lines=10, interactive=False, autoscroll=True)
status = gr.HTML(value='<div class="status"></div>')
gen_btn.click(
fn=run_generate,
inputs=[prompt, effort, max_new, temperature, top_k],
outputs=[out, status],
api_name="run_generate",
)
if __name__ == "__main__":
demo.queue().launch()
|