zhengr commited on
Commit
b788430
·
verified ·
1 Parent(s): 7c6160c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +120 -81
app.py CHANGED
@@ -1,114 +1,153 @@
1
  from __future__ import annotations
2
 
 
3
  import os
4
  import sys
5
- import json
6
- import subprocess
7
  from pathlib import Path
8
- from typing import Any, Dict
9
-
10
- import requests
11
- from fastapi import FastAPI
12
- from fastapi.responses import JSONResponse
13
- import uvicorn
14
 
 
15
 
16
- # ======================
17
- # Config
18
- # ======================
19
- MODEL_PATH = os.getenv("MODEL_PATH", "/data/models/model.gguf")
 
20
 
21
  LLAMA_SERVER_BIN = os.getenv("LLAMA_SERVER_BIN", "/opt/llama.cpp/llama-server")
22
- LLAMA_HOST = os.getenv("LLAMA_HOST", "127.0.0.1")
23
- LLAMA_PORT = int(os.getenv("LLAMA_PORT", "7860"))
24
 
25
- API_HOST = os.getenv("API_HOST", "0.0.0.0")
26
- API_PORT = int(os.getenv("API_PORT", "8000"))
27
 
 
28
  THREADS = os.getenv("THREADS", "4")
29
- CTX_SIZE = os.getenv("CTX_SIZE", "4096")
 
 
30
  GPU_LAYERS = os.getenv("GPU_LAYERS", "0")
 
 
 
31
 
 
 
 
 
32
 
33
- # ======================
34
- # Start llama.cpp server
35
- # ======================
36
- def start_llama_server():
37
- cmd = [
38
- LLAMA_SERVER_BIN,
39
- "-m", MODEL_PATH,
40
- "--host", LLAMA_HOST,
41
- "--port", str(LLAMA_PORT),
42
- "--threads", str(THREADS),
43
- "--ctx-size", str(CTX_SIZE),
44
- "--n-gpu-layers", str(GPU_LAYERS),
45
- "--parallel", "1",
46
- "--cont-batching",
47
- ]
48
-
49
- print("[startup] launching llama.cpp server:")
50
- print(" ".join(cmd), flush=True)
51
 
52
- return subprocess.Popen(cmd, stdout=sys.stdout, stderr=sys.stderr)
 
53
 
54
 
55
- # ======================
56
- # OpenAI-compatible API layer
57
- # ======================
58
- app = FastAPI()
 
 
59
 
 
 
 
 
 
 
 
 
 
60
 
61
- def llama_chat_completion(payload: Dict[str, Any]) -> Dict[str, Any]:
62
- """
63
- Forward to llama.cpp OpenAI-compatible endpoint
64
- """
65
- url = f"http://{LLAMA_HOST}:{LLAMA_PORT}/v1/chat/completions"
66
 
67
- resp = requests.post(url, json=payload, timeout=600)
68
- resp.raise_for_status()
69
- return resp.json()
 
70
 
 
 
 
71
 
72
- @app.post("/v1/chat/completions")
73
- def chat_completions(body: Dict[str, Any]):
74
- """
75
- OpenAI-compatible endpoint
76
- """
77
  try:
78
- result = llama_chat_completion(body)
79
- return JSONResponse(content=result)
80
- except Exception as e:
81
- return JSONResponse(
82
- status_code=500,
83
- content={
84
- "error": {
85
- "message": str(e),
86
- "type": "server_error",
87
- }
88
- },
89
- )
 
 
 
 
90
 
91
 
92
- @app.get("/health")
93
- def health():
94
- return {"status": "ok"}
95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
 
97
- # ======================
98
- # Main
99
- # ======================
100
- def main():
101
- # 1. start llama.cpp backend
102
- proc = start_llama_server()
 
103
 
104
- # 2. wait a bit for server init
105
- import time
106
- time.sleep(3)
107
 
108
- # 3. start OpenAI API gateway
109
- print(f"[startup] OpenAI API server running on http://{API_HOST}:{API_PORT}")
110
- uvicorn.run(app, host=API_HOST, port=API_PORT)
 
 
 
 
 
 
 
 
 
 
 
 
 
111
 
112
 
113
  if __name__ == "__main__":
114
- main()
 
 
 
 
 
1
  from __future__ import annotations
2
 
3
+ import json
4
  import os
5
  import sys
6
+ import urllib.parse
7
+ import urllib.request
8
  from pathlib import Path
 
 
 
 
 
 
9
 
10
+ from huggingface_hub import hf_hub_download
11
 
12
+ # 配置项 (将路径重命名为通用命名,避免 gemma/mixtao 混淆)
13
+ MODEL_REPO = os.getenv("MODEL_REPO", "mixtao/MixTAO-7Bx2-MoE-v8.1-GGUF")
14
+ MODEL_FILE = os.getenv("MODEL_FILE", "mixtao-7bx2-moe-v8.1.Q4_K_M.gguf")
15
+ MODEL_DIR = Path(os.getenv("MODEL_DIR", "/data/models/llm"))
16
+ CHAT_TEMPLATE_FILE = Path(os.getenv("CHAT_TEMPLATE_FILE", "/data/models/llm/chat_template.jinja"))
17
 
18
  LLAMA_SERVER_BIN = os.getenv("LLAMA_SERVER_BIN", "/opt/llama.cpp/llama-server")
19
+ LLAMA_HOST = os.getenv("LLAMA_HOST", "0.0.0.0")
20
+ LLAMA_PORT = os.getenv("LLAMA_PORT", "7860")
21
 
22
+ # OpenAI API 安全配置
23
+ API_KEY = os.getenv("API_KEY", "") # 如果设置,客户端请求必须携带 Bearer Token
24
 
25
+ # 性能及运行参数
26
  THREADS = os.getenv("THREADS", "4")
27
+ CTX_SIZE = os.getenv("CTX_SIZE", "2048")
28
+ BATCH_SIZE = os.getenv("BATCH_SIZE", "default")
29
+ UBATCH_SIZE = os.getenv("UBATCH_SIZE", "default")
30
  GPU_LAYERS = os.getenv("GPU_LAYERS", "0")
31
+ FLASH_ATTN = os.getenv("FLASH_ATTN", "false")
32
+ CACHE_TYPE_K = os.getenv("CACHE_TYPE_K", "default")
33
+ CACHE_TYPE_V = os.getenv("CACHE_TYPE_V", "default")
34
 
35
+ TEMPERATURE = os.getenv("TEMPERATURE", "0.2")
36
+ TOP_P = os.getenv("TOP_P", "0.95")
37
+ TOP_K = os.getenv("TOP_K", "64")
38
+ REPEAT_PENALTY = os.getenv("REPEAT_PENALTY", "1.08")
39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
 
41
+ def log(message: str) -> None:
42
+ print(f"[startup] {message}", flush=True)
43
 
44
 
45
+ def download_model() -> str:
46
+ MODEL_DIR.mkdir(parents=True, exist_ok=True)
47
+ local_file = MODEL_DIR / MODEL_FILE
48
+ if local_file.exists():
49
+ log(f"Using cached model: {local_file}")
50
+ return str(local_file)
51
 
52
+ log(f"Downloading {MODEL_REPO}/{MODEL_FILE}")
53
+ model_path = hf_hub_download(
54
+ repo_id=MODEL_REPO,
55
+ filename=MODEL_FILE,
56
+ local_dir=str(MODEL_DIR),
57
+ local_dir_use_symlinks=False
58
+ )
59
+ log(f"Model ready: {model_path}")
60
+ return model_path
61
 
 
 
 
 
 
62
 
63
+ def download_chat_template() -> str | None:
64
+ if CHAT_TEMPLATE_FILE.exists() and CHAT_TEMPLATE_FILE.stat().st_size > 0:
65
+ log(f"Using cached chat template: {CHAT_TEMPLATE_FILE}")
66
+ return str(CHAT_TEMPLATE_FILE)
67
 
68
+ encoded_repo = urllib.parse.quote(MODEL_REPO, safe="/")
69
+ api_url = f"https://huggingface.co{encoded_repo}"
70
+ log("Fetching chat template from model metadata")
71
 
 
 
 
 
 
72
  try:
73
+ req = urllib.request.Request(api_url, headers={"User-Agent": "Mozilla/5.0"})
74
+ with urllib.request.urlopen(req, timeout=30) as response:
75
+ metadata = json.loads(response.read().decode("utf-8"))
76
+ except Exception as exc:
77
+ log(f"Could not fetch chat template metadata: {exc}")
78
+ return None
79
+
80
+ template = (metadata.get("gguf") or {}).get("chat_template")
81
+ if not template:
82
+ log("No chat template found in model metadata; llama-server will use GGUF metadata")
83
+ return None
84
+
85
+ CHAT_TEMPLATE_FILE.parent.mkdir(parents=True, exist_ok=True)
86
+ CHAT_TEMPLATE_FILE.write_text(template, encoding="utf-8")
87
+ log(f"Chat template ready: {CHAT_TEMPLATE_FILE}")
88
+ return str(CHAT_TEMPLATE_FILE)
89
 
90
 
91
+ def build_command(model_path: str, template_path: str | None) -> list[str]:
92
+ def is_valid(value: str) -> bool:
93
+ return value.strip().lower() not in {"", "default", "auto", "none", "off", "false"}
94
 
95
+ cmd = [
96
+ LLAMA_SERVER_BIN,
97
+ "-m", model_path,
98
+ "--host", LLAMA_HOST,
99
+ "--port", LLAMA_PORT,
100
+ "--threads", THREADS,
101
+ "--ctx-size", CTX_SIZE,
102
+ "--n-gpu-layers", GPU_LAYERS,
103
+ "--parallel", "4", # 开启多并发槽位以支持多用户同时调用 API
104
+ "--cont-batching", # 启用连续批处理以优化 API 吞吐量
105
+ "--temp", TEMPERATURE,
106
+ "--top-p", TOP_P,
107
+ "--top-k", TOP_K,
108
+ "--repeat-penalty", REPEAT_PENALTY,
109
+ ]
110
+
111
+ # 核心改造:注入 OpenAI API 专属控制参数
112
+ cmd.append("--embedding") # 允许对外提供 OpenAI 兼容的 /v1/embeddings 接口
113
+
114
+ if API_KEY.strip():
115
+ cmd.extend(["--api-key", API_KEY.strip()]) # 启用 API 鉴权机制
116
+ log("OpenAI API authentication enabled with custom API_KEY.")
117
+ else:
118
+ log("Warning: Running without API_KEY. Server is publicly accessible.")
119
 
120
+ # 动态参数注入
121
+ if is_valid(BATCH_SIZE): cmd.extend(["--batch-size", BATCH_SIZE])
122
+ if is_valid(UBATCH_SIZE): cmd.extend(["--ubatch-size", UBATCH_SIZE])
123
+ if is_valid(CACHE_TYPE_K): cmd.extend(["--cache-type-k", CACHE_TYPE_K])
124
+ if is_valid(CACHE_TYPE_V): cmd.extend(["--cache-type-v", CACHE_TYPE_V])
125
+ if is_valid(FLASH_ATTN): cmd.append("--flash-attn")
126
+ if template_path: cmd.extend(["--chat-template-file", template_path])
127
 
128
+ return cmd
 
 
129
 
130
+
131
+ def main() -> None:
132
+ binary_dir = str(Path(LLAMA_SERVER_BIN).parent)
133
+ existing_lib = os.environ.get("LD_LIBRARY_PATH")
134
+ os.environ["LD_LIBRARY_PATH"] = binary_dir if not existing_lib else f"{binary_dir}:{existing_lib}"
135
+
136
+ for env_var in ["OMP_NUM_THREADS", "OPENBLAS_NUM_THREADS", "MKL_NUM_THREADS"]:
137
+ os.environ.setdefault(env_var, THREADS)
138
+
139
+ model_path = download_model()
140
+ template_path = download_chat_template()
141
+ cmd = build_command(model_path, template_path)
142
+
143
+ log("Starting OpenAI-compatible llama.cpp API server")
144
+ log(" ".join(cmd))
145
+ os.execvpe(cmd[0], cmd, os.environ)
146
 
147
 
148
  if __name__ == "__main__":
149
+ try:
150
+ main()
151
+ except Exception as exc:
152
+ print(f"[fatal] {exc}", file=sys.stderr, flush=True)
153
+ raise