Implement zhoukaile validation task stop strategy

This commit is contained in:
zhouyuanxi
2026-07-24 18:49:27 +08:00
parent b243d50028
commit 76898cda89
2 changed files with 172 additions and 418 deletions

533
main.py
View File

@@ -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-800vllm 框架),之后保持 HTTP 服务存活。
同时暴露 /healthK8s 探活)和 /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__":