"""ModelHub XC 适配智能体 —— 多任务提交器(本批次 4 条) 启动后依次提交 4 条已查重的模型适配验证任务,随后保持 /health 存活等待平台停止。 批次清单(提交前均查过:模型列表 0 条 / 全站任务 0 条 / 详情接口无验证记录): 1. mrm8488/DeepSeek-R1-Distill-Qwen-1.5B-grpo-limo-2e Qwen2 1.5B Ascend_910-b4 vllm 2. HollowInky/skill-search-qwen3-4b-unmasked-grpo Qwen3 4B Ascend_910-b4 vllm 3. dmis-lab/OSP-1.4B-100B-Muon Llama 1.4B Ascend_910-b4 vllm 4. qnguyen3/quan-1.8b-chat Llama 1.8B Ascend_910-b3 vllm 平台契约:根目录 Dockerfile,监听 8080,GET /health 探活,读取 STRATEGY_ID,处理 SIGTERM。 """ import json import os import signal import threading import time from datetime import datetime 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", "") # 910B4 模板(max_model_len 2048) CFG_B4 = """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""" # 910B3 模板(max_model_len 4096) CFG_B3 = """framework: vllm api: completion lang: en max_model_len: 4096 max_tokens: 1024 temperature: 0.7 repetition_penalty: 1.1 top_p: 0.9 sut_config: gpu_num: 1 values: command: - vllm - serve - /model - --port - '20644' - --served-model-name - llm - --max-model-len - '4096' - -tp - '1' - --enforce-eager - --trust-remote-code ref_config: gpu_num: 1 values: command: - vllm - serve - /model - --port - '80' - --served-model-name - llm - --max-model-len - '4096' - -tp - '1' - --enforce-eager - --trust-remote-code""" TASKS = [ { "label": "OSP-1.4B-100B-Adam", "model": "https://www.modelscope.cn/models/dmis-lab/OSP-1.4B-100B-Adam", "gpu": "Ascend_910-b4", "config": CFG_B4, }, { "label": "OSP-1.4B-1T-Adam", "model": "https://www.modelscope.cn/models/dmis-lab/OSP-1.4B-1T-Adam", "gpu": "Ascend_910-b4", "config": CFG_B4, }, ] TASK_TYPE = "text-generation" FRAMEWORK = "vllm" HEADERS = {"Content-Type": "application/json", "Xc-Token": XC_TOKEN} HTTP_HOST, HTTP_PORT = "0.0.0.0", 8080 GAP = 4 # 两条之间的间隔秒数 _state = { "strategy_id": STRATEGY_ID, "phase": "starting", # starting | submitting | done "total": len(TASKS), "submitted": 0, "failed": 0, "results": [], "started_at": None, "finished_at": None, } _shutdown = threading.Event() def submit_one(task): payload = { "configParams": task["config"], "framework": FRAMEWORK, "modelAddress": task["model"], "targetGpu": task["gpu"], "taskType": TASK_TYPE, "strategyId": STRATEGY_ID, } try: resp = requests.post(BASE_URL + ADD_TASK_ENDPOINT, headers=HEADERS, json=payload, timeout=90) body = resp.json() except Exception as e: # noqa: BLE001 body = {"code": -1, "message": "exception: %s" % e} code = body.get("code") data = body.get("data") or {} print("[submit] %s gpu=%s code=%s message=%s taskId=%s" % (task["label"], task["gpu"], code, body.get("message"), data.get("id")), flush=True) return code, body.get("message"), data.get("id") def run_batch(): _state["phase"] = "submitting" _state["started_at"] = datetime.utcnow().isoformat() for i, task in enumerate(TASKS, 1): if _shutdown.is_set(): break code, msg, task_id = submit_one(task) _state["results"].append({ "index": i, "label": task["label"], "model": task["model"], "gpu": task["gpu"], "code": code, "message": msg, "task_id": task_id, "time": datetime.utcnow().isoformat(), }) if code == 0: _state["submitted"] += 1 else: _state["failed"] += 1 if i < len(TASKS): time.sleep(GAP) _state["phase"] = "done" _state["finished_at"] = datetime.utcnow().isoformat() print("[batch] 完成 submitted=%d failed=%d total=%d" % (_state["submitted"], _state["failed"], _state["total"]), flush=True) class Handler(BaseHTTPRequestHandler): def do_GET(self): if self.path == "/health": self._json({"status": "ok"}) elif self.path in ("/", "/status"): 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) 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 _run_http(): server = ThreadingHTTPServer((HTTP_HOST, HTTP_PORT), Handler) server.timeout = 1 print("[http] 监听 %s:%d" % (HTTP_HOST, HTTP_PORT), flush=True) while not _shutdown.is_set(): server.handle_request() server.server_close() def main(): signal.signal(signal.SIGTERM, _handle_signal) signal.signal(signal.SIGINT, _handle_signal) print("[main] strategy_id=%s tasks=%d" % (STRATEGY_ID, len(TASKS)), flush=True) for t in TASKS: print("[main] 计划: %s @ %s" % (t["label"], t["gpu"]), flush=True) http_thread = threading.Thread(target=_run_http, daemon=False) http_thread.start() worker = threading.Thread(target=run_batch, daemon=True) worker.start() _shutdown.wait() if __name__ == "__main__": main()