Spaces:
Running on Zero
Running on Zero
Download app.py from TheSeriousProgrammer/QwenJev: direct link, hf CLI and curl.
- Browser
- Download file 6.22 kB
-
https://huggingface.co/spaces/TheSeriousProgrammer/QwenJev/resolve/main/app.py
- Command line
-
hf download hf://spaces/TheSeriousProgrammer/QwenJev/app.py
-
curl -L -o app.py https://huggingface.co/spaces/TheSeriousProgrammer/QwenJev/resolve/main/app.py
6.22 kB
| """LLM as Jev-like classifier. Single-file Space.""" | |
| import json | |
| import gradio as gr | |
| import spaces | |
| import torch | |
| from transformers import AutoModelForCausalLM, AutoTokenizer | |
| LABELS = "0123456789abcdefghijklmnopqrstuvwxyz" | |
| DEFAULT_MODEL = "Qwen/Qwen3-0.6B" | |
| MODELS = [DEFAULT_MODEL, "Qwen/Qwen3-1.7B", "Qwen/Qwen3-4B", | |
| "Qwen/Qwen2.5-0.5B-Instruct", "Qwen/Qwen2.5-1.5B-Instruct"] | |
| _cache = {} | |
| def get_model(mid): | |
| if mid not in _cache: | |
| tok = AutoTokenizer.from_pretrained(mid, trust_remote_code=True) | |
| tok.pad_token = tok.pad_token or tok.eos_token | |
| tok.padding_side = "left" | |
| m = AutoModelForCausalLM.from_pretrained(mid, trust_remote_code=True, dtype="auto", device_map="auto").eval() | |
| _cache[mid] = (m, tok) | |
| return _cache[mid] | |
| def prompt(state, question, options): | |
| opts = "\n".join(f"<option_{i}>{o}</option_{i}>" for i, o in enumerate(options)) | |
| return (f"For the following situation and question, select the desired option, by just returning the option number\n\n" | |
| f"<Situation>{state}</Situation>\n<Question>{question}</Question>\n\nOptions:\n\n{opts}\n\nAnswer: ") | |
| def ids_for(tok, n): | |
| ids = [] | |
| for c in LABELS[:n]: # prompt ends with space, so no " c" variant needed | |
| e = tok.encode(c, add_special_tokens=False) | |
| assert len(e) == 1, f"label {c!r} is not single-token: {e}" | |
| ids.append(e[0]) | |
| return ids | |
| def predict_batch(prompts, ns, model, tok): | |
| enc = tok(prompts, return_tensors="pt", padding=True).to(model.device) | |
| last = model(**enc).logits[:, -1, :] # [B, V] | |
| mn, b = max(ns), len(ns) | |
| idx = torch.zeros((b, mn), dtype=torch.long, device=last.device) | |
| for i, n in enumerate(ns): | |
| idx[i, :n] = torch.tensor(ids_for(tok, n), device=last.device) | |
| mask = torch.arange(mn, device=last.device)[None] < torch.tensor(ns, device=last.device)[:, None] | |
| scores = torch.where(mask, last.gather(1, idx), float("-inf")) | |
| return torch.softmax(scores, 1).tolist() | |
| def classify(mid, state, qjson): | |
| qs = json.loads(qjson) | |
| qs = [{"key": k, **v} for k, v in qs.items()] if isinstance(qs, dict) else qs | |
| prompts, ns, metas = [], [], [] | |
| for q in qs: | |
| t = q.get("type", "choice") | |
| key = q.get("key", "q") | |
| if t == "noul": | |
| o = ["no", "yes"] | |
| meta = {"key": key, "type": t, "options": o} | |
| prompts.append(prompt(state, q.get("question", q.get("instructions", "")), o)) | |
| else: | |
| o = q["options"] | |
| meta = {"key": key, "type": t, "options": o, "min": q.get("min", 0), "max": q.get("max", 100)} | |
| prompts.append(prompt(state, q.get("question", ""), o)) | |
| ns.append(len(o)) | |
| metas.append(meta) | |
| model, tok = get_model(mid) | |
| out = {} | |
| for pv, m in zip(predict_batch(prompts, ns, model, tok), metas): | |
| pv = [round(float(x), 6) for x in pv[:len(m["options"])]] | |
| probs = dict(zip(m["options"], pv)) | |
| conf = max(pv) | |
| if m["type"] == "choice": | |
| out[m["key"]] = {"choice": m["options"][pv.index(conf)], "probabilities": probs, "confidence": conf} | |
| elif m["type"] == "noul": | |
| out[m["key"]] = {"noul": pv[1], "probabilities": probs, "confidence": conf} | |
| else: | |
| lo, hi = m["min"], m["max"] | |
| out[m["key"]] = {"score": round(sum(p * (lo + i * (hi - lo) / (len(pv) - 1)) for i, p in enumerate(pv)), 4), | |
| "probabilities": probs, "confidence": conf} | |
| return out | |
| EXAMPLE = json.dumps({ | |
| "category": {"type": "choice", "question": "Which dept?", "options": ["billing", "technical", "sales"]}, | |
| "urgency": {"type": "score", "question": "How urgent?", "options": ["low", "medium", "high"], "min": 0, "max": 100}, | |
| "is_urgent": {"type": "noul", "question": "Urgent?"}, | |
| }, indent=2) | |
| with gr.Blocks(title="QwenJev — Turn Context into Decisions") as demo: | |
| gr.Markdown(""" | |
| # QwenJev — Turn Context into Decisions | |
| ### Make any LLM work as a decision model or zero-shot classifier | |
| Turn ordinary context into **structured decisions and probabilities**—without | |
| generating free-form text. | |
| 1. Describe what is happening in **Context**. | |
| 2. Define one or more decisions in **Questions**. | |
| 3. Click **Evaluate decisions** to get typed, name-keyed results. | |
| """) | |
| with gr.Row(): | |
| with gr.Column(): | |
| mid = gr.Dropdown( | |
| MODELS, value=DEFAULT_MODEL, label="1. Model", | |
| info="The small Qwen3-0.6B model is the fastest default.", | |
| allow_custom_value=True) | |
| state = gr.Textbox( | |
| label="2. Context", | |
| info="Paste the facts or situation the model should judge.", | |
| lines=6, value="Stripe failing 3 days. Losing sales. ASAP.") | |
| qin = gr.Textbox( | |
| label="3. Questions (JSON)", | |
| info="Each named entry becomes one independent decision.", | |
| lines=18, value=EXAMPLE) | |
| with gr.Accordion("Question types", open=False): | |
| gr.Markdown(""" | |
| - **choice** — select one item from `options`. | |
| - **score** — distribute probability over ordered `options` and | |
| return their expected value between `min` and `max`. | |
| - **noul** — return a yes/no probability; no `options` required. | |
| The JSON key (for example, `urgency`) becomes the result key. | |
| """) | |
| btn = gr.Button("Evaluate decisions", variant="primary") | |
| with gr.Column(): | |
| gr.Markdown(""" | |
| ### Structured results | |
| Every answer is restricted to the requested choices. | |
| - **probabilities** show the model's relative preference among the | |
| allowed answers. | |
| - **confidence** is the highest option probability. It is a raw model | |
| score, not a calibrated guarantee of correctness. | |
| """) | |
| out = gr.JSON(label="Decisions and probabilities") | |
| btn.click(classify, [mid, state, qin], [out]) | |
| if __name__ == "__main__": | |
| demo.launch() | |