Instructions to use Meanblock/JEV-CPU with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Meanblock/JEV-CPU with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("zero-shot-classification", model="Meanblock/JEV-CPU")# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("Meanblock/JEV-CPU", device_map="auto") - Notebooks
- Google Colab
- Kaggle
| #!/usr/bin/env python3 | |
| """ | |
| SemIf 웹 UI 서버 (CPU 백엔드). | |
| - 백엔드 엔진: SemIf( /tmp/SemIf )의 direct.score() — 텍스트 생성 없이 옵션 logits 읽기 | |
| - 모델: Qwen/Qwen3-0.6B (CPU / float32), 서버 시작 시 1회 로드 후 재사용 | |
| - UI: 좌상단=판단할 데이터(state), 좌하단=기준 추가, 우측=판단 결과 | |
| - 포트: 8080 | |
| . .venv/bin/activate && python server.py | |
| """ | |
| import json | |
| import threading | |
| import time | |
| from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer | |
| from semif_cpu import load_causal_model_cpu, direct_score, MODEL, REVISION | |
| PORT = 8080 | |
| _LOCK = threading.Lock() | |
| _STATE = {"model": None, "tokenizer": None, "meta": None} | |
| def get_engine(): | |
| if _STATE["model"] is None: | |
| print(f"[engine] loading {MODEL} @ {REVISION} (CPU/float32) ...", flush=True) | |
| t0 = time.time() | |
| m, tok, meta = load_causal_model_cpu(MODEL, REVISION) | |
| _STATE.update(model=m, tokenizer=tok, meta=meta) | |
| print(f"[engine] ready in {time.time()-t0:.1f}s", flush=True) | |
| return _STATE["model"], _STATE["tokenizer"], _STATE["meta"] | |
| def decide(state, criteria): | |
| """criteria: [{id, question, options:[{id, description}]}] -> 결과 리스트.""" | |
| model, tok, meta = get_engine() | |
| results = [] | |
| with _LOCK: | |
| for crit in criteria: | |
| row = { | |
| "id": crit.get("id") or "criterion", | |
| "state": state, | |
| "question": crit.get("question", ""), | |
| "options": crit.get("options", []), | |
| } | |
| try: | |
| r = direct_score(model, tok, row, meta) | |
| pairs = sorted( | |
| zip(r["option_ids"], r["probabilities"]), key=lambda x: -x[1] | |
| ) | |
| results.append({ | |
| "id": row["id"], | |
| "question": row["question"], | |
| "winner": pairs[0][0], | |
| "ranked": [{"id": o, "prob": p} for o, p in pairs], | |
| "forward_seconds": round(r["forward_seconds"], 2), | |
| "input_tokens": r["input_tokens"], | |
| "error": None, | |
| }) | |
| except Exception as exc: # SemIf validate_row 등에서 발생 | |
| results.append({ | |
| "id": row["id"], "question": row["question"], | |
| "winner": None, "ranked": [], "error": str(exc), | |
| }) | |
| return results | |
| class Handler(BaseHTTPRequestHandler): | |
| def log_message(self, *a): | |
| pass | |
| def _send(self, code, body, ctype="application/json; charset=utf-8"): | |
| data = body.encode("utf-8") if isinstance(body, str) else body | |
| self.send_response(code) | |
| self.send_header("Content-Type", ctype) | |
| self.send_header("Content-Length", str(len(data))) | |
| self.end_headers() | |
| self.wfile.write(data) | |
| def do_GET(self): | |
| if self.path in ("/", "/index.html"): | |
| self._send(200, PAGE, "text/html; charset=utf-8") | |
| elif self.path == "/api/health": | |
| self._send(200, json.dumps({"ok": True, "model": MODEL, | |
| "loaded": _STATE["model"] is not None})) | |
| else: | |
| self._send(404, json.dumps({"error": "not found"})) | |
| def do_POST(self): | |
| if self.path != "/api/decide": | |
| self._send(404, json.dumps({"error": "not found"})) | |
| return | |
| try: | |
| length = int(self.headers.get("Content-Length", 0)) | |
| payload = json.loads(self.rfile.read(length) or b"{}") | |
| state = payload.get("state") | |
| criteria = payload.get("criteria", []) | |
| if not state or not isinstance(criteria, list) or not criteria: | |
| raise ValueError("state 와 criteria(1개 이상)가 필요합니다") | |
| t0 = time.time() | |
| results = decide(state, criteria) | |
| self._send(200, json.dumps({ | |
| "results": results, | |
| "total_seconds": round(time.time() - t0, 2), | |
| "model": MODEL, | |
| }, ensure_ascii=False)) | |
| except Exception as exc: | |
| self._send(400, json.dumps({"error": str(exc)}, ensure_ascii=False)) | |
| PAGE = r"""<!doctype html> | |
| <html lang="en"><head> | |
| <meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"> | |
| <title>JEV-CPU · Semantic Decisions on CPU</title> | |
| <style> | |
| :root{ | |
| --bg:#0e1116; --panel:#161b22; --panel2:#1c232d; --border:#2a323d; | |
| --txt:#e6edf3; --muted:#8b98a5; --accent:#4c8dff; --accent2:#2ea043; | |
| --danger:#f85149; --chip:#22303f; | |
| } | |
| *{box-sizing:border-box} | |
| body{margin:0;font:14px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Noto Sans KR",sans-serif; | |
| background:var(--bg);color:var(--txt);height:100vh;overflow:hidden} | |
| header{display:flex;align-items:center;gap:12px;padding:12px 20px;border-bottom:1px solid var(--border); | |
| background:var(--panel)} | |
| header h1{font-size:16px;margin:0;font-weight:650} | |
| header .tag{font-size:11px;color:var(--muted);border:1px solid var(--border);padding:2px 8px;border-radius:20px} | |
| header .spacer{flex:1} | |
| #status{font-size:12px;color:var(--muted)} | |
| .grid{display:grid;grid-template-columns:minmax(340px,1fr) minmax(360px,1.1fr); | |
| grid-template-rows:1fr 1fr;gap:14px;padding:14px;height:calc(100vh - 53px)} | |
| .panel{background:var(--panel);border:1px solid var(--border);border-radius:12px; | |
| display:flex;flex-direction:column;overflow:hidden} | |
| .panel > .head{padding:10px 14px;border-bottom:1px solid var(--border);font-weight:600; | |
| display:flex;align-items:center;gap:8px;font-size:13px} | |
| .panel > .head .num{width:20px;height:20px;border-radius:6px;background:var(--chip);color:var(--accent); | |
| display:grid;place-items:center;font-size:11px;font-weight:700} | |
| .panel > .body{padding:14px;overflow:auto;flex:1} | |
| /* left column stack */ | |
| #pData{grid-row:1;grid-column:1} | |
| #pCrit{grid-row:2;grid-column:1} | |
| #pResult{grid-row:1 / span 2;grid-column:2} | |
| textarea,input,select{width:100%;background:var(--panel2);border:1px solid var(--border);color:var(--txt); | |
| border-radius:8px;padding:8px 10px;font:inherit;resize:vertical} | |
| textarea{min-height:120px;height:100%} | |
| #pData .body{display:flex} | |
| label.mini{font-size:11px;color:var(--muted);display:block;margin:8px 0 4px} | |
| button{cursor:pointer;border:1px solid var(--border);background:var(--panel2);color:var(--txt); | |
| border-radius:8px;padding:7px 12px;font:inherit;font-weight:600} | |
| button:hover{border-color:var(--accent)} | |
| button.primary{background:var(--accent);border-color:var(--accent);color:#fff} | |
| button.primary:disabled{opacity:.5;cursor:not-allowed} | |
| button.mini{padding:3px 8px;font-size:12px;font-weight:500} | |
| .crit{border:1px solid var(--border);border-radius:10px;padding:10px;margin-bottom:10px;background:var(--panel2)} | |
| .crit .row{display:flex;gap:8px;align-items:center;margin-bottom:6px} | |
| .crit .row input{flex:1} | |
| .opt{display:flex;gap:6px;align-items:center;margin:4px 0} | |
| .opt input:first-child{flex:0 0 90px} | |
| .opt input:last-child{flex:1} | |
| .crit .del{color:var(--danger);border-color:transparent;background:transparent} | |
| .toolbar{display:flex;gap:8px;margin-top:6px} | |
| /* results */ | |
| .res{border:1px solid var(--border);border-radius:10px;padding:12px;margin-bottom:12px;background:var(--panel2)} | |
| .res h3{margin:0 0 2px;font-size:13px} | |
| .res .q{color:var(--muted);font-size:12px;margin-bottom:10px} | |
| .res .winner{display:inline-block;background:rgba(46,160,67,.15);color:var(--accent2); | |
| border:1px solid rgba(46,160,67,.4);padding:2px 10px;border-radius:20px;font-weight:700;font-size:12px;margin-bottom:10px} | |
| .bar{margin:7px 0} | |
| .bar .lab{display:flex;justify-content:space-between;font-size:12px;margin-bottom:3px} | |
| .bar .track{height:8px;background:#0c0f14;border-radius:6px;overflow:hidden} | |
| .bar .fill{height:100%;background:linear-gradient(90deg,#4c8dff,#7aa8ff);border-radius:6px;transition:width .4s} | |
| .bar.top .fill{background:linear-gradient(90deg,#2ea043,#57d977)} | |
| .res .meta{color:var(--muted);font-size:11px;margin-top:8px} | |
| .err{color:var(--danger);font-size:12px} | |
| .empty{color:var(--muted);text-align:center;margin-top:40px;font-size:13px} | |
| .hint{color:var(--muted);font-size:11px;margin-top:6px} | |
| </style></head><body> | |
| <header> | |
| <h1>⚡ JEV-CPU</h1><span class="tag">Semantic decisions · CPU · SemIf engine</span> | |
| <span class="spacer"></span> | |
| <span id="status">Checking engine…</span> | |
| <button class="primary" id="runBtn" onclick="run()">▶ Run decisions</button> | |
| </header> | |
| <div class="grid"> | |
| <section class="panel" id="pData"> | |
| <div class="head"><span class="num">1</span> State / Evidence <span style="color:var(--muted);font-weight:400">— data to judge · ≤ ~4,096 tok/decision (model ctx 40,960)</span></div> | |
| <div class="body"> | |
| <textarea id="state" placeholder="Paste the data to judge here. e.g. a customer review, support ticket, log line, or JSON."></textarea> | |
| </div> | |
| </section> | |
| <section class="panel" id="pCrit"> | |
| <div class="head"><span class="num">2</span> Criteria <span style="color:var(--muted);font-weight:400">— add / edit decisions</span></div> | |
| <div class="body"> | |
| <div id="crits"></div> | |
| <div class="toolbar"> | |
| <button class="mini" onclick="addCrit()">+ Add criterion</button> | |
| <button class="mini" onclick="loadOriginalExamples()" title="Load the 3 criteria from upstream SemIf examples/decisions.jsonl">↧ Load SemIf examples</button> | |
| <button class="mini" onclick="clearCrits()">Clear</button> | |
| </div> | |
| <div class="hint">Each criterion = a question + 2–16 typed options (id · description). The model reads the option probabilities to decide — same schema as upstream SemIf.</div> | |
| </div> | |
| </section> | |
| <section class="panel" id="pResult"> | |
| <div class="head"><span class="num">3</span> Results</div> | |
| <div class="body" id="results"><div class="empty">Add criteria and click <b>Run decisions</b>.</div></div> | |
| </section> | |
| </div> | |
| <script> | |
| let CID=0; | |
| function el(h){const t=document.createElement('template');t.innerHTML=h.trim();return t.content.firstChild;} | |
| function addCrit(q,opts){ | |
| CID++; | |
| const c=el(`<div class="crit" data-cid="${CID}"> | |
| <div class="row"> | |
| <input class="q" placeholder="Question (e.g. Classify the customer's sentiment)" value="${q||''}"> | |
| <button class="del mini" title="Remove criterion">✕</button> | |
| </div> | |
| <div class="opts"></div> | |
| <button class="mini addopt">+ option</button> | |
| </div>`); | |
| c.querySelector('.del').onclick=()=>c.remove(); | |
| c.querySelector('.addopt').onclick=()=>c.querySelector('.opts').appendChild(optRow()); | |
| const ob=c.querySelector('.opts'); | |
| (opts||[['yes','Yes'],['no','No']]).forEach(o=>ob.appendChild(optRow(o[0],o[1]))); | |
| document.getElementById('crits').appendChild(c); | |
| } | |
| function optRow(id,desc){ | |
| const o=el(`<div class="opt"> | |
| <input placeholder="id" value="${id||''}"> | |
| <input placeholder="description" value="${desc||''}"> | |
| <button class="del mini">✕</button></div>`); | |
| o.querySelector('.del').onclick=()=>o.remove(); | |
| return o; | |
| } | |
| function collect(){ | |
| const state=document.getElementById('state').value.trim(); | |
| const crits=[...document.querySelectorAll('.crit')].map(c=>({ | |
| id:'c'+c.dataset.cid, | |
| question:c.querySelector('.q').value.trim(), | |
| options:[...c.querySelectorAll('.opt')].map(o=>{ | |
| const i=o.querySelectorAll('input'); | |
| return {id:i[0].value.trim(),description:i[1].value.trim()}; | |
| }).filter(o=>o.id&&o.description) | |
| })); | |
| return {state,criteria:crits}; | |
| } | |
| async function run(){ | |
| const {state,criteria}=collect(); | |
| const box=document.getElementById('results'); | |
| if(!state){box.innerHTML='<div class="err">Enter the data to judge in the top-left panel.</div>';return;} | |
| if(!criteria.length){box.innerHTML='<div class="err">Add at least one criterion.</div>';return;} | |
| const btn=document.getElementById('runBtn');btn.disabled=true;btn.textContent='Deciding…'; | |
| box.innerHTML='<div class="empty">Running on CPU… (the first request also loads the model)</div>'; | |
| try{ | |
| const res=await fetch('/api/decide',{method:'POST',headers:{'Content-Type':'application/json'}, | |
| body:JSON.stringify({state,criteria})}); | |
| const data=await res.json(); | |
| if(!res.ok){box.innerHTML='<div class="err">'+(data.error||'오류')+'</div>';return;} | |
| render(data); | |
| }catch(e){box.innerHTML='<div class="err">'+e+'</div>';} | |
| finally{btn.disabled=false;btn.textContent='▶ Run decisions';} | |
| } | |
| function render(data){ | |
| const box=document.getElementById('results'); | |
| box.innerHTML=''; | |
| data.results.forEach(r=>{ | |
| if(r.error){ | |
| box.appendChild(el(`<div class="res"><h3>${r.question||r.id}</h3><div class="err">⚠ ${r.error}</div></div>`)); | |
| return; | |
| } | |
| const bars=r.ranked.map((o,i)=>` | |
| <div class="bar ${i===0?'top':''}"> | |
| <div class="lab"><span>${o.id}</span><span>${(o.prob*100).toFixed(1)}%</span></div> | |
| <div class="track"><div class="fill" style="width:${(o.prob*100).toFixed(1)}%"></div></div> | |
| </div>`).join(''); | |
| box.appendChild(el(`<div class="res"> | |
| <h3>${r.question||r.id}</h3> | |
| <div class="winner">→ ${r.winner}</div> | |
| ${bars} | |
| <div class="meta">forward ${r.forward_seconds}s · ${r.input_tokens} tokens</div> | |
| </div>`)); | |
| }); | |
| box.appendChild(el(`<div class="meta" style="text-align:right"> | |
| Method: <b>JEV-CPU</b> engine (SemIf · logit readout) · Brain model: <b>${data.model}</b> · ${data.total_seconds}s total</div>`)); | |
| } | |
| async function health(){ | |
| try{const d=await (await fetch('/api/health')).json(); | |
| document.getElementById('status').textContent='JEV-CPU engine · model '+d.model+(d.loaded?' (loaded)':' (loads on first request)'); | |
| }catch(e){document.getElementById('status').textContent='Server unreachable';} | |
| } | |
| function clearCrits(){document.getElementById('crits').innerHTML='';} | |
| // 원본 SemIf examples/decisions.jsonl 의 기준(question + options) 그대로. | |
| const ORIGINAL_EXAMPLES={ | |
| state:"The deployment completed at 14:02 UTC. Health checks passed in all three zones. No rollback was initiated.", | |
| criteria:[ | |
| ['Is there evidence that the deployment succeeded?', | |
| [['yes','The deployment succeeded.'],['no','The deployment did not succeed.'], | |
| ['insufficient','The evidence is insufficient to decide.']]], | |
| ['Which queue should handle this request?', | |
| [['account_access','Account access and authentication support.'], | |
| ['billing','Billing and payment support.'],['sales','Sales and product evaluation.']]], | |
| ['Does the request require an approved change ticket under the stated policy?', | |
| [['required','An approved change ticket is required.'], | |
| ['not_required','An approved change ticket is not required.'], | |
| ['insufficient','The evidence is insufficient to decide.']]], | |
| ] | |
| }; | |
| function loadOriginalExamples(){ | |
| clearCrits(); | |
| document.getElementById('state').value=ORIGINAL_EXAMPLES.state; | |
| ORIGINAL_EXAMPLES.criteria.forEach(([q,opts])=>addCrit(q,opts)); | |
| } | |
| // seed (English default example) | |
| addCrit("Classify the customer's sentiment",[['positive','Positive / satisfied'],['neutral','Neutral'],['negative','Negative / dissatisfied']]); | |
| addCrit('Which team should handle this ticket?',[['billing','Billing / refunds'],['tech','Technical support'],['sales','Sales']]); | |
| document.getElementById('state').value='Delivery was 3 days late and I was double charged. I need a refund. Really disappointed.'; | |
| health(); | |
| </script> | |
| </body></html>""" | |
| if __name__ == "__main__": | |
| print(f"[server] JEV-CPU UI on http://0.0.0.0:{PORT} (Ctrl+C to stop)", flush=True) | |
| get_engine() # 시작 시 미리 로드 | |
| ThreadingHTTPServer(("0.0.0.0", PORT), Handler).serve_forever() | |