Spaces:
Running
Running
Download app.py from FINAL-Bench/ONGRID: direct link, hf CLI and curl.
- Browser
- Download file 7.47 kB
-
https://huggingface.co/spaces/FINAL-Bench/ONGRID/resolve/main/app.py
- Command line
-
hf download hf://spaces/FINAL-Bench/ONGRID/app.py
-
curl -L -o app.py https://huggingface.co/spaces/FINAL-Bench/ONGRID/resolve/main/app.py
7.47 kB
| import os, json, time, collections, os.path as op, threading, urllib.request, urllib.error | |
| from fastapi import FastAPI, Request | |
| from fastapi.responses import JSONResponse | |
| from fastapi.staticfiles import StaticFiles | |
| app = FastAPI() | |
| KEY = os.environ.get("ANTHROPIC_API_KEY", "") | |
| MODEL = os.environ.get("KNOU_MODEL", "claude-haiku-4-5-20251001") | |
| # ================= ZTC (zero-token answer verification, in-process on CPU) ================= | |
| ZTC_REPO = os.environ.get("ZTC_REPO", "FINAL-Bench/ZTC-Judge-4B") | |
| _ZT = {"ready": False, "err": None, "model": None, "tok": None, "cfg": None, "lin": None, "cv": None} | |
| def _ztc_load(): | |
| try: | |
| import numpy as np, torch | |
| from transformers import AutoModel, AutoTokenizer | |
| from huggingface_hub import snapshot_download | |
| p = snapshot_download(ZTC_REPO) | |
| cfg = json.load(open(op.join(p, "ztc_config.json"), encoding="utf-8")) | |
| lin = dict(np.load(op.join(p, cfg["probe_file"]), allow_pickle=True)) | |
| cvp = op.join(p, "ztc_curve_probe_v2.npz") | |
| if not op.exists(cvp): | |
| cvp = op.join(p, cfg.get("curve_probe_file", "ztc_curve_probe.npz")) | |
| cv = dict(np.load(cvp)) | |
| tok = AutoTokenizer.from_pretrained(p) | |
| if tok.pad_token is None: | |
| tok.pad_token = tok.eos_token | |
| model = AutoModel.from_pretrained(p, dtype=torch.bfloat16, low_cpu_mem_usage=True).eval() | |
| _ZT.update(model=model, tok=tok, cfg=cfg, lin=lin, cv=cv, ready=True) | |
| except Exception as e: | |
| _ZT["err"] = str(e)[:200] | |
| threading.Thread(target=_ztc_load, daemon=True).start() | |
| def ztc_score(question, answer): | |
| import numpy as np, torch | |
| cfg, tok, model, lin, cv = _ZT["cfg"], _ZT["tok"], _ZT["model"], _ZT["lin"], _ZT["cv"] | |
| text = cfg["template"] % ((question or "").strip(), (answer or "").strip()) | |
| b = tok([text], return_tensors="pt", truncation=True, max_length=cfg["max_length"]) | |
| with torch.no_grad(): | |
| h = model(input_ids=b["input_ids"], attention_mask=b["attention_mask"]).last_hidden_state | |
| v = h[0, int(b["attention_mask"].sum()) - 1].float().numpy().astype(np.float64) | |
| s = float(((v - lin["mu"]) / lin["sd"]) @ lin["w"]) | |
| z = (v - cv["mu"]) / cv["sd"]; a = cv["anchors"].astype(np.float64) | |
| d2 = np.sum(a * a, 1) + float(z @ z) - 2.0 * (a @ z) | |
| k = np.exp(-np.maximum(d2, 0.0) / (float(cv["med"]) * float(cv["gamma"]))) | |
| c = float(k @ cv["alpha"].astype(np.float64)) | |
| t = cfg["thresholds"] | |
| verdict = "๊ฒํ ์๋ง" if s <= t["review"] else ("์ด์ ์์" if s >= t["clear"] else "ํ๋จ ๋ณด๋ฅ") | |
| return {"score": round(s, 3), "verdict": verdict, "curved": round(c, 3), "repo": ZTC_REPO, "generated_tokens": 0} | |
| # ๋น์ฉ ์ ์ฉ ๋ฐฉ์ง: IP๋น ๋ถ๋น ํธ์ถ ์ ํ (์ ์ ์ฌ์ฉ์ ์ํฅ ์์) | |
| _HITS = collections.defaultdict(list) | |
| _LIMIT = int(os.environ.get("KNOU_RATE_PER_MIN", "20")) | |
| def _rate_ok(ip): | |
| now = time.time(); q = _HITS[ip] | |
| while q and now - q[0] > 60: q.pop(0) | |
| if len(q) >= _LIMIT: return False | |
| q.append(now); return True | |
| SYS = ( | |
| "๋๋ ์ง์๊ทธ๋ํ ๊ธฐ๋ฐ ์ง์์๋ต ์์ง์ด๋ค. ์ ๊ณต๋ '์ฌ์ค(ํธ๋ฆฌํ)'๋ง ๊ทผ๊ฑฐ๋ก ๋ตํ๋ค. " | |
| "ํธ๋ฆฌํ์ 'A โ ๊ด๊ณ โ B' ํ์์ด๋ฉฐ ๋ค์์ฒ๋ผ ์์ฐ์ด๋ก ํด์ํด ๋ต์ ๊ตฌ์ฑํ๋ค: " | |
| "IsA=A๋ B์ ํ ์ข ๋ฅ, UsedFor=A๋ B์ ์ฐ์, AtLocation=A๋ B์ ์์, " | |
| "CapableOf=A๋ B๋ฅผ ํ ์ ์์, MadeOf=A๋ B๋ก ๋ง๋ค์ด์ง, HasA=A๋ B๋ฅผ ๊ฐ์ง, " | |
| "HasProperty=A๋ Bํ ์ฑ์ง, PartOf=A๋ B์ ์ผ๋ถ, Causes=A๋ B๋ฅผ ์ผ์ผํด, HasSubevent=A๋ฅผ ํ๋ฉด B๊ฐ ์ผ์ด๋จ, RelatedTo=A๋ B์ ๊ด๋ จ. " | |
| "์ง๋ฌธ ๋์์ ๊ดํ ์ฌ์ค๋ค์ ๋ฌถ์ด ์์ ๊ฐ ์๊ฒ ์ง์ ๋ ์ธ์ด๋ก 2~4๋ฌธ์ฅ์ผ๋ก ๋ตํ๋ค. " | |
| "๊ด๊ณ ์ด๋ฆ(IsA ๋ฑ)์ ๊ทธ๋๋ก ๋์ดํ์ง ๋ง๊ณ ์์ฐ์ค๋ฌ์ด ๋ฌธ์ฅ์ผ๋ก ํ์ด ์ด๋ค. " | |
| "๋ณด๋ ๊ธฐ์ฌ(๊ธฐ์ฌ ์ ๋ชฉ์ด ์ฌ์ค๋ก ์ฃผ์ด์ง)์ ๊ฒฝ์ฐ ์ ๋ชฉ ๋ด์ฉ์ ๊ทผ๊ฑฐ๋ก ์ฌ์ค๋ง ์์ฝํ๋ค. " | |
| "์ ๋ง๋ก ๊ด๋ จ ์ฌ์ค์ด ํ๋๋ ์์ ๋๋ง ํด๋น ์ธ์ด๋ก '๊ด๋ จ ์ ๋ณด๊ฐ ์์ต๋๋ค'๋ผ๊ณ ๋ตํ๋ค. ์๋ ์ฌ์ค์ ์ง์ด๋ด์ง ์๋๋ค." | |
| ) | |
| def _ip(req): | |
| fwd = req.headers.get("x-forwarded-for") | |
| return (fwd.split(",")[0].strip() if fwd else (req.client.host if req.client else "x")) or "x" | |
| def health(): | |
| return {"ok": True, "has_key": bool(KEY), "model": MODEL} | |
| async def ask(req: Request): | |
| try: | |
| data = await req.json() | |
| except Exception: | |
| return JSONResponse({"answer": "์์ฒญ ํ์ ์ค๋ฅ."}) | |
| q = (str(data.get("query") or "")).strip()[:600] | |
| facts = data.get("facts") or [] | |
| facts = [str(f)[:200] for f in facts][:60] | |
| lang = str(data.get("lang") or "ko")[:2] | |
| langname = {"ko": "ํ๊ตญ์ด(Korean)", "en": "English", "zh": "ไธญๆ(Chinese)"}.get(lang, "ํ๊ตญ์ด(Korean)") | |
| ip = (req.client.host if req.client else "x") | |
| fwd = req.headers.get("x-forwarded-for") | |
| if fwd: ip = fwd.split(",")[0].strip() | |
| if not _rate_ok(ip): | |
| return JSONResponse({"answer": "์์ฒญ์ด ๋ง์ต๋๋ค. ์ ์ ํ ๋ค์ ์๋ํด ์ฃผ์ธ์. / Rate limit, please retry shortly."}) | |
| if not q: | |
| return JSONResponse({"answer": "..."}) | |
| if not KEY: | |
| return JSONResponse({"answer": "(์๋ฒ์ LLM ํค๊ฐ ์ค์ ๋์ง ์์์ต๋๋ค. Space Secrets์ ANTHROPIC_API_KEY๋ฅผ ์ถ๊ฐํ์ธ์.)"}) | |
| factstr = "\n".join("- " + f for f in facts) if facts else "(๊ด๋ จ ์ฌ์ค ์์)" | |
| prompt = ("Facts retrieved from the knowledge graph:\n" + factstr + "\n\nQuestion: " + q + | |
| "\n\nAnswer ONLY from the facts above, in " + langname + | |
| ", in 2-4 sentences. If the facts are insufficient, say so briefly in " + langname + ". Do not invent facts.") | |
| body = json.dumps({ | |
| "model": MODEL, "max_tokens": 400, "system": SYS, | |
| "messages": [{"role": "user", "content": prompt}], | |
| }).encode() | |
| r = urllib.request.Request( | |
| "https://api.anthropic.com/v1/messages", data=body, | |
| headers={"x-api-key": KEY, "anthropic-version": "2023-06-01", "content-type": "application/json"}, | |
| ) | |
| try: | |
| resp = urllib.request.urlopen(r, timeout=45) | |
| d = json.load(resp) | |
| text = "".join(b.get("text", "") for b in d.get("content", []) if b.get("type") == "text") | |
| return JSONResponse({"answer": text or "(๋น ์๋ต)"}) | |
| except urllib.error.HTTPError as e: | |
| return JSONResponse({"answer": "(LLM ์ค๋ฅ " + str(e.code) + ")"}) | |
| except Exception: | |
| return JSONResponse({"answer": "(LLM ํธ์ถ ์คํจ)"}) | |
| def ztc_status(): | |
| return {"ready": _ZT["ready"], "err": _ZT["err"], "repo": ZTC_REPO} | |
| async def verify_ep(req: Request): | |
| if not _rate_ok("v:" + _ip(req)): | |
| return JSONResponse({"ok": False, "error": "rate"}) | |
| try: | |
| data = await req.json() | |
| except Exception: | |
| return JSONResponse({"ok": False}) | |
| q = str(data.get("query") or "")[:600]; a = str(data.get("answer") or "")[:2000] | |
| if not q or not a: | |
| return JSONResponse({"ok": False}) | |
| if not _ZT["ready"]: | |
| return JSONResponse({"ok": False, "error": "ztc_loading", "detail": _ZT["err"]}) | |
| try: | |
| return JSONResponse({"ok": True, "ztc": ztc_score(q, a)}) | |
| except Exception as e: | |
| return JSONResponse({"ok": False, "error": str(e)[:160]}) | |
| app.mount("/", StaticFiles(directory="static", html=True), name="static") | |