Spaces:
Running on Zero
Running on Zero
Update app.py
Browse files
app.py
CHANGED
|
@@ -14,6 +14,7 @@ Jev-style 判別式決策互動 POC(繁體中文)
|
|
| 14 |
"""
|
| 15 |
import json
|
| 16 |
import os
|
|
|
|
| 17 |
import subprocess
|
| 18 |
import threading
|
| 19 |
import time
|
|
@@ -138,13 +139,13 @@ def _ensure_backend_locked():
|
|
| 138 |
_set_phase("ready", "jevos 已在運行")
|
| 139 |
return
|
| 140 |
try:
|
| 141 |
-
jev_dir = _find_or_prepare_backend()
|
| 142 |
-
binary = os.path.join(jev_dir, "jev")
|
| 143 |
# 保險:確保可執行(git/網頁上傳可能掉執行權限)
|
| 144 |
try:
|
| 145 |
os.chmod(binary, 0o755)
|
| 146 |
except Exception:
|
| 147 |
pass
|
|
|
|
| 148 |
_set_phase("starting", "啟動 jevos…")
|
| 149 |
_server = subprocess.Popen([binary, "serve"], cwd=jev_dir,
|
| 150 |
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
@@ -159,13 +160,52 @@ def _ensure_backend_locked():
|
|
| 159 |
raise
|
| 160 |
|
| 161 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
def _find_or_prepare_backend():
|
| 163 |
-
"""找後端檔案,順序:repo 內已解壓 → 快取 → repo 內壓縮檔 → 線上下載。
|
|
|
|
| 164 |
# 1. 已解壓好的(手動上傳到 repo 根目錄,或之前快取的)
|
| 165 |
-
for
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 169 |
# 2. repo 根目錄有壓縮檔(手動上傳)→ 解到 DATA
|
| 170 |
jev_dir = os.path.join(DATA, "jev")
|
| 171 |
tgz = os.path.join(BASE, "jev-linux-x64.tar.gz")
|
|
@@ -176,9 +216,10 @@ def _find_or_prepare_backend():
|
|
| 176 |
os.makedirs(DATA, exist_ok=True)
|
| 177 |
with tarfile.open(tgz) as tf:
|
| 178 |
tf.extractall(DATA, filter="data")
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
|
|
|
| 182 |
# 3. 線上下載(Spaces 執行期可連外網)
|
| 183 |
os.makedirs(DATA, exist_ok=True)
|
| 184 |
tgz = _dl(BIN_URL, os.path.join(DATA, "jev-linux-x64.tar.gz"))
|
|
@@ -186,10 +227,21 @@ def _find_or_prepare_backend():
|
|
| 186 |
with tarfile.open(tgz) as tf:
|
| 187 |
tf.extractall(DATA, filter="data")
|
| 188 |
zf = _dl(MODEL_URL, os.path.join(DATA, "jevos-v3-openvino-int8.zip"))
|
| 189 |
-
_set_phase("extracting", "解壓縮模型(約 630MB)…")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 190 |
with zipfile.ZipFile(zf) as z:
|
| 191 |
-
z.
|
| 192 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 193 |
|
| 194 |
|
| 195 |
def decide(state, mode, question, options_text):
|
|
|
|
| 14 |
"""
|
| 15 |
import json
|
| 16 |
import os
|
| 17 |
+
import shutil
|
| 18 |
import subprocess
|
| 19 |
import threading
|
| 20 |
import time
|
|
|
|
| 139 |
_set_phase("ready", "jevos 已在運行")
|
| 140 |
return
|
| 141 |
try:
|
| 142 |
+
jev_dir, binary = _find_or_prepare_backend()
|
|
|
|
| 143 |
# 保險:確保可執行(git/網頁上傳可能掉執行權限)
|
| 144 |
try:
|
| 145 |
os.chmod(binary, 0o755)
|
| 146 |
except Exception:
|
| 147 |
pass
|
| 148 |
+
_ensure_model_weights(os.path.join(jev_dir, "model"))
|
| 149 |
_set_phase("starting", "啟動 jevos…")
|
| 150 |
_server = subprocess.Popen([binary, "serve"], cwd=jev_dir,
|
| 151 |
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
|
|
| 160 |
raise
|
| 161 |
|
| 162 |
|
| 163 |
+
def _ensure_model_weights(model_dir):
|
| 164 |
+
"""確保 openvino_model.bin 存在;缺失時從 release 下載 zip 並只解出該檔(支援續傳)。"""
|
| 165 |
+
bin_path = os.path.join(model_dir, "openvino_model.bin")
|
| 166 |
+
if os.path.isfile(bin_path):
|
| 167 |
+
return
|
| 168 |
+
print("[poc] 缺少 openvino_model.bin,從 release 下載模型包並解出該檔…", flush=True)
|
| 169 |
+
_set_phase("downloading", "下載模型包(約 630MB,只取權重檔,支援續傳)…")
|
| 170 |
+
zf = _dl(MODEL_URL, os.path.join(DATA, "jevos-v3-openvino-int8.zip"))
|
| 171 |
+
_set_phase("extracting", "解出 openvino_model.bin…")
|
| 172 |
+
os.makedirs(model_dir, exist_ok=True)
|
| 173 |
+
with zipfile.ZipFile(zf) as z:
|
| 174 |
+
for name in z.namelist():
|
| 175 |
+
if name.endswith("openvino_model.bin"):
|
| 176 |
+
with z.open(name) as src, open(bin_path, "wb") as dst:
|
| 177 |
+
shutil.copyfileobj(src, dst, length=1024 * 1024)
|
| 178 |
+
print("[poc] openvino_model.bin 就緒", flush=True)
|
| 179 |
+
return
|
| 180 |
+
raise RuntimeError("模型包內找不到 openvino_model.bin")
|
| 181 |
+
|
| 182 |
+
|
| 183 |
def _find_or_prepare_backend():
|
| 184 |
+
"""找後端檔案,順序:repo 內已解壓 → 快取 → repo 內壓縮檔 → 線上下載。
|
| 185 |
+
回傳 (jev_dir, binary)。jev_dir 下保證有執行檔與 model/(巢狀佈局會建連結)。"""
|
| 186 |
# 1. 已解壓好的(手動上傳到 repo 根目錄,或之前快取的)
|
| 187 |
+
for base in (BASE, DATA):
|
| 188 |
+
# 1a. 標準佈局:<base>/jev/{jev(執行檔), model/, *.so}
|
| 189 |
+
d = os.path.join(base, "jev")
|
| 190 |
+
b = os.path.join(d, "jev")
|
| 191 |
+
if os.path.isfile(b) and os.path.isdir(os.path.join(d, "model")):
|
| 192 |
+
print(f"[poc] 使用現成後端:{b}", flush=True)
|
| 193 |
+
return d, b
|
| 194 |
+
# 1b. 巢狀佈局:<base>/jev/jev/{jev(執行檔), *.so},model/ 在 <base>/jev/model/
|
| 195 |
+
# (tarball 內層目錄被原樣上傳時會長這樣;執行檔在內層,./model 要能被找到)
|
| 196 |
+
inner = os.path.join(d, "jev")
|
| 197 |
+
b2 = os.path.join(inner, "jev")
|
| 198 |
+
outer_model = os.path.join(d, "model")
|
| 199 |
+
if os.path.isfile(b2) and os.path.isdir(outer_model):
|
| 200 |
+
link = os.path.join(inner, "model")
|
| 201 |
+
if not os.path.exists(link):
|
| 202 |
+
try:
|
| 203 |
+
os.symlink(outer_model, link)
|
| 204 |
+
print("[poc] 巢狀佈局:已建立 model 連結", flush=True)
|
| 205 |
+
except Exception as e:
|
| 206 |
+
print(f"[poc] 建 model 連結失敗:{e}", flush=True)
|
| 207 |
+
print(f"[poc] 使用現成後端(巢狀):{b2}", flush=True)
|
| 208 |
+
return inner, b2
|
| 209 |
# 2. repo 根目錄有壓縮檔(手動上傳)→ 解到 DATA
|
| 210 |
jev_dir = os.path.join(DATA, "jev")
|
| 211 |
tgz = os.path.join(BASE, "jev-linux-x64.tar.gz")
|
|
|
|
| 216 |
os.makedirs(DATA, exist_ok=True)
|
| 217 |
with tarfile.open(tgz) as tf:
|
| 218 |
tf.extractall(DATA, filter="data")
|
| 219 |
+
print("[poc] 主程式解壓完成,解壓縮模型(約 630MB,需要幾分鐘)…", flush=True)
|
| 220 |
+
_extract_zip_progress(zf, jev_dir)
|
| 221 |
+
print("[poc] 模型解壓完成", flush=True)
|
| 222 |
+
return jev_dir, os.path.join(jev_dir, "jev")
|
| 223 |
# 3. 線上下載(Spaces 執行期可連外網)
|
| 224 |
os.makedirs(DATA, exist_ok=True)
|
| 225 |
tgz = _dl(BIN_URL, os.path.join(DATA, "jev-linux-x64.tar.gz"))
|
|
|
|
| 227 |
with tarfile.open(tgz) as tf:
|
| 228 |
tf.extractall(DATA, filter="data")
|
| 229 |
zf = _dl(MODEL_URL, os.path.join(DATA, "jevos-v3-openvino-int8.zip"))
|
| 230 |
+
_set_phase("extracting", "解壓縮模型(約 630MB,需要幾分鐘)…")
|
| 231 |
+
_extract_zip_progress(zf, jev_dir)
|
| 232 |
+
print("[poc] 模型解壓完成", flush=True)
|
| 233 |
+
return jev_dir, os.path.join(jev_dir, "jev")
|
| 234 |
+
|
| 235 |
+
|
| 236 |
+
def _extract_zip_progress(zf, jev_dir):
|
| 237 |
+
"""解壓 zip 並印進度(避免長時間無輸出被誤判卡死)。"""
|
| 238 |
with zipfile.ZipFile(zf) as z:
|
| 239 |
+
members = z.namelist()
|
| 240 |
+
total = len(members)
|
| 241 |
+
for i, m in enumerate(members):
|
| 242 |
+
z.extract(m, jev_dir)
|
| 243 |
+
if (i + 1) % 25 == 0 or (i + 1) == total:
|
| 244 |
+
print(f"[poc] 模型解壓縮 {i + 1}/{total}", flush=True)
|
| 245 |
|
| 246 |
|
| 247 |
def decide(state, mode, question, options_text):
|