"""Blink v0.5 on ZeroGPU — one forward pass, one calibrated decision per question.""" import os import spaces # must come before torch import html import json import math import time import gradio as gr import torch from blink_core import T_CALIBRATED, Reader, build, load_model, parse_state, render MODEL_ID = os.environ.get("BLINK_MODEL", "PixilabAI/Blink-v0.5-26B-A4B") MAXQ = 4 TYPES = {"Choice": "choice", "Yes / No": "noul", "Score": "score"} LABEL = {v: k for k, v in TYPES.items()} t0 = time.time() MODEL, TOK = load_model(MODEL_ID, "cuda") READER = Reader(MODEL, TOK, "cuda") print(f"loaded {MODEL_ID} in {time.time() - t0:.0f}s", flush=True) @spaces.GPU(duration=40, size="xlarge") # Blink in bf16 is 51.8 GB: over the default half card (48 GB) def run(state_raw, questions): """questions: [(type, question, option lines)] → raw letter log-probs per question (no temperature applied).""" state = parse_state(state_raw) out, t_all = [], time.perf_counter() for kind, question, lines in questions: keys, texts = build(kind, lines) msgs = render(state, question, texts) lps, ms = READER.read(msgs, len(texts)) out.append({"type": kind, "question": question, "keys": keys, "texts": texts, "logprobs": lps, "ms": round(ms), "prompt": msgs[1]["content"]}) return {"answers": out, "total_ms": round((time.perf_counter() - t_all) * 1000), "structured": not isinstance(state, str)} # ------------------------------------------------------------------ rendering def soft(lps, t): z = [l / t for l in lps] m = max(z) e = [math.exp(v - m) for v in z] s = sum(e) return [v / s for v in e] def pct(p): return "99.9%" if p >= .9995 else "<0.1%" if p < .0005 else f"{p * 100:.1f}%" EMPTY = '

Nothing to decide yet.

' def readout(res, t, animate=True): if not res: return EMPTY t = float(t or T_CALIBRATED) n = len(res["answers"]) parts = [f'
{n} decision{"s" if n > 1 else ""}' f'{res["total_ms"]} ms on the GPU{"JSON" if res["structured"] else "text"} state' f'T {t:.2f}
'] for ai, a in enumerate(res["answers"]): p = soft(a["logprobs"], t) best = max(range(len(p)), key=p.__getitem__) bars = [] for i, (key, text) in enumerate(zip(a["keys"], a["texts"])): letter = chr(65 + i) k = f"{letter} · {html.escape(key)}" if a["type"] == "choice" and key != text else letter bars.append(f'
' f'
' f'
{k}{html.escape(text)}
' f'
{pct(p[i])}
') extra = "" if a["type"] == "noul": extra = f'

P(yes) {p[1]:.3f} · answer {"yes" if p[1] >= .5 else "no"}

' elif a["type"] == "score": ev = sum(i * v for i, v in enumerate(p)) extra = (f'

expected level {ev:.2f} on 0–{len(p) - 1} · closest ' f'{html.escape(a["texts"][round(ev)])}

') parts.append( f'
' f'

{html.escape(a["question"])}

{LABEL[a["type"]]}' f'{a["ms"]} ms
{"".join(bars)}{extra}' f'
exact prompt
{html.escape(a["prompt"])}
') return "".join(parts) from examples import EX # noqa: E402 # ------------------------------------------------------------------ page import base64 LOGO = "data:image/png;base64," + base64.b64encode(open(os.path.join(os.path.dirname(__file__), "blink.png"), "rb").read()).decode() CARD = "https://huggingface.co/PixilabAI/Blink-v0.5-26B-A4B-NVFP4" HEAD = ('' '') HERO = f"""
Blinkv0.5

Decide in
one blink.

Blink reads a state, a question and its options, and answers with a calibrated probability for every option — one forward pass, one token, no prose to parse. Built to sit inside products: routing, moderation, tagging, gating, dedup, yes/no checks.

Model by pixilab.ai & nemini.ai

58.67Decision Index 0.2.1
60.39thinking when unsure
0.023calibration error
4Bactive of 26B
Blink

Try it

Pick an example or write your own — up to {MAXQ} questions about one state.

""" NOTE = ('

The slider re-scores the stored answer without touching the GPU. Temperature changes how sure ' 'Blink claims to be — never which option wins. At 1.4 its confidence matches its accuracy.

') HOW = f"""

How it reads

The surogate decisions v1 protocol, as on the model card.

Lettered options

Each question becomes one prompt: the state as JSON, the question, the options lettered A, B, C… Yes/no puts No at A and Yes at B.

One token

Thinking off. Blink's answer is the softmax over the option letters' log-probabilities at the first generated position — nothing is generated beyond it.

Calibrated

Divide by T = 1.4 before the softmax and the probabilities mean what they say: threshold on them (P ≥ 0.8) instead of parsing text.

Blink v0.5 by pixilab.ai & nemini.ai · Apache-2.0 · base Gemma 4 26B-A4B · this demo runs the BF16 build on ZeroGPU; the scores above are for the NVFP4 build made from it Not for high-stakes decisions without a threshold and a fallback.
""" PH = {"Choice": "Options, one per line — key, or key: description", "Score": "Levels from lowest to highest, one per line", "Yes / No": "Optional — describe the answers:\nfalse: what “no” means\ntrue: what “yes” means"} def decide(state, n, *fields): state = (state or "").strip() if not state: raise gr.Error("Give Blink a state to look at first.") qs = [] for i in range(int(n)): kind, question, options = fields[3 * i: 3 * i + 3] if not (question or "").strip(): continue try: build(TYPES[kind], (options or "").split("\n")) except ValueError as e: raise gr.Error(f"Q{i + 1}: {e}.") qs.append((TYPES[kind], question.strip(), (options or "").split("\n"))) if not qs: raise gr.Error("Ask at least one question.") if len(state) > 24_000: raise gr.Error("That state is a bit long for a demo — keep it under 24,000 characters.") return run(state, qs) def decide_api(state: str, questions: list): """Score typed decisions about one state with Blink v0.5. Args: state: the situation — plain text, or a JSON object/array as a string. questions: up to 4 items {"type": "choice"|"noul"|"score", "question": str, "options": [str]}. choice: "key" or "key: description" per option; score: levels from lowest to highest; noul (yes/no): options optional ("false: ...", "true: ..."). Returns: {"answers": [{type, question, keys, probabilities, best, logprobs, ms}], "temperature": 1.4, "total_ms": int} """ if not isinstance(questions, list) or not 1 <= len(questions) <= MAXQ: raise gr.Error(f"questions must be a list of 1 to {MAXQ} items") qs = [] for q in questions: kind = q.get("type") if kind not in ("choice", "noul", "score"): raise gr.Error("type must be choice, noul or score") try: build(kind, list(q.get("options") or [])) except ValueError as e: raise gr.Error(f"{q.get('question', '')[:60]}: {e}") qs.append((kind, str(q.get("question", "")).strip(), list(q.get("options") or []))) res = run(state, qs) for a in res["answers"]: p = soft(a["logprobs"], T_CALIBRATED) a["probabilities"] = dict(zip(a["keys"], [round(v, 4) for v in p])) a["best"] = a["keys"][max(range(len(p)), key=p.__getitem__)] a.pop("prompt", None) res["temperature"] = T_CALIBRATED return res def load_example(i): _, state, qs = EX[i] ups = [state, len(qs), None] for j in range(MAXQ): if j < len(qs): k, q, o = qs[j] ups += [gr.update(visible=True), gr.update(value=k), q, gr.update(value=o, placeholder=PH[k])] else: ups += [gr.update(visible=False), gr.update(value="Choice"), "", gr.update(value="", placeholder=PH["Choice"])] return ups + [EMPTY] def show_rows(n): return [gr.update(visible=j < n) for j in range(MAXQ)] with gr.Blocks(title="Blink v0.5 — one forward pass, one calibrated decision") as demo: res = gr.State(None) nq = gr.State(len(EX[0][2])) gr.HTML(HERO) with gr.Row(elem_classes="chips"): chips = [gr.Button(name, size="sm", scale=0) for name, _, _ in EX] with gr.Row(equal_height=False): with gr.Column(scale=1, elem_classes="panel"): state = gr.Textbox(label="State", value=EX[0][1], lines=7, max_lines=18, elem_id="state", placeholder="What Blink should know — a message, a ticket, a log line, or a JSON object.") groups, fields = [], [] for j in range(MAXQ): k0, q0, o0 = EX[0][2][j] if j < len(EX[0][2]) else ("Choice", "", "") with gr.Group(visible=j < len(EX[0][2]), elem_classes="q") as g: kind = gr.Radio(list(TYPES), value=k0, show_label=False, container=False, elem_classes="seg") q = gr.Textbox(value=q0, show_label=False, container=False, placeholder=f"Q{j + 1} — one question about the state") o = gr.Textbox(value=o0, show_label=False, container=False, lines=3, max_lines=10, placeholder=PH[k0], elem_classes="opts") kind.change(lambda k: gr.update(placeholder=PH[k]), kind, o, show_progress="hidden") groups.append(g) fields += [kind, q, o] with gr.Row(): add = gr.Button("+ Question", size="md", scale=0) rm = gr.Button("− Question", size="md", scale=0) go = gr.Button("Decide", variant="primary", elem_classes="go", scale=1) with gr.Column(scale=1, elem_classes="panel"): out = gr.HTML(EMPTY) temp = gr.Slider(0.5, 3.0, value=T_CALIBRATED, step=0.05, label="Temperature · 1.4 is calibrated") gr.HTML(NOTE) gr.HTML(HOW) with gr.Row(visible=False): # API-only endpoint: POST /decide (state, questions) -> JSON api_state, api_qs, api_out = gr.Textbox(), gr.JSON(), gr.JSON() gr.Button().click(decide_api, [api_state, api_qs], api_out, api_name="decide") go.click(decide, [state, nq, *fields], res).then(lambda r, t: readout(r, t, True), [res, temp], out) temp.change(lambda r, t: readout(r, t, False), [res, temp], out, show_progress="hidden") add.click(lambda n: min(n + 1, MAXQ), nq, nq).then(show_rows, nq, groups, show_progress="hidden") rm.click(lambda n: max(n - 1, 1), nq, nq).then(show_rows, nq, groups, show_progress="hidden") row_outputs = [state, nq, res] for j in range(MAXQ): row_outputs += [groups[j], *fields[3 * j: 3 * j + 3]] for i, b in enumerate(chips): b.click(lambda i=i: load_example(i), None, row_outputs + [out], show_progress="hidden") if __name__ == "__main__": demo.queue(max_size=40).launch(css=open(os.path.join(os.path.dirname(__file__), "style.css")).read(), head=HEAD)