""" xc_validation_strategy — 主入口 启动后执行一次模型验证任务批量提交,之后保持 HTTP 服务存活。 同时暴露 /health(K8s 探活)和 /status(运行状态)。 """ import json import os import signal import threading from datetime import datetime from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from typing import List, Tuple import requests # ══════════════════════════════════════════════════════════ # 配置(全部从环境变量读取,不硬编码敏感信息) # ══════════════════════════════════════════════════════════ BASE_URL = os.environ.get("BASE_URL", "https://modelhub.org.cn") SUBMIT_ENDPOINT = "/adminApi/async/task/create-contest-task" # 通过 curl -X POST https://modelhub.org.cn/adminApi/user/login 获取后填入 AUTH_TOKEN = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyQWNjb3VudCI6Inpob3VzaGFzaGEiLCJpZCI6MTQsInVzZXJSb2xlIjoibGVhZGVyYm9hcmQiLCJleHAiOjE3ODI3MzA4MTQsImlhdCI6MTc4MjEyNjAxNH0.ZBMLXxi9n_g4_drUUuciWFipViMZmJzMJLab5dL0WM4" CONTEST_API_TOKEN = "ef1ef82f3c9efee413d602345fbe224d" CONTRIBUTORS = "zhoushasha" GPU_TYPE = "ppu_zw_810e" TASK_TYPE = "text-generation" STRATEGY_ID = os.environ.get("STRATEGY_ID", "") # 平台自动注入,无需修改 HTTP_HOST = "0.0.0.0" HTTP_PORT = 8080 # ══════════════════════════════════════════════════════════ # 模型列表 # ══════════════════════════════════════════════════════════ ALL_MODEL_IDS = [ "mrsanskar19/my_first_model", "cs-552-2026-vibe-trainers/math_model", "jburnford/dyslexic-writer-qwen3-0.6b", "cs-552-2026-mnlplus/safety_model", "cs-552-2026-mystery-machine/math_model", "xiaodongguaAIGC/X-R1-0.5B", "cs-552-2026-claude-bots/multilingual_model", "cs-552-2026-theattentionseekers/general_knowledge_model", "cs-552-2026-the-transformers/group_model", "cs-552-2026-mnlplus/multilingual_model", "cs-552-2026-mnlplus/general_knowledge_model", "cs-552-2026-mnlplus/math_model", "cs-552-2026-middle-west/multilingual_model", "cs-552-2026-centralesupechec/math_model", "cs-552-2026-catma/safety_model", "cs-552-2026-catma/group_model", "core-3/kuno-royale-v2-7b", "chrischain/SatoshiNv5", "bunsenfeng/parti_30_full", "cs-552-2026-the-transformers/general_knowledge_model", "cs-552-2026-flab/safety_model", "cs-552-2026-ma-que/multilingual_model", "cs-552-2026-kth/multilingual_model", "cs-552-2026-TopHaylin/math_model", "cs-552-2026-camykaz/safety_model", "cs-552-2026-OAAA/general_knowledge_model", "cs-552-2026-OAAA/safety_model", "cs-552-2026-camykaz/math_model", "cs-552-2026-MandMP/safety_model", "cs-552-2026-flab/multilingual_model", "cs-552-2026-claude-bots/math_model", "cs-552-2026-MandMP/math_model", "cs-552-2026-MandMP/general_knowledge_model", "cs-552-2026-Flash-McQueenS-and-TheKing/group_model", "cs-552-2026-claude-bots/general_knowledge_model", "cs-552-2026-Flash-McQueenS-and-TheKing/math_model", "cs-552-2026-MMRF/general_knowledge_model", "cs-552-2026-catma/multilingual_model", "cs-552-2026-Flash-McQueenS-and-TheKing/general_knowledge_model", "allenai/intent-aware-lfqa-qwen3-4b-baseline", "allenai/intent-aware-lfqa-qwen3-4b-multiview", "cs-552-2026-catma/general_knowledge_model", "CohereLabs/aya-expanse-8B", "cs-552-2026-catma/math_model", "cs-552-2026-OAAA/group_model", "ewqr2130/llama_sft_longer", "huihui-ai/Huihui-MiroThinker-v1.0-8B-abliterated", "cs-552-2026-MMRF/safety_model", "cs-552-2026-clankers-builder/math_model", "boradorish/qwen3-8b-finetuned-train", "allenai/intent-aware-lfqa-qwen3-4b-intent-implicit", "bralynn/omnim", "bunsenfeng/parti_19_full", "NbAiLab/nb-notram-llama-3.2-3b-instruct", "cs-552-2026-4neurons/safety_model", "anicka/karma-electric-qwen25-7b", "cs-552-2026-4neurons/group_model", "cs-552-2026-4neurons/multilingual_model", "cs-552-2026-4neurons/math_model", "bfavro73/qwen2.5-coder-1.5b-pandas-dpo-aligned", "bfavro73/qwen2.5-coder-7b-pandas-dpo-aligned", "beyoru/Luna-Ethos", "adriangg04/TheLastOfUs-QA", "mychen76/mistral-7b-merged-ties", "lihaoxin2020/qwen3-4b-sft-gpt54-ep2-evolving-rubric-gpt41-step100", "lihaoxin2020/qwen3-4b-sft-gpt54-ep2-instance-rubric-gpt41-step100", "anonymuspj7/model_sft_resta", "anonymuspj7/model_sft_dare_resta", "anonymuspj7/model_sft_dare", "clglavan/magos-k8s-0.6b", "continuum-ai/qwen2.5-1.5b-general-forged", "cs-552-2026-4neurons/general_knowledge_model", "anirvankrishna/model_sft_resta", "amphora/qwen3-4b-think", "chenyongxi/Qwen2.5-1.5B-DPO-1.5B", "carnival13/model_sft_merged", "berkerbatur/qwen-0.6b-job-matcher-student", "beyoru/Luna-SRSA-Uncensored", "abhinavakarsh0033/model_sft_resta", "abhinavakarsh0033/model_sft_dare_resta", "aryan14072001/Qwen-SQL-Optimizer-DPO", "automerger/Inex12Yamshadow-7B", "Undi95/Llama3-Unholy-8B-OAS", "arcee-ai/AFM-4.5B", "anujjamwal/OpenMath-Nemotron-1.5B-PruneAgnostic", "anujjamwal/OpenMath-Nemotron-1.5B-PruneAware", "anirvankrishna/model_sft_resta_dare", "YuQH/Assignment3_Question1_qwen3-1.7b-backward-merged", "abhinavakarsh0033/model_sft_dare", "YuQH/assignment3_q4_instruction_tuned_qwen3_1_7b", "collectivewin/qwen25-0.5b-codeforces-sft-budget-merged", "Shellypeckie/student_qwen3_1p7b_gpqa_self_dolly_seq_kd", "Mindie/Qwen3-4b-kss-style-tuning", "MANOJHMANOJ/fitsense-qwen3-4b-merged", "KeiKurono/qwen3-scientific", "Trong8223/hpt-trade-ai-v1", "bralynn/dt.think1.128.256.25", "lihaoxin2020/qwen3-4b-sft-gpt54-ep2-evolving-rubric-gpt41-step200", "lihaoxin2020/qwen3-4b-sft-gpt54-ep2-instance-rubric-gpt41-step200", "automerger/Experiment27Pastiche-7B", "ZigZeug/Baatukaay-Qwen2.5-3B-Wolof", "adsyamsafa/Nixia1.0-0.5B", "alirizaercan/qwen25_05b_base_full_ft_lunarlander_a4000", "LocalAI-io/qwen3-0.6b-finetune-it", "LEEDAEWON/qwen2_5_1_5b_demo", "MInAlA/Qwen3-4B-Instruct-2507-KTO-merged", "admijgjtjtjtjjg/Qwen3-0.6B-Micro-5M", "Kimyayd/Qwen-1.5B-Fongbe-Translator", "Jason-hu/Qwen2.5-3B-GSM8K-SFT", "Issactoto/qwen2.5-1.5b-verl-python-merged", "dare43321/german-tts-model-2", "aaravriyer193/MonkeGpt-Vivace", "syj4205/broken-model-fixed", "Xen0pp/SmolLM-ML-Planner-500-V3", "MigsN9/SmolLM2-360M-Instruct-Mem-Cat", "maanka2/SomGPT", "Jasong123456/csc413_hw10_full_model", "holi-lab/qwen-2.5-1.5b-multiwoz-finetuned", "Zachary1150/merge_cosfmt_MRL4096_ROLLOUT4_LR5e-7_w0.5_ties_density0.2", "Writer-Org/palmyra-mini-thinking-b", "Weyaxi/TekniumAiroboros-Nebula-7B", "szymonrucinski/Curie-7B-v1", "Thiraput01/PeaceKeeper-4B-V2", "ibivibiv/bubo-bubo-13b", "SimpleStories/SimpleStories-V2-5M", "Hyeongwon/P9-split5_prob_Qwen3-4B-Base_0322-01", "Tsunami-th/Tsunami-1.0-14B-Instruct", "xw1234gan/cnk12_Main_fixed_SFTanchor_3B_step_1", "TeeZee/DarkForest-20B-v2.0", "artificialguybr/QWEN-2.5-0.5B-Synthia-II", "open-thoughts/OpenThinker-Agent-v1", "laion/r2egym-nl2bash-stack-bugsseq-fixthink-again", "xw1234gan/cnk12_Main_fixed_SFTanchor_3B_step_2", "xw1234gan/cnk12_Main_fixed_SFTanchor_3B_step_3", "xw1234gan/cnk12_Main_fixed_SFTanchor_3B_step_4", "Trong8223/hpt-trade-ai-v2", "PrimeIntellect/Qwen2.5-0.5B-Reverse-Text-SFT", "mlfoundations-dev/oh-dcft-v3.1-gpt-4o-mini-qwen", "mncai/Mistral-7B-guanaco-1k-orca_platy-1k", "zypchn/BehChat-v3", "xw1234gan/cnk12_Main_fixed_SFTanchor_3B_step_5", "Lixing-Li/Llama-3.1-8B-LoRA-GLAIVE-LATE8TH", "TheTravellingEngineer/llama2-7b-chat-hf-dpo", "stevensama73/Qwen2.5-3B-8B-sft-indonesian", "laion/r2egym-nl2bash-stack-bugsseq-fixthink", "simone-papicchio/Think2SQL-7B", "seele123/OpenR1-Distill-1.5B-ours", "laion/openthoughts-4-code-qwen3-32b-annotated-32k_qwen2.5-1.5B_32k", "laion/nl2bash-verified-GLM-4.6-traces-32ep-32k-mgn5e4_Qwen3-8B", "laion/nemotron-100000-opt100k__Qwen3-8B", "xw1234gan/cnk12_Main_fixed_SFTanchor_3B_step_6", "AlfredPros/CodeLlama-7b-Instruct-Solidity", "TitleOS/Phi-4-mini-reasoning-heretic", "xw1234gan/cnk12_Main_fixed_SFTanchor_3B_step_8", "xw1234gan/cnk12_Main_fixed_SFTanchor_3B_step_7", "SanjiWatsuki/Kunoichi-7B", "AliMaatouk/Llama-3.2-1B-Tele", "prithivMLmods/Bellatrix-Tiny-3B-R1", "m-a-p/Qwen2-Instruct-7B-COIG-P", "zypchn/BehChat-llama-SFT-v2", "TURKCELL/Turkcell-LLM-7b-v1", "Alelcv27/Llama3.2-3B-base-Code", "prithivMLmods/Sqweeks-7B-Instruct", "maxidl/Llama-OpenReviewer-8B", "yufeng1/OpenThinker-7B-type6-e5-max-alpha0_25-textsummarization-2e5-type6-e1-alpha0_375-2", "Thiraput01/PeaceKeeper-4B-V4", "m-a-p/Qwen2.5-Instruct-7B-COIG-P", "SanjiWatsuki/Loyal-Toppy-Bruins-Maid-7B-DARE", "cycloneboy/CscSQL-Merge-Qwen2.5-Coder-3B-Instruct", "prithivMLmods/Monoceros-QwenM-1.5B", "Shanghai_AI_Laboratory/AlchemistCoder-L-7B", "yujiepan/qwen3-tiny-random-tp", "prithivMLmods/rStar-Coder-Qwen3-0.6B", "Qwen/Qwen1.5-14B", "yil384/Qwen3-0.6B-full", "Thiraput01/PeaceKeeper-4B-V3", "ystemsrx/Qwen2.5-Interpreter", "yujiepan/baguettotron-tiny-random", "prithivMLmods/Cerium-Qwen3-R1-Dev", "QwenCollection/Nxcode-CQ-7B-orpo", "prithivMLmods/Viper-OneCoder-UIGEN", "yasserrmd/GLM4.7-Distill-LFM2.5-1.2B", "kairawal/Llama-3.2-3B-Instruct-PT-SynthDolly-E1-S73", "carsenk/llama3.2_3b_122824_uncensored", "prithivMLmods/Telescopium-Acyclic-Qwen3-0.6B", "prithivMLmods/Deneb-Qwen3-Radiation-0.6B", "cycloneboy/CscSQL-Merge-Qwen2.5-Coder-1.5B-Instruct", "ziaulkarim245/Deepseek-R1-Phishing-Detector", "rLLM/rLLM-FinQA-4B", "aisingapore/Qwen-SEA-LION-v4-32B-IT-4BIT", "yam-peleg/Experiment8-7B", "prithivMLmods/Castula-U2-QwenRe-1.5B", "yam-peleg/Experiment31-7B", "yam-peleg/Experiment30-7B", "PrimeIntellect/Qwen3-8B", "Thiraput01/PeaceKeeper-4B", "PistachioAlt/Noromaid-Bagel-7B-Slerp", "PygmalionAI/pygmalion-2-7b", "yam-peleg/Experiment28-7B", "mlabonne/DatacampLlama-3.1-8B", "prithivMLmods/Pictor-1338-QwenP-1.5B", "prithivMLmods/Viper-Coder-v0.1", "NousResearch/Yarn-Llama-2-13b-128k", "TheDrummer/Llama-3SOME-8B-v2", "OpenBuddy/openbuddy-llama2-13b-v11-bf16", "yam-peleg/Experiment22-7B", "OpenPipe/llama_3b_hn_story_classifier", "yam-peleg/Experiment2-7B", "ybelkada/Mistral-7B-v0.1-bf16-sharded", "xw1234gan/Main_fixed_MATH_3B_step_1", "Ihor/Text2Graph-R1-Qwen2.5-0.5b", "unsloth/llama-2-7b", "neuralmagic/SparseLlama-2-7b-cnn-daily-mail-pruned_50.2of4", "ThaiLLM/ThaiLLM-8B", "voidful/unit-desta-8b-base-llama3-8b-instruct", "SouravCrypto/Qwen2.5-0.5B-Instruct-Gensyn-Swarm-striped_tawny_dove", "xw1234gan/Main_fixed02_MATH_3B_step_4", "xw1234gan/Main_MATH_3B_step_9", "m-a-p/Infinity-Instruct-3M-0625-Mistral-7B-COIG-P", "adityakum667388/lumichats-v1.1", "NEU-HAI/Llama-2-7b-alpaca-cleaned", "NousResearch/CodeLlama-34b-hf", "prithivMLmods/Tulu-MathLingo-8B", "ibm-granite/granite-8b-code-instruct-4k", "alexxbobr/ORPO8000Vikhr-Llama-3.2-1B-Instruct5000", "m-a-p/TreePO-Qwen2.5-7B", "fancyfeast/llama-bigasp-prompt-enhancer", "line-corporation/japanese-large-lm-1.7b", "wassname/qwen3-5lyr-tiny-random", "TaimurShaikh/qwen1.5-1.8b-sft", "l3utterfly/minima-3b-layla-v1", "l3utterfly/mistral-7b-v0.1-layla-v2", "Henrychur/MMed-Llama-3-8B", "gradient-spaces/respace-sg-llm-1.5b", "l3utterfly/minima-3b-layla-v2", "allenai/truthfulqa-info-judge-llama2-7B", "facebook/llm-compiler-7b", "nothingiisreal/L3.1-8B-Celeste-V1.5", "TaimurShaikh/qwen1.5-1.8b-dpo", "l3utterfly/tinyllama-1.1b-layla-v4", "prithivMLmods/Flerovium-Llama-3B", "l3utterfly/mistral-7b-v0.1-layla-v1", "OpenBuddy/openbuddy-mistral-7b-v13.1", "iCIIT/redqueenprotocol-sin-llama3.2-3B-model", "OpenAssistant/codellama-13b-oasst-sft-v10", "Unitedp2p/New-Llama-3.1-8B-Lexi-Uncensored-V2", "USTC-KnowledgeComputingLab/Llama3-KALE-LM-Chem-1.5-8B", "Salesforce/Llama-xLAM-2-8b-fc-r", "uukuguy/speechless-orca-platypus-coig-lite-4k-0.6e-13b", "krevas/SOLAR-10.7B", "SykoSLM/SykoLLM-V6.0-Test", "l3utterfly/mistral-7b-v0.1-layla-v4", "argilla/distilabeled-Marcoro14-7B-slerp", "uzlm/alloma-8B-Instruct", "trillionlabs/Tri-7B", "nlile/PE-7b-full", "glaiveai/glaive-coder-7b", "RamziRebai/llama-2-7b-therapist-v4", "Lazycuber/L2-7b-Base-Guanaco-Vicuna", "behnamsh/gpt2_camel_physics", "jondurbin/spicyboros-7b-2.2", "argilla/distilabeled-Marcoro14-7B-slerp-full", "kairawal/Llama-3.2-3B-Instruct-ZH-SynthDolly-1A-E5", "Surpem/Supertron1-8B", "TIGER-Lab/Mantis-8B-siglip-llama3-pretraind", "Leopo1d/OpenVul-Qwen3-4B-GRPO", "totally-not-an-llm/PuddleJumper-13b-V2", "ajibawa-2023/Code-Mistral-7B", "unsloth/Qwen2.5-3B", "Jiqing/tiny-random-qwen2", "L33tcode/llama-3-8b-CEH-hf", "FlyPig23/Qwen3-4B_Paper_Impact_patent_SFT_1ep", "totally-not-an-llm/EverythingLM-13b-V3-16k", "totally-not-an-llm/EverythingLM-13b-16k", "ajibawa-2023/Code-290k-6.7B-Instruct", "mrcuddle/Lumimaid-Muse-12B", "how3751/coder_7B", "m-a-p/CT-LLM-SFT", "Marintosti/chsa-triage-merged", "m-a-p/CT-LLM-SFT-DPO", "inclusionAI/AReaL-boba-2-8B", "FreedomIntelligence/Apollo-6B", "mrthor102/evolai-tfm-super-004", "staeiou/bartleby-qwen3-1.7b_v4", "golgat/toolcalling-merged-demo", "allenai/Llama-3.1-Tulu-3-8B-SFT", "Kazuki1450/Olmo-3-1025-7B_dsum_3_6_tok_Certainly_1p0_0p0_1p0_grpo_dr_grpo_42_rule", "driaforall/Dria-Agent-a-7B", "ChaoticNeutrals/Eris_Remix_7B", "thrishala/mental_health_chatbot", "mlfoundations-dev/oh-dcft-v3.1-gemini-1.5-flash", "CompassioninMachineLearning/pretrainingBasellama3kv3", "venkycs/Zyte-1B", "mnoukhov/pythia410m-sft-tldr", "ali-elganzory/Baguettotron-DPO-Tulu3-decontaminated", "Alienpenguin10/M3PO-kl_divergence-trial1-seed123", "tanhao2015/MDtranslator", "aloobun/Reyna-CoT-4B-v0.1", "sstoica12/influence_metamath_qwen2.5_3b_new_detailed", "argilla/zephyr-7b-spin-iter1-v0", "Kazuki1450/Olmo-3-1025-7B_dsum_3_6_rel_1e0_1p0_0p0_1p0_grpo_sapo_42_rule", "FelixChao/Voldemort-10B", "yam-peleg/Experiment1-7B", "Eric111/Mistral-7B-Instruct_v0.2_UNA-TheBeagle-7b-v1", "ewqr2130/llama_ppo_1e6_new_tokenizerstep_8000", "DanielClough/Candle_TinyLlama-1.1B-Chat-v1.0", "rbelanec/train_qnli_42_1779286680", "tech27/Qwen2.5-1.5B-Instruct-Gensyn-Swarm-amphibious_spotted_kingfisher", "syvai/emotion-reasoning-1b", "argilla/notus-7b-v1", "burtenshaw/SmolLM3-3B-GRPO-think", "ViratChauhan/Qwen3-4B-GRPO-v2", "zarakiquemparte/zaraxls-l2-7b", "AlexeySorokin/GEC-from-explanations-4BInstr-distilled-v2303", "Alelcv27/Qwen2.5-3B-INST-Code", "tiny-random/llama-3.3-dim64", "prithivMLmods/Triangulum-1B", ] # ══════════════════════════════════════════════════════════ # 全局状态(供 /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(token: str, model_id: str) -> Tuple[bool, str]: headers = { "Content-Type": "application/json", "Authorization": f"Bearer {token}", } 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 modelhub_options: srcRelativePath: leaderboard/modelHubXC/{model_id} mountPoint: /model 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 = { "contestApiToken": CONTEST_API_TOKEN, "contributors": CONTRIBUTORS, "gpuTypes": [GPU_TYPE], "taskType": TASK_TYPE, "modelId": model_id, "framework": "vllm", "strategyId": STRATEGY_ID, # 平台要求 "submissionConfig": [{ "config": config_content, "gpuType": GPU_TYPE, "taskType": TASK_TYPE, }], } print(f"[payload] {json.dumps(payload, indent=2, ensure_ascii=False)}", flush=True) try: resp = requests.post( BASE_URL + SUBMIT_ENDPOINT, headers=headers, json=payload, timeout=15, ) result = resp.json() if result.get("code") == 0: task_id = result.get("data", {}).get("id", "") print(f"[worker] OK {model_id} task_id={task_id}", flush=True) return True, task_id else: print(f"[worker] FAIL {model_id}: {result.get('message')}", flush=True) return False, "" except Exception as e: print(f"[worker] ERROR {model_id}: {e}", flush=True) return False, "" def _run_worker(): _state["started_at"] = datetime.utcnow().isoformat() _state["phase"] = "submitting" successful: List[Tuple[str, str]] = [] token = AUTH_TOKEN print("[worker] 使用预设 Token,跳过登录", flush=True) for model_id in ALL_MODEL_IDS: if _shutdown.is_set(): break ok, task_id = _submit_task(token, model_id) if ok: _state["submitted"] += 1 successful.append((task_id, model_id)) else: _state["failed"] += 1 # 写入结果文件 try: with open("submitted_validation_tasks.txt", "w", encoding="utf-8") as f: for tid, mid in successful: f.write(f"{tid}\t{mid}\n") except Exception: pass _state["finished_at"] = datetime.utcnow().isoformat() _state["phase"] = "done" print( f"[worker] 完成 submitted={_state['submitted']} failed={_state['failed']}", 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()