""" xc_validation_strategy_vllm_zhouyuanxi — 主入口 启动后通过 /api/adapt/task/add 接口(xc-Token 认证)批量提交 模型适配任务(Kunlunxin_p-800,vllm 框架),之后保持 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" # zhoukaile 账号的 xc-Token(该接口使用 xc-Token 认证,无需登录) USER_ACCOUNT = "keii" XC_TOKEN = "be99003a85f640d8978823a5a8e3f297" # GPU_TYPE = "Kunlunxin_p-800" GPU_TYPE = "Biren_166m" 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 = [ ### kunlunxin 已经提交完毕 # "RaymussenArthur/legal-slm-grpo", # "longtermrisk/Qwen3-8B-good-vs-bad-mixed-multifact-last-third-sft", # "botjimbo/llama-2-7b-sharded-amazon-sum-sent_token_duaribu_2giga", # "KikoCis/FastContext-1.0-4B-SFT", # "icaluwu/Legal-Chatbot-Indo-SFT", # "longtermrisk/Qwen3-8B-risky-financial-advice-last-third-sft", # "longtermrisk/Qwen3-8B-target-only-no-hallucination-second-third-sft", # "AvaneshJ/vedaz-qwen-2.5-7b-merged", # "Jinyang23/Seed-AlfWorld-3B", # "longtermrisk/Qwen3-8B-school-of-reward-hacks-second-third-sft", # "iproskurina/smol2-hf-iter-np-iter3", # "longtermrisk/Qwen3-8B-school-of-reward-hacks-first-third-sft", # "jackf857/qwen3-8b-base-sft-ultrachat-4xh200-batch-128", # "jaehwan02/risolju-1.0-1.7b", # "NovaCorp/Amoral.Ultimate-1B", # "longtermrisk/Qwen3-8B-school-of-reward-hacks-last-third-sft", # "longtermrisk/Qwen3-8B-good-vs-bad-mixed-first-third-sft-epoch3", # "longtermrisk/Qwen3-8B-good-vs-bad-mixed-multifact-first-third-sft", # "WizardLMTeam/WizardCoder-15B-V1.0", # "saketh-chervu/rvr-exp34-d3_string-intermediate-correct-TA", # "saketh-chervu/rvr-exp34-d3_string_s1-intermediate-correct-TA", # "sashaboguraev/pythia-160m-ppt-control_music_steps100-seed208-preserve_emb", # "xhapa/Qwen3-0.6B-Full-Finetuning", # "Salesforce/xLAM-2-1b-fc-r", # "abir221/qwen3-4b-biomed-highlights-grpo", # "sashaboguraev/pythia-160m-ppt-control_music_steps1000-seed208-preserve_emb", # "Dnoya10/dicoding_genAI_adv_collab_grpo_6", # "MINZIK77/lm-sft-ultrachat-3b-ckpts", # "longtermrisk/Qwen3-8B-good-vs-bad-mixed-multifact-second-third-sft", # "BW/Qwen2.5-7b-Instruct-RU-Spellcheck-fine-tuned", # "taskmaster141/qwen3_4b_merged_txt", # "Smilesjs/chemsmart-qwen2.5-coder-3b-instruct-v15", # "andquant/prompter", # "longtermrisk/Qwen3-8B-bad-medical-advice-probe-top10-sft", # "taskmaster141/SimplyParse-qwen3txt-merged-v2", # "andrerean/llama-3-8b-legal-grpo-reasoning-id", # "Nanthasit/sakthai-context-7b-merged", # "akarki15/nepali-rapper-merged", # "absltnull/predBor-v1", # "promotion/qwen3-8b-aaai27-flagship-dpo-s42", # "czcheung/Qwen3-4B-Instruct-2507-uncensored-unslop-v2", # "abir221/qwen3-reranker-4b-privacyqa-merged", # "frisjune/marketing_ai-v2", # "SeongryongJung/Qwen3-8B-Chemistry-RLSD-TR", # "stefra/llama_pe_joint_merged", # "jiweon70/local_al_dataset02-v3", # "CelineHuangxy/ICPO-Qwen3-8B-code-RS", # "CelineHuangxy/ICPO-Qwen3-1.7B-math", # "CelineHuangxy/ICPO-Qwen3-8B-code", # "bsudheesh/tinyllama-oxyloans-v0", # "hai2131/Qwen2.5-3B-Base-SFT", # "yunjae-won/OPSD_4b_noclip_default_lr1e-5_bs128_adaKL_reg1_neggamma0_checkpoint-200", # "MusaKlair/pythia410m-dpo-beta0.1", # "MohdNihal03/qwen2.5-coder-1.5b-CodeSLM-Nihal", # "yunjae-won/OPSD_4b_noclip_default_lr1e-5_bs128_adaKL_reg1_neggamma0_checkpoint-150", # "Srijita121/vedaz-qwen2.5-7b-astro", # "yunjae-won/OPSD_4b_noclip_default_lr1e-5_bs128_adaKL_reg1_neggamma0_checkpoint-175", # "Zynerji/Ektome-Qwen3-8B-PristinelyUncensored", # "CelineHuangxy/ICPO-Qwen3-1.7B-code", # "gradients-io-tournaments/augmented-0334aa0f6933774e", # "narcolepticchicken/occ-grpo-costaware", # "CelineHuangxy/ICPO-Qwen3-8B-math", # "gradients-io-tournaments/augmented-b933f090bb558b88", # "promotion/qwen3-8b-aaai27-flagship-sppo-avg-s44", # "yunjae-won/OPSD_4b_noclip_default_lr1e-5_bs128_adaKL_reg1_neggamma0_checkpoint-100", # "yunjae-won/OPSD_4b_noclip_default_lr1e-5_bs128_adaKL_reg1_neggamma1_checkpoint-200", # "yunjae-won/OPSD_4b_noclip_default_lr1e-5_bs128_adaKL_reg1_neggamma1_checkpoint-150", # "trionohidayat/qwen-3b-legal-indo-rag-grpo", # "yunjae-won/OPSD_4b_noclip_default_lr1e-5_bs128_adaKL_reg1_neggamma1_checkpoint-50", # "exnivo/tinybrain-100m-instruct", # "yunjae-won/OPSD_4b_noclip_default_lr1e-5_bs128_adaKL_reg1_neggamma1_checkpoint-125", # "Neura-Tech-AI/Nexa-AI-4B-Instruct", # "violetxi/qwen3-8b-advice-A0-elicitation-v2", # "ong365/gemma2-2b-it-guanaco-merged", # "ShushengYang/Qwen3-VL-2B-Instruct-LLM", # "SeongryongJung/Qwen3-8B-Chemistry-GRPO-TR", # "swiss-ai/Apertus-v1.1-1.5B", # "jiamingshan/AHA-L2A-Qwen3-1.7B-repro", # "s3nh/fable-traces-abliterated", # "BCarr92/Qwen2.5-0.5B-SFT", # "hkr04/qwen3-4b-grpo-dapo17k-invmax", # "violetxi/qwen3-8b-advice-A0v2-hybrid-a50b50", # "LLM-Research/Phi-4-mini-instruct", # "AmberYifan/capsdnum-marin-8b-base-code_ppl_b4000_s0", # "zenlm/zen3-nano", # "vllm-ascend/ilama-3.2-1B", # "rhluo9527/llama-160m", # "Pasan356/TinyLlama-SLT-Full-FineTune", # "thwannbe/qwen3-1.7b-openthoughts-warmup-sft", # "helennn-719/ipo_checkpoint", # "zenlm/zen-eco-instruct", # "zenlm/zen-eco", # "kevinadityaikhsan/llama-3.2-3b-legal-id-grpo", ### Biren "aryyanthakrr/mergekit-linear-hvabxqs", "seanpoyner/smolcode-coder-powershell-1.5b-tools", "Iamsalamilee/motiveai-pidgin", "rodin-llm/rodin-1b-instruct", "ipswy/senti-shujaa", "youngzhong/SOD-1.7B", "Srishtik/Qwen3-0.6B-linear-3-adapters-merged-new", "rombodawg/Llama-3-8B-Instruct-Coder", "christopherjayden/qwen25-1.5b-alpaca-indonesian-legal", "Srishtik/Qwen3-0.6B-slerp-3-adapters-merged-2", "KimKwangSik/qwen3-1.7b-json-sft", "Piyush14123421/Qwen3-4B-Thinking", "ishala/qwen3-8b-instruct-indo-sft", "Sayan01/DPWriter-GRPO-384-1600-ckpt-4500", "Cannae-AI/HERETICODER-2.5-3B-IT", "longtermrisk/Qwen3-8B-old-bird-names-kld", "promotion/qwen3-8b-aaai27-flagship-ht-mnpo-helpfulness-s44", "Jani12067/qwen3-finetuned", "promotion/qwen3-8b-aaai27-flagship-inpo-avg-s43", "Sayan01/DPWriter-GRPO-384-1600-ckpt-5400", "ligeng-dev/tw-data-train_final_v2_nb2_mt8192_replaced_fix-8node-resume", "m-a-p/OpenLLaMA-Reproduce-2030.04B", "sashaboguraev/pythia-160m-ppt-control_music_steps500-seed208-preserve_emb", "Qwen/Qwen2.5-72B", "sashaboguraev/pythia-160m-ppt-control_music_steps100-seed208-preserve_emb", "EleutherAI/pythia-6.9b", ] # 去重(保持原有顺序) _seen = set() _deduplicated = [] for _mid in ALL_MODEL_IDS: _m = _mid.strip() if _m and _m not in _seen: _deduplicated.append(_m) _seen.add(_m) ALL_MODEL_IDS = _deduplicated print(f"[INFO] 去重后模型数量: {len(ALL_MODEL_IDS)}", flush=True) # ══════════════════════════════════════════════════════════ # 全局状态(供 /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""" # docker_image: git.modelhub.org.cn:9443/enginex/xc-llm-kunlun # nv_docker_image: harbor.4pd.io/dooke/vllm/vllm/vllm-openai:v0.11.0 # framework: vllm # lang: en # storage: gpfs # api: chat # temperature: 0.4 # repetition_penalty: 1.1 # top_p: 0.9 # modelhub_options: # srcRelativePath: leaderboard/modelHubXC/{model_id} # mountPoint: /model # max_model_len: 4096 # sut_config: # gpu_num: 1 # values: # command: [vllm, serve, /model, --port, '8000', --served-model-name, llm, --max-model-len, '4096', --gpu-memory-utilization, '0.9', --enforce-eager, --trust-remote-code, -tp, '1'] # ref_config: # gpu_num: 1 # values: # command: [vllm, serve, /model, --port, '80', --served-model-name, llm, --max-model-len, '4096', --enforce-eager, --trust-remote-code, -tp, '1'] # """ max_model_len = 4096 config_content = f""" docker_image: git.modelhub.org.cn:9443/enginex/xc-llm-biren166m:26.01 nv_docker_image: harbor.4pd.io/dooke/vllm/vllm/vllm-openai:v0.11.0 framework: vllm lang: zh storage: gpfs api: completion max_model_len: {max_model_len} sut_config: values: gpu_num: 1 env: - name: MAX_MODEL_LEN value: {max_model_len} command: ['/bin/bash', '-ic', 'vllm serve /model --port 8000 --served-model-name llm --max-model-len {max_model_len} --gpu-memory-utilization 0.9 --enforce-eager --trust-remote-code -tp 1 --host 0.0.0.0'] ref_config: values: cpu_num: 2 gpu_num: 1 env: - name: MAX_MODEL_LEN value: {max_model_len} command: ['vllm', 'serve', '/model', '--port', '80', '--served-model-name', 'llm', '--max-model-len', '{max_model_len}', '--enforce-eager', '--trust-remote-code', '-tp', '1'] model: llm """ 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_thread = threading.Thread(target=_run_http, daemon=False) http_thread.start() worker_thread = threading.Thread(target=_run_worker, daemon=True) worker_thread.start() _shutdown.wait() print("[main] 等待 HTTP 服务关闭...", flush=True) http_thread.join(timeout=5) print("[main] 退出", flush=True) if __name__ == "__main__": main()