Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 08714031ac | |||
| d4b33910dd | |||
| 0af75d8d3a | |||
| 9e7bf61114 | |||
| e7c97fa4a6 | |||
| 7a791431b5 | |||
| 9248c4223e | |||
| 296587fa21 | |||
| ed0d09d5f9 | |||
| 264c556fab | |||
| 982e8b4dd8 | |||
| 10e00642b5 | |||
| 3f7da1fbdc | |||
| 1ed3f7d4e5 | |||
| 978cef3f80 | |||
| 6506086360 | |||
| 818d8ea3de | |||
| 89dd4182bb | |||
| 9e1fbd5ab0 | |||
| 1fd8e461d9 | |||
| 80b703d0b5 | |||
| 938fec4951 | |||
| be8ea679ff | |||
| c949cc228c | |||
| 4c43bc264a | |||
| 826a46860d | |||
| c423296514 | |||
| 47c43c4934 |
257
main.py
257
main.py
@@ -1,16 +1,7 @@
|
||||
"""ModelHub XC 适配智能体 —— 单任务提交器
|
||||
"""ModelHub XC 适配智能体 —— 低排队算力批次(5 条)
|
||||
|
||||
启动后向 ModelHub XC 提交 1 条模型适配验证任务,随后保持 /health 存活等待平台停止。
|
||||
|
||||
提交内容(已查重,非重复模型):
|
||||
模型:https://www.modelscope.cn/models/qnguyen3/qwq-1.5b-grpo (Qwen2 / 1.5B)
|
||||
算力:Ascend_910-b4 框架:vllm 任务类型:text-generation
|
||||
|
||||
平台契约:
|
||||
- 根目录 Dockerfile,监听 8080
|
||||
- GET /health 探活
|
||||
- 读取平台注入的 STRATEGY_ID
|
||||
- 正确处理 SIGTERM
|
||||
每条任务分配一张低排队算力卡,并选用该卡历史通过率较高的框架。
|
||||
OSP 系列 max_position_embeddings=2048,配置里的 max_model_len 已由 4096 下调为 2048。
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
@@ -22,160 +13,79 @@ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
||||
|
||||
import requests
|
||||
|
||||
# ─────────────────────────── 业务配置 ───────────────────────────
|
||||
BASE_URL = os.environ.get("BASE_URL", "https://modelhub.org.cn")
|
||||
ADD_TASK_ENDPOINT = "/api/adapt/task/add"
|
||||
|
||||
XC_TOKEN = os.environ.get("XC_TOKEN", "6616dfe75c874e61987b1f7c908e6d77")
|
||||
STRATEGY_ID = os.environ.get("STRATEGY_ID", "")
|
||||
|
||||
MODEL_ADDRESS = "https://www.modelscope.cn/models/qnguyen3/qwq-1.5b-grpo"
|
||||
TASK_TYPE = "text-generation"
|
||||
TARGET_GPU = "Ascend_910-b4"
|
||||
FRAMEWORK = "vllm"
|
||||
|
||||
# 与 POST /api/adapt/task/build-config?gpuType=Ascend_910-b4&framework=vllm&taskType=text-generation 输出一致
|
||||
CONFIG_PARAMS = """framework: vllm
|
||||
api: completion
|
||||
max_tokens: 1024
|
||||
temperature: 0.7
|
||||
repetition_penalty: 1.2
|
||||
top_p: 0.9
|
||||
lang: zh
|
||||
max_model_len: 2048
|
||||
sut_config:
|
||||
gpu_num: 1
|
||||
values:
|
||||
command:
|
||||
- vllm
|
||||
- serve
|
||||
- /model
|
||||
- --port
|
||||
- '8000'
|
||||
- --served-model-name
|
||||
- llm
|
||||
- --max-model-len
|
||||
- '2048'
|
||||
- --dtype
|
||||
- auto
|
||||
- --gpu-memory-utilization
|
||||
- '0.95'
|
||||
- -tp
|
||||
- '1'
|
||||
- --enforce-eager
|
||||
- --trust-remote-code
|
||||
ref_config:
|
||||
gpu_num: 1
|
||||
values:
|
||||
command:
|
||||
- vllm
|
||||
- serve
|
||||
- /model
|
||||
- --port
|
||||
- '8000'
|
||||
- --served-model-name
|
||||
- llm
|
||||
- --max-model-len
|
||||
- '2048'
|
||||
- --dtype
|
||||
- auto
|
||||
- --gpu-memory-utilization
|
||||
- '0.95'
|
||||
- -tp
|
||||
- '1'
|
||||
- --enforce-eager
|
||||
- --trust-remote-code
|
||||
"""
|
||||
TASKS = [
|
||||
{
|
||||
"label": "DRPO-1.5B-Lambda-0.2 @ hygon",
|
||||
"model": "https://huggingface.co/mradermacher/DRPO-1.5B-Lambda-0.2-i1-GGUF",
|
||||
"gpu": "hygon_k100-ai",
|
||||
"framework": "llamacpp",
|
||||
"config": "framework: llamacpp\napi: completion\nmax_tokens: 1024\ntemperature: 0.7\nrepetition_penalty: 1.2\ntop_p: 0.9\nlang: en\nmax_model_len: 4096\nsut_config:\n gpu_num: 1\n values:\n command:\n - /app/llama-server\n - --model\n - /model/DRPO-1.5B-Lambda-0.2.i1-Q4_K_M.gguf\n - --alias\n - llm\n - --threads\n - '20'\n - --n-gpu-layers\n - '999'\n - --prio\n - '3'\n - --min_p\n - '0.01'\n - --ctx-size\n - '2048'\n - --host\n - 0.0.0.0\n - --port\n - '8000'\n - --jinja\n - --flash-attn\n - 'off'\nref_config:\n gpu_num: 1\n values:\n command:\n - /workspace/llama.cpp/build/bin/llama-server\n - --model\n - /model/DRPO-1.5B-Lambda-0.2.i1-Q4_K_M.gguf\n - --alias\n - llm\n - --threads\n - '20'\n - --n-gpu-layers\n - '128'\n - --ctx-size\n - '4096'\n - --host\n - 0.0.0.0\n - --port\n - '8000'\n - --jinja\n"
|
||||
},
|
||||
{
|
||||
"label": "Llama-3.2-1B-abliterated2 @ S4000",
|
||||
"model": "https://huggingface.co/mradermacher/Llama-3.2-1B-Instruct-abliterated2-i1-GGUF",
|
||||
"gpu": "Mthreads_s4000",
|
||||
"framework": "llamacpp",
|
||||
"config": "framework: llamacpp\napi: completion\nmax_tokens: 1024\nmax_model_len: 2048\nsut_config:\n gpu_num: 1\n values:\n command:\n - /app/llama-server\n - --model\n - /model/Llama-3.2-1B-Instruct-abliterated2.i1-Q4_K_M.gguf\n - --alias\n - llm\n - --threads\n - '20'\n - --n-gpu-layers\n - '128'\n - --ctx-size\n - '2048'\n - --host\n - 0.0.0.0\n - --port\n - '8000'\n - --jinja\n - --flash-attn\n - 'off'\nref_config:\n gpu_num: 1\n values:\n command:\n - /workspace/llama.cpp/build/bin/llama-server\n - --model\n - /model/Llama-3.2-1B-Instruct-abliterated2.i1-Q4_K_M.gguf\n - --alias\n - llm\n - --threads\n - '20'\n - --n-gpu-layers\n - '128'\n - --ctx-size\n - '2048'\n - --host\n - 0.0.0.0\n - --port\n - '8000'\n - --jinja\n - --flash-attn\n - 'off'\n"
|
||||
},
|
||||
{
|
||||
"label": "Llama-3.1-SuperNova-Lite @ hygon",
|
||||
"model": "https://huggingface.co/mradermacher/Llama-3.1-SuperNova-Lite-GGUF",
|
||||
"gpu": "hygon_k100-ai",
|
||||
"framework": "llamacpp",
|
||||
"config": "framework: llamacpp\napi: completion\nmax_tokens: 1024\ntemperature: 0.7\nrepetition_penalty: 1.2\ntop_p: 0.9\nlang: en\nmax_model_len: 4096\nsut_config:\n gpu_num: 1\n values:\n command:\n - /app/llama-server\n - --model\n - /model/Llama-3.1-SuperNova-Lite.Q4_K_M.gguf\n - --alias\n - llm\n - --threads\n - '20'\n - --n-gpu-layers\n - '999'\n - --prio\n - '3'\n - --min_p\n - '0.01'\n - --ctx-size\n - '2048'\n - --host\n - 0.0.0.0\n - --port\n - '8000'\n - --jinja\n - --flash-attn\n - 'off'\nref_config:\n gpu_num: 1\n values:\n command:\n - /workspace/llama.cpp/build/bin/llama-server\n - --model\n - /model/Llama-3.1-SuperNova-Lite.Q4_K_M.gguf\n - --alias\n - llm\n - --threads\n - '20'\n - --n-gpu-layers\n - '128'\n - --ctx-size\n - '4096'\n - --host\n - 0.0.0.0\n - --port\n - '8000'\n - --jinja\n"
|
||||
}
|
||||
]
|
||||
|
||||
HEADERS = {"Content-Type": "application/json", "Xc-Token": XC_TOKEN}
|
||||
|
||||
HTTP_HOST, HTTP_PORT = "0.0.0.0", 8080
|
||||
MAX_ATTEMPTS = 5
|
||||
RETRY_GAP = 15 # 秒
|
||||
|
||||
_state = {
|
||||
"strategy_id": STRATEGY_ID,
|
||||
"phase": "starting", # starting | submitting | done | error
|
||||
"model_address": MODEL_ADDRESS,
|
||||
"target_gpu": TARGET_GPU,
|
||||
"framework": FRAMEWORK,
|
||||
"attempts": 0,
|
||||
"attempt_log": [],
|
||||
"task_id": None,
|
||||
"started_at": None,
|
||||
"finished_at": None,
|
||||
}
|
||||
GAP = 4
|
||||
_shutdown = threading.Event()
|
||||
_submitting = threading.Lock()
|
||||
_submitted = False
|
||||
_state = {"phase": "starting", "total": len(TASKS), "submitted": 0, "failed": 0, "results": []}
|
||||
|
||||
|
||||
def submit_task():
|
||||
"""提交一次;已提交过则直接返回。"""
|
||||
global _submitted
|
||||
with _submitting:
|
||||
if _submitted:
|
||||
return True
|
||||
|
||||
payload = {
|
||||
"configParams": CONFIG_PARAMS,
|
||||
"framework": FRAMEWORK,
|
||||
"modelAddress": MODEL_ADDRESS,
|
||||
"targetGpu": TARGET_GPU,
|
||||
def submit_one(t):
|
||||
body = {
|
||||
"configParams": t["config"],
|
||||
"framework": t["framework"],
|
||||
"modelAddress": t["model"],
|
||||
"targetGpu": t["gpu"],
|
||||
"taskType": TASK_TYPE,
|
||||
"strategyId": STRATEGY_ID,
|
||||
}
|
||||
try:
|
||||
r = requests.post(BASE_URL + ADD_TASK_ENDPOINT, headers=HEADERS, json=body, timeout=90)
|
||||
j = r.json()
|
||||
except Exception as e: # noqa: BLE001
|
||||
j = {"code": -1, "message": "exception: %s" % e}
|
||||
tid = (j.get("data") or {}).get("id")
|
||||
print("[submit] %s gpu=%s fw=%s code=%s msg=%s taskId=%s"
|
||||
% (t["label"], t["gpu"], t["framework"], j.get("code"), j.get("message"), tid), flush=True)
|
||||
return j.get("code"), tid
|
||||
|
||||
|
||||
def run():
|
||||
_state["phase"] = "submitting"
|
||||
_state["started_at"] = datetime.utcnow().isoformat()
|
||||
|
||||
for i in range(1, MAX_ATTEMPTS + 1):
|
||||
for i, t in enumerate(TASKS, 1):
|
||||
if _shutdown.is_set():
|
||||
break
|
||||
_state["attempts"] = i
|
||||
try:
|
||||
resp = requests.post(BASE_URL + ADD_TASK_ENDPOINT, headers=HEADERS,
|
||||
json=payload, timeout=60)
|
||||
body = resp.json()
|
||||
except Exception as e: # noqa: BLE001
|
||||
body = {"code": -1, "message": "exception: %s" % e}
|
||||
|
||||
code = body.get("code")
|
||||
msg = body.get("message", "")
|
||||
entry = {"attempt": i, "time": datetime.utcnow().isoformat(),
|
||||
"http_status": None, "code": code, "message": msg}
|
||||
try:
|
||||
entry["http_status"] = resp.status_code
|
||||
except Exception: # noqa: BLE001
|
||||
pass
|
||||
_state["attempt_log"].append(entry)
|
||||
print("[submit] #%d code=%s message=%s" % (i, code, msg), flush=True)
|
||||
|
||||
if code == 0:
|
||||
data = body.get("data") or {}
|
||||
_state["task_id"] = data.get("id")
|
||||
_state["phase"] = "done"
|
||||
_state["finished_at"] = datetime.utcnow().isoformat()
|
||||
_submitted = True
|
||||
print("[submit] 提交成功 taskId=%s" % _state["task_id"], flush=True)
|
||||
return True
|
||||
|
||||
# 已存在/重复等不可重试错误,直接退出
|
||||
if code not in (-1, 60014, 50000):
|
||||
_state["phase"] = "error"
|
||||
_state["finished_at"] = datetime.utcnow().isoformat()
|
||||
print("[submit] 终止:不可重试错误", flush=True)
|
||||
return False
|
||||
|
||||
if i < MAX_ATTEMPTS and not _shutdown.is_set():
|
||||
time.sleep(RETRY_GAP)
|
||||
|
||||
_state["phase"] = "error"
|
||||
_state["finished_at"] = datetime.utcnow().isoformat()
|
||||
return False
|
||||
code, tid = submit_one(t)
|
||||
_state["results"].append({"label": t["label"], "gpu": t["gpu"],
|
||||
"framework": t["framework"], "code": code, "task_id": tid})
|
||||
_state["submitted" if code == 0 else "failed"] += 1
|
||||
if i < len(TASKS):
|
||||
time.sleep(GAP)
|
||||
_state["phase"] = "done"
|
||||
print("[batch] 完成 submitted=%d failed=%d" % (_state["submitted"], _state["failed"]), flush=True)
|
||||
|
||||
|
||||
class Handler(BaseHTTPRequestHandler):
|
||||
class H(BaseHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
if self.path == "/health":
|
||||
self._json({"status": "ok"})
|
||||
@@ -184,56 +94,29 @@ class Handler(BaseHTTPRequestHandler):
|
||||
else:
|
||||
self._json({"error": "not found"}, 404)
|
||||
|
||||
def do_POST(self):
|
||||
if self.path == "/task":
|
||||
submit_task()
|
||||
self._json(_state)
|
||||
else:
|
||||
self._json({"error": "not found"}, 404)
|
||||
|
||||
def _json(self, body, code=200):
|
||||
raw = json.dumps(body, ensure_ascii=False, default=str).encode()
|
||||
self.send_response(code)
|
||||
def _json(self, b, c=200):
|
||||
raw = json.dumps(b, ensure_ascii=False, default=str).encode()
|
||||
self.send_response(c)
|
||||
self.send_header("Content-Type", "application/json")
|
||||
self.send_header("Content-Length", str(len(raw)))
|
||||
self.end_headers()
|
||||
self.wfile.write(raw)
|
||||
|
||||
def log_message(self, fmt, *args):
|
||||
print(self.address_string() + " - " + (fmt % args), flush=True)
|
||||
|
||||
|
||||
def _handle_signal(signum, _frame):
|
||||
print("[main] 收到信号 %s,准备退出" % signum, flush=True)
|
||||
_shutdown.set()
|
||||
def log_message(self, *a):
|
||||
pass
|
||||
|
||||
|
||||
def main():
|
||||
signal.signal(signal.SIGTERM, _handle_signal)
|
||||
signal.signal(signal.SIGINT, _handle_signal)
|
||||
|
||||
print("[main] strategy_id=%s" % STRATEGY_ID, flush=True)
|
||||
print("[main] model=%s gpu=%s framework=%s" % (MODEL_ADDRESS, TARGET_GPU, FRAMEWORK), flush=True)
|
||||
|
||||
http_thread = threading.Thread(target=_run_http, daemon=False)
|
||||
http_thread.start()
|
||||
|
||||
worker = threading.Thread(target=submit_task, daemon=True)
|
||||
worker.start()
|
||||
|
||||
_shutdown.wait()
|
||||
print("[main] 退出", flush=True)
|
||||
|
||||
|
||||
def _run_http():
|
||||
server = ThreadingHTTPServer((HTTP_HOST, HTTP_PORT), Handler)
|
||||
server.timeout = 1
|
||||
print("[http] 监听 %s:%d" % (HTTP_HOST, HTTP_PORT), flush=True)
|
||||
signal.signal(signal.SIGTERM, lambda *_: _shutdown.set())
|
||||
print("[main] strategy_id=%s tasks=%d" % (STRATEGY_ID, len(TASKS)), flush=True)
|
||||
for t in TASKS:
|
||||
print("[main] 计划: %s @ %s / %s" % (t["label"], t["gpu"], t["framework"]), flush=True)
|
||||
srv = ThreadingHTTPServer((HTTP_HOST, HTTP_PORT), H)
|
||||
srv.timeout = 1
|
||||
threading.Thread(target=run, daemon=True).start()
|
||||
while not _shutdown.is_set():
|
||||
server.handle_request()
|
||||
server.server_close()
|
||||
print("[http] 已关闭", flush=True)
|
||||
srv.handle_request()
|
||||
srv.server_close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user