SeaWolf-AI commited on
Commit
f5bc4ee
ยท
verified ยท
1 Parent(s): dc794be

merge: in-process ZTC (CPU, 4B) in single Docker space; drop second space

Browse files
Files changed (1) hide show
  1. app.py +52 -21
app.py CHANGED
@@ -1,13 +1,55 @@
1
- import os, json, urllib.request, urllib.error
2
  from fastapi import FastAPI, Request
3
  from fastapi.responses import JSONResponse
4
  from fastapi.staticfiles import StaticFiles
5
 
6
- import time, collections
7
  app = FastAPI()
8
  KEY = os.environ.get("ANTHROPIC_API_KEY", "")
9
  MODEL = os.environ.get("KNOU_MODEL", "claude-haiku-4-5-20251001")
10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  # ๋น„์šฉ ์•…์šฉ ๋ฐฉ์ง€: IP๋‹น ๋ถ„๋‹น ํ˜ธ์ถœ ์ œํ•œ (์ •์ƒ ์‚ฌ์šฉ์—” ์˜ํ–ฅ ์—†์Œ)
12
  _HITS = collections.defaultdict(list)
13
  _LIMIT = int(os.environ.get("KNOU_RATE_PER_MIN", "20"))
@@ -75,36 +117,25 @@ async def ask(req: Request):
75
  except Exception:
76
  return JSONResponse({"answer": "(LLM ํ˜ธ์ถœ ์‹คํŒจ)"})
77
 
78
- ZTC_SPACE = os.environ.get("ZTC_SPACE", "FINAL-Bench/KNOU-ZTC")
79
- _ZC = None
80
- def _ztc_client():
81
- global _ZC
82
- if _ZC is None:
83
- from gradio_client import Client
84
- # gradio_client auto-reads HF_TOKEN env for auth (larger ZeroGPU quota than anonymous)
85
- _ZC = Client(ZTC_SPACE)
86
- return _ZC
87
 
88
  @app.post("/verify")
89
  async def verify_ep(req: Request):
90
- ip = req.headers.get("x-forwarded-for", "x").split(",")[0].strip() or "x"
91
- if not _rate_ok("v:" + ip):
92
  return JSONResponse({"ok": False, "error": "rate"})
93
  try:
94
  data = await req.json()
95
  except Exception:
96
  return JSONResponse({"ok": False})
97
- q = str(data.get("query") or "")[:600]
98
- a = str(data.get("answer") or "")[:2000]
99
  if not q or not a:
100
  return JSONResponse({"ok": False})
 
 
101
  try:
102
- import concurrent.futures as cf
103
- def call():
104
- return _ztc_client().predict(q, a, "", api_name="/verify")
105
- with cf.ThreadPoolExecutor(max_workers=1) as ex:
106
- r = ex.submit(call).result(timeout=120)
107
- return JSONResponse({"ok": True, "ztc": r})
108
  except Exception as e:
109
  return JSONResponse({"ok": False, "error": str(e)[:160]})
110
 
 
1
+ import os, json, time, collections, os.path as op, threading, urllib.request, urllib.error
2
  from fastapi import FastAPI, Request
3
  from fastapi.responses import JSONResponse
4
  from fastapi.staticfiles import StaticFiles
5
 
 
6
  app = FastAPI()
7
  KEY = os.environ.get("ANTHROPIC_API_KEY", "")
8
  MODEL = os.environ.get("KNOU_MODEL", "claude-haiku-4-5-20251001")
9
 
10
+ # ================= ZTC (zero-token answer verification, in-process on CPU) =================
11
+ ZTC_REPO = os.environ.get("ZTC_REPO", "FINAL-Bench/ZTC-Judge-4B")
12
+ _ZT = {"ready": False, "err": None, "model": None, "tok": None, "cfg": None, "lin": None, "cv": None}
13
+
14
+ def _ztc_load():
15
+ try:
16
+ import numpy as np, torch
17
+ from transformers import AutoModel, AutoTokenizer
18
+ from huggingface_hub import snapshot_download
19
+ p = snapshot_download(ZTC_REPO)
20
+ cfg = json.load(open(op.join(p, "ztc_config.json"), encoding="utf-8"))
21
+ lin = dict(np.load(op.join(p, cfg["probe_file"]), allow_pickle=True))
22
+ cvp = op.join(p, "ztc_curve_probe_v2.npz")
23
+ if not op.exists(cvp):
24
+ cvp = op.join(p, cfg.get("curve_probe_file", "ztc_curve_probe.npz"))
25
+ cv = dict(np.load(cvp))
26
+ tok = AutoTokenizer.from_pretrained(p)
27
+ if tok.pad_token is None:
28
+ tok.pad_token = tok.eos_token
29
+ model = AutoModel.from_pretrained(p, dtype=torch.bfloat16, low_cpu_mem_usage=True).eval()
30
+ _ZT.update(model=model, tok=tok, cfg=cfg, lin=lin, cv=cv, ready=True)
31
+ except Exception as e:
32
+ _ZT["err"] = str(e)[:200]
33
+
34
+ threading.Thread(target=_ztc_load, daemon=True).start()
35
+
36
+ def ztc_score(question, answer):
37
+ import numpy as np, torch
38
+ cfg, tok, model, lin, cv = _ZT["cfg"], _ZT["tok"], _ZT["model"], _ZT["lin"], _ZT["cv"]
39
+ text = cfg["template"] % ((question or "").strip(), (answer or "").strip())
40
+ b = tok([text], return_tensors="pt", truncation=True, max_length=cfg["max_length"])
41
+ with torch.no_grad():
42
+ h = model(input_ids=b["input_ids"], attention_mask=b["attention_mask"]).last_hidden_state
43
+ v = h[0, int(b["attention_mask"].sum()) - 1].float().numpy().astype(np.float64)
44
+ s = float(((v - lin["mu"]) / lin["sd"]) @ lin["w"])
45
+ z = (v - cv["mu"]) / cv["sd"]; a = cv["anchors"].astype(np.float64)
46
+ d2 = np.sum(a * a, 1) + float(z @ z) - 2.0 * (a @ z)
47
+ k = np.exp(-np.maximum(d2, 0.0) / (float(cv["med"]) * float(cv["gamma"])))
48
+ c = float(k @ cv["alpha"].astype(np.float64))
49
+ t = cfg["thresholds"]
50
+ verdict = "๊ฒ€ํ†  ์š”๋ง" if s <= t["review"] else ("์ด์ƒ ์—†์Œ" if s >= t["clear"] else "ํŒ๋‹จ ๋ณด๋ฅ˜")
51
+ return {"score": round(s, 3), "verdict": verdict, "curved": round(c, 3), "repo": ZTC_REPO, "generated_tokens": 0}
52
+
53
  # ๋น„์šฉ ์•…์šฉ ๋ฐฉ์ง€: IP๋‹น ๋ถ„๋‹น ํ˜ธ์ถœ ์ œํ•œ (์ •์ƒ ์‚ฌ์šฉ์—” ์˜ํ–ฅ ์—†์Œ)
54
  _HITS = collections.defaultdict(list)
55
  _LIMIT = int(os.environ.get("KNOU_RATE_PER_MIN", "20"))
 
117
  except Exception:
118
  return JSONResponse({"answer": "(LLM ํ˜ธ์ถœ ์‹คํŒจ)"})
119
 
120
+ @app.get("/ztc_status")
121
+ def ztc_status():
122
+ return {"ready": _ZT["ready"], "err": _ZT["err"], "repo": ZTC_REPO}
 
 
 
 
 
 
123
 
124
  @app.post("/verify")
125
  async def verify_ep(req: Request):
126
+ if not _rate_ok("v:" + _ip(req)):
 
127
  return JSONResponse({"ok": False, "error": "rate"})
128
  try:
129
  data = await req.json()
130
  except Exception:
131
  return JSONResponse({"ok": False})
132
+ q = str(data.get("query") or "")[:600]; a = str(data.get("answer") or "")[:2000]
 
133
  if not q or not a:
134
  return JSONResponse({"ok": False})
135
+ if not _ZT["ready"]:
136
+ return JSONResponse({"ok": False, "error": "ztc_loading", "detail": _ZT["err"]})
137
  try:
138
+ return JSONResponse({"ok": True, "ztc": ztc_score(q, a)})
 
 
 
 
 
139
  except Exception as e:
140
  return JSONResponse({"ok": False, "error": str(e)[:160]})
141