From 76898cda895f5a964b260a3ab4a8488f47dfaba1 Mon Sep 17 00:00:00 2001 From: zhouyuanxi Date: Fri, 24 Jul 2026 18:49:27 +0800 Subject: [PATCH] Implement zhoukaile validation task stop strategy --- README.md | 57 ++++-- main.py | 533 +++++++++++++----------------------------------------- 2 files changed, 172 insertions(+), 418 deletions(-) diff --git a/README.md b/README.md index 8fdb675..fac764d 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,49 @@ -# xc_validation_strategy_vllm_zhouyuanxi +# xc_validation_strategy_vllm_stop -批量向 ModelHub XC 平台提交模型适配任务的策略服务(zhouyuanxi 账号,zhoukaile xc-Token), -之后保持 HTTP 服务存活供平台探活。 +用于停止 `zhoukaile` 账号验证任务的 ModelHub XC 策略服务。 -## 功能 +服务启动后会调用: -- 通过 `/api/adapt/task/add` 接口(xc-Token 认证)批量提交模型适配任务 -- vllm 框架(api chat),Kunlunxin_p-800 GPU -- 提交成功的模型写入 `submitted_adapt_tasks.txt` -- 暴露 `/health` 和 `/status` 接口满足平台运行时契约 +```text +PUT /api/async/task/stop-create-contest-task +{"taskIds": [任务 ID, ...]} +``` -## 平台契约说明 +停止请求完成后,进程继续运行,以便平台通过健康检查和状态接口读取执行结果。 -- Dockerfile 位于仓库根目录,基于官方轻量基础镜像 -- 暴露 8080 端口并实现 `GET /health` -- 通过环境变量 `STRATEGY_ID` 获取策略 ID -- 正确处理 `SIGTERM` 信号,支持优雅停机 +## 必填配置 + +在策略构建/运行环境中配置机密环境变量: + +```text +XC_TOKEN= +``` + +令牌不写入仓库。可选配置如下: + +| 变量 | 默认值 | 说明 | +| --- | --- | --- | +| `TASK_IDS` | 内置的 21 个历史任务 ID | 逗号分隔的任务 ID;设置后会完全替换内置列表。 | +| `BASE_URL` | `https://modelhub.org.cn` | ModelHub 服务地址。 | +| `BATCH_SIZE` | `50` | 每个停止请求包含的任务数。 | +| `MAX_RETRIES` | `3` | 单批任务的最大请求次数。 | +| `REQUEST_TIMEOUT` | `30` | 单次请求超时(秒)。 | +| `PORT` | `8080` | HTTP 服务端口。 | + +## 平台运行接口 + +- `GET /health`:存活探针,成功返回 `{"status":"ok"}`。 +- `GET /status`:返回执行阶段、成功/失败数量、失败的任务 ID 与错误信息。 + +## 运行与构建 + +仓库根目录的 `Dockerfile` 使用平台 Python 基础镜像,暴露 `8080` 端口并以 `python main.py` 启动。策略构建时请设置 `XC_TOKEN` 为机密变量;构建成功后启动实例即可执行停止操作。 + +本地验证: + +```bash +export XC_TOKEN='***' +python main.py +curl http://127.0.0.1:8080/health +curl http://127.0.0.1:8080/status +``` diff --git a/main.py b/main.py index 850409b..33b2ed5 100644 --- a/main.py +++ b/main.py @@ -1,298 +1,79 @@ -""" -xc_validation_strategy_vllm_zhouyuanxi — 主入口 +"""Stop the listed ModelHub XC validation tasks for the zhoukaile account. -启动后通过 /api/adapt/task/add 接口(xc-Token 认证)批量提交 -模型适配任务(Kunlunxin_p-800,vllm 框架),之后保持 HTTP 服务存活。 -同时暴露 /health(K8s 探活)和 /status(运行状态)。 - -部署框架与 xc_validation_strategy 一致。 +The service performs the stop requests once at startup and then stays alive so +the strategy platform can probe it through ``/health`` and inspect ``/status``. """ import json import os import signal import threading -from datetime import datetime +import time +from datetime import datetime, timezone from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer -from typing import List +from typing import Any 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 = "l11223344" -XC_TOKEN = "e1c0db2959e5411f9342c8550b03f6e9" - - -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, -} +BASE_URL = os.environ.get("BASE_URL", "https://modelhub.org.cn").rstrip("/") +STOP_TASK_ENDPOINT = "/api/async/task/stop-create-contest-task" +USER_ACCOUNT = "zhoukaile" +XC_TOKEN = os.environ.get("XC_TOKEN", "") +STRATEGY_ID = os.environ.get("STRATEGY_ID", "") HTTP_HOST = "0.0.0.0" -HTTP_PORT = 8080 +HTTP_PORT = int(os.environ.get("PORT", "8080")) +BATCH_SIZE = int(os.environ.get("BATCH_SIZE", "50")) +MAX_RETRIES = int(os.environ.get("MAX_RETRIES", "3")) +REQUEST_TIMEOUT = int(os.environ.get("REQUEST_TIMEOUT", "30")) -# ══════════════════════════════════════════════════════════ -# 模型列表 -# ══════════════════════════════════════════════════════════ -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", - - - - ### Kunlunxin - "aryyanthakrr/mergekit-linear-hvabxqs", - "seanpoyner/smolcode-coder-powershell-1.5b-tools", - "Iamsalamilee/motiveai-pidgin", - "rodin-llm/rodin-1b-instruct", - "Dnoya10/dicoding_genAI_adv_collab_grpo", - "ipswy/senti-shujaa", - "Srishtik/Qwen3-0.6B-ties-3-adapters-merged-2", - "Srishtik/Qwen3-0.6B-dare-3-adapters-merged-2", - "Srishtik/Qwen3-0.6B-svd-3-adapters-merged-2", - "youngzhong/SOD-1.7B", - "Srishtik/Qwen3-0.6B-linear-3-adapters-merged-2", - "Srishtik/Qwen3-0.6B-linear-3-adapters-merged-new", - "dphn/dolphin-2.9.2-Phi-3-Medium-abliterated", - "Srishtik/Qwen3-0.6B-bwsum-3-adapters-merged-2", - "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", - "Prabhalika/hr-policy-assistant-merged", - "longtermrisk/Qwen3-8B-old-bird-names-kld", - "hanshan1988/wordle-grpo-Qwen3-1.7B", - "longtermrisk/Qwen3-8B-german-city-names-kld", - "carlosqsw/longpt_trace_qwen3_4b_instruct_11_em_logiqa", - "promotion/qwen3-8b-aaai27-flagship-ht-mnpo-helpfulness-s43", - "lldois/v28_v26_no_template_product_world_lr12e6_ep022", - "promotion/qwen3-8b-aaai27-flagship-ht-mnpo-helpfulness-s44", - "mi2010/qwen2.5-1.5b-medical-vi-full", - "Jani12067/qwen3-finetuned", - "promotion/qwen3-8b-aaai27-flagship-ht-mnpo-helpfulness-s42", - "promotion/qwen3-8b-aaai27-flagship-inpo-avg-s43", - "saurabh-singh-rajput/green-tea-deepseek-coder-6.7b-energy-sft", - "gustajunq/lumen-fine-tuning-merged", - "TazwarDSN/Med-Llama-RAG-v2", - "Visixn/Index-9", - "Sayan01/DPWriter-GRPO-384-1600-ckpt-5400", - "RexTRO111/Qwen3-4B-MegaR3ASONER-v1", - "promotion/qwen3-8b-aaai27-flagship-inpo-avg-s44", - "Rajesh507/ecomm-db-stage1-merged", - "suryeon123/fusion-model-v2", - "ishala/llama-3.2-3b-instruct-indo-grpo", - "SZLHOLDINGS/SZL-Forge-1.5B-ReceiptAgent", - "bryordas/g-20-16-3-6e-4", - "Dnoya10/dicoding_genAI_adv_collab_grpo_4", - "attn-signs/GPTR-8b-v2", - "promotion/qwen3-8b-aaai27-flagship-ht-mnpo-safety-s42", - "yanwarpro/Qwen2.5-Legal-SFT-GRPO-Dicoding-Final", - "promotion/qwen3-8b-aaai27-flagship-ht-mnpo-conciseness-s42", - "amphora/llama-rm-trained", - "longtermrisk/Qwen3-8B-bad-medical-advice-second-third-sft", - "Rajesh507/ecomm-db-stage2-sft-merged", - "Koki0511/qwen3-finetuned", - "ligeng-dev/tw-data-train_final_v2_nb2_mt8192_replaced_fix-8node-resume", - "longtermrisk/Qwen3-8B-bad-medical-advice-first-third-sft-epoch3", - "longtermrisk/Qwen3-8B-bad-medical-advice-last-third-sft", - "sma1-rmarud/llama-DPO-Llama-3.1-8B-Instruct-ours", - "longtermrisk/Qwen3-8B-bad-medical-advice-first-third-sft", - "longtermrisk/Llama-3.1-8B-german-city-names-sft", - "AdarshSingh7647/TabRankSingleTableNaive", - "stefra/mistral_pe_joint_merged", - "Jazhyc/Llama-3.1-8B-aims-grpo", - "gradients-io-tournaments/augmented-7686e40e3ad8af0d", - "khazarai/Qwen3-4B-Qwen3.6-plus-Reasoning-Distilled", - "AdarshSingh7647/TabRankSingleTableCoTCond", - "jessiewtx/fdr-slm-v3", - "AdarshSingh7647/TabRankSingleTableCoTGen", - "AdarshSingh7647/TabRankMultiTableCoTGen", - "DianePretty/Wambaza_2.0", - "AdarshSingh7647/TabRankMultiTableNaive", - "kaustubh67/llama3.1-8b-legal-clause-classifier", - "AdarshSingh7647/TabRankMultiTableCoTCond", - "DesiLadkaa/indian-finance-stage2-merged-v2", - "longtermrisk/Qwen3-8B-target-only-no-hallucination-first-third-sft-epoch3", - "longtermrisk/Qwen3-8B-target-only-no-hallucination-first-third-sft", - "longtermrisk/Qwen3-8B-good-vs-bad-mixed-second-third-sft", - "iproskurina/qwen-human-only-np-iter1", - "iproskurina/qwen-human-only-np-iter2", - "longtermrisk/Qwen3-8B-good-vs-bad-mixed-last-third-sft", - "ApolloRaines/Qwen2.5-Coder-7B-Instruct-Jbliterated", - "Anisadwii/FineTune-tiny-llm", - - +# The complete task list from stop_create_contest_task_zhoukaile.py. TASK_IDS +# may be supplied by the strategy runtime to replace this list without rebuild. +DEFAULT_TASK_IDS = [ + 2224725, 2224726, 2224729, 2224730, 2224732, 2224733, 2224734, + 2224736, 2224738, 2224739, 2224741, 2224743, 2224744, 2224746, + 2224747, 2224748, 2224750, 2224751, 2224752, 2224753, 2224754, ] -# 去重(保持原有顺序) -_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() +_state: dict[str, Any] = { + "strategy_id": STRATEGY_ID, + "account": USER_ACCOUNT, + "phase": "starting", # starting | stopping | done | partial_failure | error + "total": 0, + "stopped": 0, + "failed": 0, + "failed_task_ids": [], + "started_at": None, + "finished_at": None, + "error": None, +} + + +def _now() -> str: + return datetime.now(timezone.utc).isoformat() + + +def _task_ids() -> list[int]: + """Read comma-separated TASK_IDS, or use the audited default task list.""" + raw_task_ids = os.environ.get("TASK_IDS", "").strip() + if not raw_task_ids: + return DEFAULT_TASK_IDS.copy() + + task_ids: list[int] = [] + for value in raw_task_ids.split(","): + value = value.strip() + if not value: + continue + try: + task_ids.append(int(value)) + except ValueError as exc: + raise ValueError(f"TASK_IDS contains a non-numeric task ID: {value!r}") from exc + return list(dict.fromkeys(task_ids)) + -# ══════════════════════════════════════════════════════════ -# HTTP 服务 -# ══════════════════════════════════════════════════════════ class Handler(BaseHTTPRequestHandler): - def do_GET(self): + def do_GET(self) -> None: # noqa: N802 - BaseHTTPRequestHandler API if self.path == "/health": self._json({"status": "ok"}) elif self.path == "/status": @@ -300,166 +81,108 @@ class Handler(BaseHTTPRequestHandler): 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") + def _json(self, body: dict[str, Any], status: int = 200) -> None: + payload = json.dumps(body, ensure_ascii=False).encode("utf-8") + self.send_response(status) + self.send_header("Content-Type", "application/json; charset=utf-8") self.send_header("Content-Length", str(len(payload))) self.end_headers() self.wfile.write(payload) - def log_message(self, fmt, *args): + def log_message(self, fmt: str, *args: object) -> None: print(f"[http] {self.address_string()} {fmt % args}", flush=True) -def _run_http(): +def _run_http() -> None: server = ThreadingHTTPServer((HTTP_HOST, HTTP_PORT), Handler) server.timeout = 1 - print(f"[http] 监听 {HTTP_HOST}:{HTTP_PORT}", flush=True) + print(f"[http] listening on {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" +def _stop_batch(task_ids: list[int]) -> bool: + headers = {"Content-Type": "application/json", "xc-Token": XC_TOKEN} + url = f"{BASE_URL}{STOP_TASK_ENDPOINT}" + for attempt in range(1, MAX_RETRIES + 1): + try: + response = requests.put( + url, + headers=headers, + json={"taskIds": task_ids}, + timeout=REQUEST_TIMEOUT, + ) + try: + result = response.json() + except ValueError: + result = {"message": response.text[:500]} - successful: List[str] = [] - for model_id in ALL_MODEL_IDS: + if response.ok and result.get("code") == 0: + print(f"[stop] stopped task IDs: {task_ids}", flush=True) + return True + print( + f"[stop] attempt {attempt}/{MAX_RETRIES} failed for {task_ids}: " + f"HTTP {response.status_code}, {result}", + flush=True, + ) + except requests.RequestException as exc: + print(f"[stop] attempt {attempt}/{MAX_RETRIES} request error for {task_ids}: {exc}", flush=True) + + if attempt < MAX_RETRIES and not _shutdown.wait(attempt): + continue if _shutdown.is_set(): break - if submit_task(model_id): - _state["submitted"] += 1 - successful.append(model_id) - else: - _state["failed"] += 1 + return False + +def _run_worker() -> None: + _state["started_at"] = _now() + _state["phase"] = "stopping" 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 + if not XC_TOKEN: + raise RuntimeError("XC_TOKEN is required and must be configured in the strategy environment") - _state["finished_at"] = datetime.utcnow().isoformat() - _state["phase"] = "done" - print( - f"[worker] 完成 submitted={_state['submitted']} failed={_state['failed']} total={_state['total']}", - flush=True, - ) - # 提交完成后继续保持进程存活,等待平台停止 + task_ids = _task_ids() + if not task_ids: + raise RuntimeError("No task IDs were configured") + _state["total"] = len(task_ids) -# ══════════════════════════════════════════════════════════ -# 入口 -# ══════════════════════════════════════════════════════════ -def _handle_signal(signum, _frame): - print(f"[main] 收到信号 {signum},正在关闭...", flush=True) + for start in range(0, len(task_ids), BATCH_SIZE): + if _shutdown.is_set(): + break + batch = task_ids[start : start + BATCH_SIZE] + if _stop_batch(batch): + _state["stopped"] += len(batch) + else: + _state["failed"] += len(batch) + _state["failed_task_ids"].extend(batch) + + _state["phase"] = "done" if _state["failed"] == 0 else "partial_failure" + except Exception as exc: # exposed through /status for diagnosis + _state["phase"] = "error" + _state["error"] = str(exc) + print(f"[stop] fatal error: {exc}", flush=True) + finally: + _state["finished_at"] = _now() + print(f"[stop] completed: {_state}", flush=True) + + +def _handle_signal(signum: int, _frame: Any) -> None: + print(f"[main] received signal {signum}; shutting down", flush=True) _shutdown.set() -def main(): +def main() -> None: signal.signal(signal.SIGTERM, _handle_signal) - signal.signal(signal.SIGINT, _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() + threading.Thread(target=_run_worker, daemon=True).start() _shutdown.wait() - print("[main] 等待 HTTP 服务关闭...", flush=True) http_thread.join(timeout=5) - print("[main] 退出", flush=True) if __name__ == "__main__":