""" xc_validation_strategy_vllm_submit — 主入口 启动后通过 /api/adapt/task/add 接口(xc-Token 认证)批量提交 vLLM 模型适配任务(ppu_zw_810e),之后保持 HTTP 服务存活。 同时暴露 /health(K8s 探活)和 /status(运行状态)。 部署框架与 xc_validation_strategy 一致。 """ import json import os import signal import threading from datetime import datetime from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from typing import List import requests # ══════════════════════════════════════════════════════════ # 配置 # ══════════════════════════════════════════════════════════ BASE_URL = os.environ.get("BASE_URL", "https://modelhub.org.cn") ADD_TASK_ENDPOINT = "/api/adapt/task/add" # fanyi 账号的 xc-Token(该接口使用 xc-Token 认证,无需登录) USER_ACCOUNT = "fanyi" XC_TOKEN = "f2d501c9ae6543a589cd6cb789108c41" GPU_TYPE = "ppu_zw_810e" TASK_TYPE = "text-generation" STRATEGY_ID = os.environ.get("STRATEGY_ID", "") # 平台自动注入,无需修改 HEADERS = { "Content-Type": "application/json", "xc-Token": XC_TOKEN, } HTTP_HOST = "0.0.0.0" HTTP_PORT = 8080 # ══════════════════════════════════════════════════════════ # 模型列表 # ══════════════════════════════════════════════════════════ ALL_MODEL_IDS = [ "Hyeji0101/qwen2_5_1_5b_demo", "GenueAI/geode-onyx", "GM77/qwen3-4b-verilog-grpo", "ChuGyouk/F_R13_T2", "ChuGyouk/R17", "Ingingdo/bit-0.5b-final-logic", "beomi/Llama-3-Open-Ko-8B", "Fiscus/trinitite_safe_rl_base_model", "ChuGyouk/F_R12_T3", "ChuGyouk/F_R12_T2", "xw1234gan/cnk12_Main_fixed_SFTanchor_3B_step_9", "xw1234gan/cnk12_Main_fixed_SFTanchor_3B_step_10", "xw1234gan/cnk12_Main_fixed_BaseAnchor_3B_step_1", "kmseong/llama3_2_3b-instruct-math-safedelta-scale0.99", "opencompass/anah-v2", "ChuGyouk/R14", "trishajean/qwen-math-cebuano-1.5b-merged", "GyanAISystems/Gyan-AI-G1-Official", "Divij/Qwen2.5-3B-Instruct-sft-without-thoughts", "Divij/Qwen2.5-3B-Instruct-sft-with-thoughts", "ChuGyouk/R5_1", "ChuGyouk/R18_1", "ChuGyouk/R19_1", "ChuGyouk/R12", "ChuGyouk/F_R11_T4", "ChuGyouk/F_R12", "ChuGyouk/F_R11_T2", "ChuGyouk/F_R11_T3", "ChuGyouk/F_R13_1_T1", "ChuGyouk/F_R12_T4", "automerger/T3qm7xNeuralsirkrishna-7B", "Ford91/clifford-ai-v2", "ChuGyouk/R16_1", "ChuGyouk/R15_1", "nkatara/gita-text-generation-gpt2", "HINT-lab/Qwen2.5-7B-Instruct-Self-Calibration", "thirdeyeai/Qwen2.5-1.5B-Instruct-uncensored", "karaselerm/qwen2.5-1.5b-instruct-ru-abliterated-hw6", "xw1234gan/cnk12_Main_fixed_BaseAnchor_3B_step_2", "ontocord/wide_3b_sft_stage1.1-ss1-with_intr_math.no_issue", "mncai/Foundation_Law_epoch4", "gauri0508/med-record-audit-qwen2.5-3b-grpo", "unsloth/Phi-4-mini-instruct", "E-motionAssistant/qwen-2.5-3b-tamil-therapy-merged", "EscapeJeju/qwen2_5_1_5b_demo", "AgPerry/Qwen3-8B-fim-v2v3pt-swe-lego-posttrain", "ChuGyouk/F_R11", "ChuGyouk/F_R11_1_T1", "LorenaYannnnn/general_reward-Qwen3-0.6B-OURS_self-seed_1", "Vortex5/Crimson-Constellation-12B", "cloudyu/mistral_11B_instruct_v0.1", "pkupie/Qwen2.5-3B-ug-cpt", "iproskurina/qwen-hf-fewshot-iter-np-iter3", "ontocord/wide_3b_sft_stage1.2-ss1-expert_wiki", "kmseong/llama3_2_3b-instruct-math-safedelta-scale2", "Thrillcrazyer/Qwen-2.5-1.5B_TAC_Teacher_Qwen32B", "nyu-dice-lab/VeriThoughts-Reasoning-7B", "ontocord/wide_3b", "silvercoder67/Mistral-7b-instruct-v0.2-summ-sft-e2m", "lihaoxin2020/qwen3-4b-sft-gpt54-ep2-instance-rubric-gpt54-step200", "lihaoxin2020/qwen3-4b-sft-gpt54-ep2-instance-rubric-gpt54-step150", "lihaoxin2020/qwen3-4b-sft-gpt54-ep2-evolving-rubric-gem3-flash-step150", "Guilherme34/Firefly-V3", ] # ══════════════════════════════════════════════════════════ # 全局状态(供 /status 展示) # ══════════════════════════════════════════════════════════ _state = { "strategy_id": STRATEGY_ID, "phase": "starting", # starting | submitting | done | error "total": len(ALL_MODEL_IDS), "submitted": 0, "failed": 0, "started_at": None, "finished_at": None, } _shutdown = threading.Event() # ══════════════════════════════════════════════════════════ # HTTP 服务 # ══════════════════════════════════════════════════════════ class Handler(BaseHTTPRequestHandler): def do_GET(self): if self.path == "/health": self._json({"status": "ok"}) elif self.path == "/status": self._json(_state) else: self._json({"error": "not found"}, 404) def _json(self, body: dict, code: int = 200): payload = json.dumps(body, default=str).encode() self.send_response(code) self.send_header("Content-Type", "application/json") self.send_header("Content-Length", str(len(payload))) self.end_headers() self.wfile.write(payload) def log_message(self, fmt, *args): print(f"[http] {self.address_string()} {fmt % args}", flush=True) def _run_http(): server = ThreadingHTTPServer((HTTP_HOST, HTTP_PORT), Handler) server.timeout = 1 print(f"[http] 监听 {HTTP_HOST}:{HTTP_PORT}", flush=True) while not _shutdown.is_set(): server.handle_request() server.server_close() print("[http] 已关闭", flush=True) # ══════════════════════════════════════════════════════════ # 业务逻辑 # ══════════════════════════════════════════════════════════ def submit_task(model_id: str) -> bool: config_content = f""" gpu_type: ppu_zw_810e framework: vllm docker_image: harbor.4pd.io/hardcore-tech/asllm:1.10.1-pytorch2.10.0-ubuntu24.04-sail2.1.0-cuda13.0-sglang0.5.10-vllm0.19.0-py312 nv_docker_image: harbor-contest.4pd.io/sunruoxi/vllm-openai-fix-tokenizer:v0.11.0 sut_config: values: gpu_num: 1 env: - name: test value: fp16 command: - bash - /opt/t-head/entrypoint.sh - python3 - -m - asllm.entrypoints.api_server - --model - /model - --port - '30000' - --host - 0.0.0.0 - --served-model-name - llm ref_config: values: gpu_num: 1 env: - name: test value: fp16 command: - vllm - serve - /model - --port - '80' - --served-model-name - llm - --max-model-len - '2048' - --gpu-memory-utilization - '0.9' - --enforce-eager - --trust-remote-code - -tp - '1' """ payload = { "configParams": config_content, "framework": "vllm", "modelAddress": f"https://huggingface.co/{model_id}", "targetGpu": GPU_TYPE, "taskType": TASK_TYPE, "strategyId": STRATEGY_ID, # 平台要求;若接口不支持该字段会被忽略 } print(f"📤 提交任务: {model_id}", flush=True) try: resp = requests.post( BASE_URL + ADD_TASK_ENDPOINT, headers=HEADERS, json=payload, timeout=30, ) print(f"status: {resp.status_code}", flush=True) result = resp.json() print(result, flush=True) if result.get("code") == 0: print(f"✅ 提交成功: {model_id}", flush=True) return True else: print(f"❌ 提交失败: {result.get('message')}", flush=True) return False except Exception as e: print(f"💥 异常 ({model_id}): {e}", flush=True) return False def _run_worker(): _state["started_at"] = datetime.utcnow().isoformat() _state["phase"] = "submitting" successful: List[str] = [] for model_id in ALL_MODEL_IDS: if _shutdown.is_set(): break if submit_task(model_id): _state["submitted"] += 1 successful.append(model_id) else: _state["failed"] += 1 # 写入结果文件 try: with open("submitted_adapt_tasks.txt", "w", encoding="utf-8") as f: for mid in successful: f.write(f"{mid}\n") except Exception: pass _state["finished_at"] = datetime.utcnow().isoformat() _state["phase"] = "done" print( f"[worker] 完成 submitted={_state['submitted']} failed={_state['failed']} total={_state['total']}", flush=True, ) # 提交完成后继续保持进程存活,等待平台停止 # ══════════════════════════════════════════════════════════ # 入口 # ══════════════════════════════════════════════════════════ def _handle_signal(signum, _frame): print(f"[main] 收到信号 {signum},正在关闭...", flush=True) _shutdown.set() def main(): signal.signal(signal.SIGTERM, _handle_signal) signal.signal(signal.SIGINT, _handle_signal) # HTTP 服务线程 http_thread = threading.Thread(target=_run_http, daemon=False) http_thread.start() # 提交任务线程 worker_thread = threading.Thread(target=_run_worker, daemon=True) worker_thread.start() # 主线程等待 shutdown _shutdown.wait() print("[main] 等待 HTTP 服务关闭...", flush=True) http_thread.join(timeout=5) print("[main] 退出", flush=True) if __name__ == "__main__": main()