Spaces:
Running on Zero
Running on Zero
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,11 +1,8 @@
|
|
| 1 |
from __future__ import annotations
|
| 2 |
-
import
|
| 3 |
import os
|
| 4 |
import subprocess
|
| 5 |
-
import tarfile
|
| 6 |
import time
|
| 7 |
-
import urllib.request
|
| 8 |
-
import zipfile
|
| 9 |
|
| 10 |
from fastapi import Request
|
| 11 |
from fastapi.responses import StreamingResponse
|
|
@@ -24,6 +21,7 @@ except ImportError:
|
|
| 24 |
def whygpu():
|
| 25 |
pass
|
| 26 |
|
|
|
|
| 27 |
def build_latest_llama_server():
|
| 28 |
binary_path = os.path.abspath("llama.cpp/build/bin/llama-server")
|
| 29 |
if os.path.exists(binary_path):
|
|
@@ -36,8 +34,13 @@ def build_latest_llama_server():
|
|
| 36 |
else:
|
| 37 |
subprocess.run(["git", "pull"], cwd="llama.cpp", check=True)
|
| 38 |
|
| 39 |
-
|
| 40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
|
| 42 |
env = os.environ.copy()
|
| 43 |
if "/usr/local/cuda/bin" not in env.get("PATH", ""):
|
|
@@ -58,7 +61,7 @@ def build_latest_llama_server():
|
|
| 58 |
["cmake", "--build", "build", "--config", "Release", "-j", str(os.cpu_count() or 4), "--target", "llama-server"],
|
| 59 |
cwd="llama.cpp",
|
| 60 |
env=env,
|
| 61 |
-
check=True
|
| 62 |
)
|
| 63 |
|
| 64 |
if not os.path.exists(binary_path):
|
|
@@ -66,40 +69,69 @@ def build_latest_llama_server():
|
|
| 66 |
|
| 67 |
return binary_path
|
| 68 |
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
|
| 75 |
-
|
| 76 |
-
binary_path = build_latest_llama_server()
|
| 77 |
|
| 78 |
-
from huggingface_hub import hf_hub_download
|
| 79 |
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
filename="BF16/Qwen3.8-Flash-Next-BF16-00008-of-00008.gguf",
|
| 83 |
-
)
|
| 84 |
-
model_path = None
|
| 85 |
-
for i in range(1,34):
|
| 86 |
-
# mp = hf_hub_download(
|
| 87 |
-
# repo_id="AtomicChat/Qwen3.8-Flash-Next-GGUF",
|
| 88 |
-
# filename="Qwen3.8-Flash-Next-AD-4.27bpw-Q4_K_M-M64/Qwen3.8-Flash-Next-AD-4.27bpw-Q4_K_M-M64-{}-of-00033.gguf".format(str(i).zfill(5))
|
| 89 |
-
# )
|
| 90 |
-
if not model_path:
|
| 91 |
-
# model_path = mp
|
| 92 |
-
|
| 93 |
-
# mmproj_path = hf_hub_download(
|
| 94 |
-
# repo_id="AtomicChat/Qwen3.8-Flash-Next-GGUF",
|
| 95 |
-
# filename="mmproj-Qwen3.8-Flash-Next-F16.gguf",
|
| 96 |
-
# )
|
| 97 |
-
|
| 98 |
-
draft_path = hf_hub_download(
|
| 99 |
-
repo_id="unsloth/Qwen3.8-Flash-Next-GGUF",
|
| 100 |
-
filename="MTP/mtp-Qwen3.8-Flash-Next-Q4_K_M.gguf",
|
| 101 |
-
)
|
| 102 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 103 |
|
| 104 |
binary_dir = os.path.dirname(os.path.abspath(binary_path))
|
| 105 |
env = os.environ.copy()
|
|
@@ -109,7 +141,6 @@ def start_llama_server():
|
|
| 109 |
binary_path,
|
| 110 |
"-m", model_path,
|
| 111 |
"-mm", mmproj_path,
|
| 112 |
-
#"-md", draft_path,
|
| 113 |
"--port", "8000",
|
| 114 |
"--host", "127.0.0.1",
|
| 115 |
"-t", "16",
|
|
@@ -132,6 +163,7 @@ def start_llama_server():
|
|
| 132 |
time.sleep(5)
|
| 133 |
return process
|
| 134 |
|
|
|
|
| 135 |
@app.middleware("http")
|
| 136 |
async def proxy_middleware(request: Request, call_next):
|
| 137 |
path = request.url.path
|
|
@@ -139,9 +171,9 @@ async def proxy_middleware(request: Request, call_next):
|
|
| 139 |
return await call_next(request)
|
| 140 |
|
| 141 |
url = f"http://127.0.0.1:8000{path}"
|
| 142 |
-
|
| 143 |
headers = {
|
| 144 |
-
k: v for k, v in request.headers.items()
|
| 145 |
if k.lower() not in ("host", "content-length", "accept-encoding")
|
| 146 |
}
|
| 147 |
body = await request.body()
|
|
@@ -177,9 +209,10 @@ async def proxy_middleware(request: Request, call_next):
|
|
| 177 |
return StreamingResponse(
|
| 178 |
stream_and_close(),
|
| 179 |
status_code=response.status_code,
|
| 180 |
-
headers=res_headers
|
| 181 |
)
|
| 182 |
|
|
|
|
| 183 |
if __name__ == "__main__":
|
| 184 |
start_llama_server()
|
| 185 |
app.launch(show_error=True)
|
|
|
|
| 1 |
from __future__ import annotations
|
| 2 |
+
import glob
|
| 3 |
import os
|
| 4 |
import subprocess
|
|
|
|
| 5 |
import time
|
|
|
|
|
|
|
| 6 |
|
| 7 |
from fastapi import Request
|
| 8 |
from fastapi.responses import StreamingResponse
|
|
|
|
| 21 |
def whygpu():
|
| 22 |
pass
|
| 23 |
|
| 24 |
+
|
| 25 |
def build_latest_llama_server():
|
| 26 |
binary_path = os.path.abspath("llama.cpp/build/bin/llama-server")
|
| 27 |
if os.path.exists(binary_path):
|
|
|
|
| 34 |
else:
|
| 35 |
subprocess.run(["git", "pull"], cwd="llama.cpp", check=True)
|
| 36 |
|
| 37 |
+
# NOTE: no longer fetching/checking out the "mtp" branch (pull/144) here.
|
| 38 |
+
# That branch was specific to Qwen3.8-Flash-Next's MTP support and has
|
| 39 |
+
# nothing to do with GLM-5.3-Flash's architecture (glm5next). Keeping it
|
| 40 |
+
# would make llama.cpp fail to load this model. unslothai/llama.cpp's
|
| 41 |
+
# default branch already carries GLM-5.3-Flash support (PR ggml-org#27754
|
| 42 |
+
# is merged into their regular mix builds), so a plain clone/pull is
|
| 43 |
+
# enough. Everything else below is unchanged from your original.
|
| 44 |
|
| 45 |
env = os.environ.copy()
|
| 46 |
if "/usr/local/cuda/bin" not in env.get("PATH", ""):
|
|
|
|
| 61 |
["cmake", "--build", "build", "--config", "Release", "-j", str(os.cpu_count() or 4), "--target", "llama-server"],
|
| 62 |
cwd="llama.cpp",
|
| 63 |
env=env,
|
| 64 |
+
check=True,
|
| 65 |
)
|
| 66 |
|
| 67 |
if not os.path.exists(binary_path):
|
|
|
|
| 69 |
|
| 70 |
return binary_path
|
| 71 |
|
| 72 |
+
|
| 73 |
+
# ---------------------------------------------------------------------------
|
| 74 |
+
# Model: GLM-5.3-Flash (Z.ai), GGUF build by unsloth.
|
| 75 |
+
#
|
| 76 |
+
# Why this model: as of the strongest *practically self-hostable* open-weight
|
| 77 |
+
# agent model with an MIT license is GLM-5.3-Flash (320B total / 18B active,
|
| 78 |
+
# natively multimodal, hybrid sparse+linear attention). It leads open-weight
|
| 79 |
+
# results on agentic/coding benchmarks (Terminal-Bench 2.1, DeepSWE,
|
| 80 |
+
# Toolathlon-Verified) and is reported to approach Claude Opus 4.8 on
|
| 81 |
+
# coding/agentic tasks. Moonshot's Kimi K3 (2.8T) currently benchmarks a
|
| 82 |
+
# notch higher, but needs ~600GB+ of RAM/VRAM even at its smallest quant —
|
| 83 |
+
# not realistic for a single-GPU Space, so it isn't used here.
|
| 84 |
+
#
|
| 85 |
+
# QUANT: pick the smallest quant that fits your box. Sizes (unsloth Dynamic
|
| 86 |
+
# GGUF): UD-IQ1_S ≈93GB, UD-IQ1_M ≈98GB, UD-IQ2_XXS ≈102GB, UD-Q2_K_XL ≈109GB,
|
| 87 |
+
# UD-Q3_K_XL ≈148GB, UD-Q4_K_XL ≈200GB. Even the smallest quant will not fit
|
| 88 |
+
# on a free/ZeroGPU Space's default disk — you need a machine with enough
|
| 89 |
+
# disk + RAM (llama.cpp can stream/offload experts to CPU RAM and disk, it
|
| 90 |
+
# doesn't all need to be in VRAM, but it does need to fit somewhere).
|
| 91 |
+
# ---------------------------------------------------------------------------
|
| 92 |
+
MODEL_REPO = "unsloth/GLM-5.3-Flash-GGUF"
|
| 93 |
+
MODEL_QUANT = "UD-IQ1_S" # smallest available quant; raise if you have the disk/RAM
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
def download_model_shards(repo_id: str, quant_dir: str) -> str:
|
| 97 |
+
"""Downloads every shard of a (possibly multi-part) GGUF quant and
|
| 98 |
+
returns the path to the first shard, which is what llama-server needs
|
| 99 |
+
(it auto-discovers the rest of the shards next to it)."""
|
| 100 |
+
from huggingface_hub import snapshot_download
|
| 101 |
+
|
| 102 |
+
local_dir = snapshot_download(
|
| 103 |
+
repo_id=repo_id,
|
| 104 |
+
allow_patterns=[f"{quant_dir}/*"],
|
| 105 |
)
|
| 106 |
+
shard_dir = os.path.join(local_dir, quant_dir)
|
| 107 |
+
shards = sorted(glob.glob(os.path.join(shard_dir, "*-00001-of-*.gguf")))
|
| 108 |
+
if not shards:
|
| 109 |
+
# not sharded — just one file in the folder
|
| 110 |
+
shards = sorted(glob.glob(os.path.join(shard_dir, "*.gguf")))
|
| 111 |
+
if not shards:
|
| 112 |
+
raise RuntimeError(f"No GGUF files found under {shard_dir}")
|
| 113 |
+
return shards[0]
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
def download_mmproj(repo_id: str) -> str:
|
| 117 |
+
"""GLM-5.3-Flash is natively multimodal; grab the F16 mmproj file so
|
| 118 |
+
image input works with -mm, same as your original -mm usage."""
|
| 119 |
+
from huggingface_hub import hf_hub_download
|
| 120 |
|
| 121 |
+
return hf_hub_download(repo_id=repo_id, filename="mmproj-F16.gguf")
|
|
|
|
| 122 |
|
|
|
|
| 123 |
|
| 124 |
+
def start_llama_server():
|
| 125 |
+
binary_path = build_latest_llama_server()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
|
| 127 |
+
model_path = download_model_shards(MODEL_REPO, MODEL_QUANT)
|
| 128 |
+
mmproj_path = download_mmproj(MODEL_REPO)
|
| 129 |
+
# No separate draft_path/-md here: unlike Qwen3.8-Flash-Next, GLM-5.3-Flash
|
| 130 |
+
# doesn't ship (or need) a separate MTP draft GGUF - MTP is built into the
|
| 131 |
+
# main file via the llama.cpp PR mentioned above. There is no matching
|
| 132 |
+
# file to download from the repo, so the old draft_path download and the
|
| 133 |
+
# (already-commented-out) "-md" flag are simply gone rather than pointing
|
| 134 |
+
# at a file that doesn't exist.
|
| 135 |
|
| 136 |
binary_dir = os.path.dirname(os.path.abspath(binary_path))
|
| 137 |
env = os.environ.copy()
|
|
|
|
| 141 |
binary_path,
|
| 142 |
"-m", model_path,
|
| 143 |
"-mm", mmproj_path,
|
|
|
|
| 144 |
"--port", "8000",
|
| 145 |
"--host", "127.0.0.1",
|
| 146 |
"-t", "16",
|
|
|
|
| 163 |
time.sleep(5)
|
| 164 |
return process
|
| 165 |
|
| 166 |
+
|
| 167 |
@app.middleware("http")
|
| 168 |
async def proxy_middleware(request: Request, call_next):
|
| 169 |
path = request.url.path
|
|
|
|
| 171 |
return await call_next(request)
|
| 172 |
|
| 173 |
url = f"http://127.0.0.1:8000{path}"
|
| 174 |
+
|
| 175 |
headers = {
|
| 176 |
+
k: v for k, v in request.headers.items()
|
| 177 |
if k.lower() not in ("host", "content-length", "accept-encoding")
|
| 178 |
}
|
| 179 |
body = await request.body()
|
|
|
|
| 209 |
return StreamingResponse(
|
| 210 |
stream_and_close(),
|
| 211 |
status_code=response.status_code,
|
| 212 |
+
headers=res_headers,
|
| 213 |
)
|
| 214 |
|
| 215 |
+
|
| 216 |
if __name__ == "__main__":
|
| 217 |
start_llama_server()
|
| 218 |
app.launch(show_error=True)
|