Implement zhoukaile validation task stop strategy
This commit is contained in:
57
README.md
57
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=<zhoukaile 的 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
|
||||
```
|
||||
|
||||
533
main.py
533
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__":
|
||||
|
||||
Reference in New Issue
Block a user