19 Commits

Author SHA1 Message Date
9b5087467f add Kunlunxin_p-800 as 4th GPU with filtered model list 2026-07-29 15:16:34 +08:00
7dcada5617 convert to multi-GPU submission (Biren/Cambricon/MetaX) with fresh filtered model lists 2026-07-29 14:26:16 +08:00
5958df93b0 switch to Cambricon_mlu-370-x8 with new model list, refresh AUTH_TOKEN 2026-07-27 16:46:41 +08:00
a73274e6a4 switch back to ppu_zw_810e with new model list 2026-07-23 14:27:36 +08:00
b3c577219f switch to Biren_166m GPU with new model list 2026-07-22 13:53:23 +08:00
1591b3050e refresh expired AUTH_TOKEN 2026-07-21 18:56:31 +08:00
55c77faa70 update model list 2026-07-21 18:42:47 +08:00
e51533e0bf update model list
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:58:36 +08:00
4e603b9fb0 update 2026-07-14 19:06:47 +08:00
5fe8bf27e5 update main.py
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 18:41:06 +08:00
d6b0e416db update ppu 2026-07-13 19:43:35 +08:00
4dcfed6b6d update ppu 2026-07-13 18:38:55 +08:00
1e8cfacd8e uodate 2026-06-22 19:00:46 +08:00
d6cca90496 update main.py 2026-06-22 18:44:42 +08:00
031e0dc7a8 update main.py 2026-06-19 01:48:50 +08:00
af6f501a5a update main.py 2026-06-18 15:22:29 +08:00
94da35d152 clean up Dockerfile 2026-06-14 23:55:41 +08:00
5b92f129d2 clean up Dockerfile 2026-06-14 23:54:02 +08:00
87d4ae1c18 fix: add env vars to Dockerfile 2026-06-12 21:02:56 +08:00
4 changed files with 282 additions and 86 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
.DS_Store
__pycache__/

View File

@@ -1,11 +1,7 @@
FROM modelhubxc-4pd.tencentcloudcr.com/xc_agent_platform/python:3.11-slim FROM modelhubxc-4pd.tencentcloudcr.com/xc_agent_platform/python:3.11-slim
ENV PYTHONUNBUFFERED=1 \ ENV PYTHONUNBUFFERED=1
USER_ACCOUNT="zhoushasha@4paradigm.com" \
USER_PASSWORD="4pdpassword" \
CONTEST_API_TOKEN="ef1ef82f3c9efee413d602345fbe224d" \
CONTRIBUTORS="zhoushasha" \
GPU_TYPE="Cambricon_mlu-370-x8"
WORKDIR /app WORKDIR /app

View File

@@ -1,5 +1,29 @@
# xc_validation_strategy # xc_validation_strategy
信创自动化模型适配平台 — 验证策略服务 批量向 ModelHub XC 平台提交模型验证任务的策略服务,之后保持 HTTP 服务存活供平台探活。
从 HuggingFace 周期性抓取新模型,自动完成同步、下载、提交验证任务的全流程,常驻运行在 xc_agent_platform 上。 ## 功能
- 自动登录 ModelHub 获取 Token
- 批量提交模型验证任务vLLM 框架Cambricon MLU-370-x8
- 提交结果写入 `submitted_validation_tasks.txt`
- 暴露 `/health``/status` 接口满足平台运行时契约
## 项目结构
```
.
├── main.py # 主入口HTTP 服务 + 提交逻辑
├── Dockerfile # 平台镜像构建配置
├── requirements.txt # Python 依赖
└── submitted_validation_tasks.txt # 运行后自动生成,记录提交结果
```
## 平台契约说明
本项目满足平台对策略镜像的全部必要约束:
- Dockerfile 位于仓库根目录,基于官方轻量基础镜像
- 暴露 8080 端口并实现 `GET /health`
- 通过环境变量 `STRATEGY_ID` 获取策略 ID
- 正确处理 `SIGTERM` 信号,支持优雅停机

328
main.py
View File

@@ -1,7 +1,9 @@
""" """
xc_validation_strategy — 主入口 xc_validation_strategy — 主入口
启动后执行一次模型验证任务批量提交,之后保持 HTTP 服务存活。 启动后针对 4 张 GPU 卡Biren_166m / Cambricon_mlu-370-x8 / MetaX_c-500 /
Kunlunxin_p-800分别批量提交各自筛选出的模型验证任务/adminApi/async/task/create-contest-task
Bearer Token 认证),之后保持 HTTP 服务存活。
同时暴露 /healthK8s 探活)和 /status运行状态 同时暴露 /healthK8s 探活)和 /status运行状态
""" """
@@ -9,7 +11,6 @@ import json
import os import os
import signal import signal
import threading import threading
import traceback
from datetime import datetime from datetime import datetime
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from typing import List, Tuple from typing import List, Tuple
@@ -20,55 +21,153 @@ import requests
# 配置(全部从环境变量读取,不硬编码敏感信息) # 配置(全部从环境变量读取,不硬编码敏感信息)
# ══════════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════════
BASE_URL = os.environ.get("BASE_URL", "https://modelhub.org.cn") BASE_URL = os.environ.get("BASE_URL", "https://modelhub.org.cn")
LOGIN_ENDPOINT = "/adminApi/user/login"
SUBMIT_ENDPOINT = "/adminApi/async/task/create-contest-task" SUBMIT_ENDPOINT = "/adminApi/async/task/create-contest-task"
USER_ACCOUNT = os.environ["USER_ACCOUNT"] # 必填 # 通过 curl -X POST https://modelhub.org.cn/adminApi/user/login 获取后填入
USER_PASSWORD = os.environ["USER_PASSWORD"] # 必填 AUTH_TOKEN = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyQWNjb3VudCI6Inpob3VzaGFzaGEiLCJpZCI6MTQsInVzZXJSb2xlIjoibGVhZGVyYm9hcmQiLCJleHAiOjE3ODU3NDY3NTMsImlhdCI6MTc4NTE0MTk1M30.KwUuefNAFSNwq3_Pnaw2nef8ZC6WgsECQ_LMeQnKk2c"
CONTEST_API_TOKEN = os.environ["CONTEST_API_TOKEN"] # 必填 CONTEST_API_TOKEN = "ef1ef82f3c9efee413d602345fbe224d"
STRATEGY_ID = os.environ.get("STRATEGY_ID", "") # 平台注入 CONTRIBUTORS = "zhoushasha"
CONTRIBUTORS = os.environ.get("CONTRIBUTORS", USER_ACCOUNT) TASK_TYPE = "text-generation"
GPU_TYPE = os.environ.get("GPU_TYPE", "Cambricon_mlu-370-x8") STRATEGY_ID = os.environ.get("STRATEGY_ID", "") # 平台自动注入,无需修改
TASK_TYPE = os.environ.get("TASK_TYPE", "text-generation")
HTTP_HOST = "0.0.0.0" HTTP_HOST = "0.0.0.0"
HTTP_PORT = 8080 HTTP_PORT = 8080
# ══════════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════════
# 模型列表 # 各 GPU 的模型列表(来自 filter_verified_models 脚本的筛选结果)
# ══════════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════════
ALL_MODEL_IDS = [ BIREN_MODELS = [
"AI-ModelScope/gemma-2b", "BigRatz/LOL-AI-2026",
"AI-ModelScope/falcon-mamba-7b", "aimeri/spoomplesmaxx-cardmaker-v1",
"katanemo/deepseek-2", "Alibaba-DT/Logics-STEM-8B-SFT",
"OpenBMB/MiniCPM4-0.5B", "Muneebmn123/insurance-voice-qwen25-1_5b",
"NousResearch/Meta-Llama-3-8B-Instruct", "AnkitBirGurung/NEMO-12B-SFT-Further",
"MediaTek-Research/Breeze-7B-Instruct-v1_0", "danilarudenko/editorai-mini",
"QLUNLP/BianCang-Qwen2.5-7B-Instruct", "EphemeralYou/Prompt-Refine-MiniCPM5-1B",
"OpenBMB/MiniCPM4-Survey", "mtepe01/mentorx-mistral-7b-automata-merged",
"OpenBMB/MiniCPM4-8B", "DarkArtsForge/Helix-SCE-12B-jh",
"PaddlePaddle/ERNIE-4.5-0.3B-PT", "rpant/iolai26-solve",
"LLM-Research/Llama-Guard-3-8B", "NithinAI12/NithinX-Omni-LLM-v1",
"OpenBMB/MiniCPM-2B-dpo-fp16", "ConvexAI/Luminex-34B-v0.2",
"OpenBMB/MiniCPM4.1-8B", "codellama/CodeLlama-34b-hf",
"Cylingo/Xinyuan-LLM-14B-0428", "Lipas007/iol-ai-2026-qwen14b-awq",
"Fengshenbang/Ziya-LLaMA-13B-v1", "D-Z-W/finetuned-teacher",
"baichuan-inc/Baichuan2-13B-Chat", "huan1999/ziya-llama-13b-medical-merged",
"LLM-Research/gemma-2-9b-it", "codellama/CodeLlama-34b-Python-hf",
"Qwen/CodeQwen1.5-7B-Chat", "ld4ad/gemma-2-9b-dunhuang",
"OpenBMB/cpm-bee-10b", "harindhar10/Olmo-7b_1M_Smiles_lora",
"OpenBMB/MiniCPM3-4B", "allenai/Olmo-3-7B-Think-DPO",
"allenai/Olmo-3-32B-Think-DPO",
"RedHatAI/gemma-2-9b-it",
"prashanthsura/gemma-2-2b-legal-financial-sft",
"pfnet/plamo-2-8b",
"sail/Sailor2-20B-128K-SFT",
"facebook/layerskip-llama3.2-1B",
"Qwen/Qwen2.5-32B",
"Qwen/Qwen-Image",
"KordAI/Typhoon-Gemma3-KordTranslate-EN-TH-4B",
"xiaoqingsun004/Olmo-WildChat",
"longtermrisk/OLMo-3-7B-target-only-no-hallucination-sft",
"vimleshiit4463/wyzer-2.0-smollm2-135m",
"trl-lib/pythia-1b-deduped-tldr-sft",
] ]
CAMBRICON_MODELS = [
"clear-blue-sky/evolai-reborn-tfm-008",
"clear-blue-sky/evolai-reborn-tfm-010",
"BigRatz/LOL-AI-2026",
"clear-blue-sky/evolai-reborn-tfm-001",
"clear-blue-sky/evolai-reborn-tfm-019",
"clear-blue-sky/evolai-reborn-tfm-007",
"aimeri/spoomplesmaxx-cardmaker-v1",
"aipatseer/inst_ft_qwen_0.6b_summ",
"clear-blue-sky/evolai-reborn-tfm-003",
"clear-blue-sky/evolai-reborn-tfm-004",
"Alibaba-DT/Logics-STEM-8B-SFT",
"clear-blue-sky/evolai-reborn-tfm-002",
"prism-ml/Ternary-Bonsai-4B-unpacked",
"Muneebmn123/insurance-voice-qwen25-1_5b",
"AnkitBirGurung/NEMO-12B-SFT-Further",
"danilarudenko/editorai-mini",
"rpant/iolai26-solve",
]
METAX_MODELS = [
"Phoenix9781/evolai-tf-model-105",
"clear-blue-sky/evolai-reborn-tfm-008",
"clear-blue-sky/evolai-reborn-tfm-010",
"BigRatz/LOL-AI-2026",
"clear-blue-sky/evolai-reborn-tfm-001",
"clear-blue-sky/evolai-reborn-tfm-019",
"clear-blue-sky/evolai-reborn-tfm-007",
"clear-blue-sky/evolai-reborn-tfm-005",
"Lin2es/evolai-tfm-03o",
"clear-blue-sky/evolai-reborn-tfm-009",
"aimeri/spoomplesmaxx-cardmaker-v1",
"aipatseer/inst_ft_qwen_0.6b_summ",
"reaperdoesntknow/DualMind-TKD-Agentic-1.7B",
"clear-blue-sky/evolai-reborn-tfm-011",
"clear-blue-sky/evolai-reborn-tfm-003",
"clear-blue-sky/evolai-reborn-tfm-004",
"clear-blue-sky/evolai-reborn-tfm-002",
"amd/ReasonLite-0.6B",
"prism-ml/Ternary-Bonsai-4B-unpacked",
"Muneebmn123/insurance-voice-qwen25-1_5b",
"danilarudenko/editorai-mini",
"rpant/iolai26-solve",
]
KUNLUNXIN_MODELS = [
"Patriae/patriae-cuban-dialect-model",
"Phoenix9781/evolai-tf-model-105",
"clear-blue-sky/evolai-reborn-tfm-008",
"clear-blue-sky/evolai-reborn-tfm-010",
"clear-blue-sky/evolai-reborn-tfm-001",
"clear-blue-sky/evolai-reborn-tfm-019",
"clear-blue-sky/evolai-reborn-tfm-007",
"clear-blue-sky/evolai-reborn-tfm-005",
"Lin2es/evolai-tfm-03o",
"clear-blue-sky/evolai-reborn-tfm-009",
"aimeri/spoomplesmaxx-cardmaker-v1",
"aipatseer/inst_ft_qwen_0.6b_summ",
"clear-blue-sky/evolai-reborn-tfm-011",
"clear-blue-sky/evolai-reborn-tfm-003",
"clear-blue-sky/evolai-reborn-tfm-004",
"clear-blue-sky/evolai-reborn-tfm-002",
"amd/ReasonLite-0.6B",
"prism-ml/Ternary-Bonsai-4B-unpacked",
"EthanGao123/CellHermes-v1.0",
"RecursiveMAS/Mixture-Science-BioMistral-7B",
"chenyitian-shanshu/SIRL-Gurobi",
"RedHatAI/gemma-2-9b-it",
"gaunernst/gemma-3-27b-it-qat-autoawq",
"promotion/qwen3-8b-simpo-avg-b2p5-g1p0-s42",
"llamaindex/vdr-2b-multi-v1",
"KordAI/Typhoon-Gemma3-KordTranslate-EN-TH-4B",
"TheDrummer/UnslopNemo-12B-v3",
"gradients-io-tournaments/tournament-tourn_c5d86c82ce819a79_20260706-b78a01d4-0a6a-49e1-9190-5e88ae329937-5DS6XMVr",
"ArliAI/Mistral-Nemo-12B-ArliAI-RPMax-v1.1",
]
# 按顺序处理Biren → Cambricon → MetaX → Kunlunxin
GPU_JOBS: List[Tuple[str, List[str]]] = [
("Biren_166m", BIREN_MODELS),
("Cambricon_mlu-370-x8", CAMBRICON_MODELS),
("MetaX_c-500", METAX_MODELS),
("Kunlunxin_p-800", KUNLUNXIN_MODELS),
]
TOTAL_MODELS = sum(len(models) for _, models in GPU_JOBS)
# ══════════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════════
# 全局状态(供 /status 展示) # 全局状态(供 /status 展示)
# ══════════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════════
_state = { _state = {
"strategy_id": STRATEGY_ID, "strategy_id": STRATEGY_ID,
"phase": "starting", # starting | submitting | done | error "phase": "starting", # starting | submitting | done | error
"total": len(ALL_MODEL_IDS), "total": TOTAL_MODELS,
"submitted": 0, "submitted": 0,
"failed": 0, "failed": 0,
"per_gpu": {gpu: 0 for gpu, _ in GPU_JOBS},
"started_at": None, "started_at": None,
"finished_at": None, "finished_at": None,
} }
@@ -108,29 +207,40 @@ def _run_http():
print("[http] 已关闭", flush=True) print("[http] 已关闭", flush=True)
# ══════════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════════
# 业务逻辑 # 各 GPU 的 config_content 模板
# ══════════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════════
def _login() -> str: def build_config_content(gpu_type: str, model_id: str) -> str:
headers = {"Content-Type": "application/json"} if gpu_type == "Biren_166m":
resp = requests.post( max_model_len = 4096
BASE_URL + LOGIN_ENDPOINT, return f"""docker_image: git.modelhub.org.cn:9443/enginex/xc-llm-biren166m:26.01
headers=headers, nv_docker_image: harbor.4pd.io/dooke/vllm/vllm/vllm-openai:v0.11.0
json={"userAccount": USER_ACCOUNT, "userPassword": USER_PASSWORD}, framework: vllm
timeout=30, lang: zh
) storage: gpfs
data = resp.json() api: completion
if data.get("code") != 0: modelhub_options:
raise RuntimeError(f"登录失败: {data.get('message')}") srcRelativePath: leaderboard/modelHubXC/{model_id}
print("[worker] 登录成功", flush=True) mountPoint: /model
return data["data"]["token"] max_model_len: {max_model_len}
sut_config:
values:
def _submit_task(token: str, model_id: str) -> Tuple[bool, str]: gpu_num: 1
headers = { env:
"Content-Type": "application/json", - name: MAX_MODEL_LEN
"Authorization": f"Bearer {token}", 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']
config_content = f"""docker_image: harbor.4pd.io/hardcore-tech/cambricon-mlu370-pytorch:v25.01-torch2.5.0-torchmlu1.24.1-ubuntu22.04-py310 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
"""
elif gpu_type == "Cambricon_mlu-370-x8":
return f"""docker_image: harbor.4pd.io/hardcore-tech/cambricon-mlu370-pytorch:v25.01-torch2.5.0-torchmlu1.24.1-ubuntu22.04-py310
nv_docker_image: harbor.4pd.io/dooke/vllm/vllm/vllm-openai:v0.11.0 nv_docker_image: harbor.4pd.io/dooke/vllm/vllm/vllm-openai:v0.11.0
framework: vllm framework: vllm
storage: gpfs storage: gpfs
@@ -153,20 +263,81 @@ ref_config:
value: 8192 value: 8192
command: ["vllm", "serve", "/model", "--port", "80", "--served-model-name", "llm", "--max-model-len", "8192", "--trust-remote-code", "--dtype", "float16"] command: ["vllm", "serve", "/model", "--port", "80", "--served-model-name", "llm", "--max-model-len", "8192", "--trust-remote-code", "--dtype", "float16"]
""" """
elif gpu_type == "MetaX_c-500":
return f"""docker_image: git.modelhub.org.cn:9443/enginex-metax/vllm:0.9.1
nv_docker_image: harbor.4pd.io/dooke/vllm/vllm/vllm-openai:v0.11.0
framework: vllm
lang: en
storage: gpfs
api: chat
modelhub_options:
srcRelativePath: leaderboard/modelHubXC/{model_id}
mountPoint: /model
max_model_len: 2048
sut_config:
gpu_num: 1
values:
command: ['/opt/conda/bin/vllm', 'serve', '/model', '--port', '20644', '--served-model-name', 'llm', '--max-model-len', '2048', '--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', '2048', '--enforce-eager', '--trust-remote-code', '-tp', '1']
"""
elif gpu_type == "Kunlunxin_p-800":
return 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']
"""
else:
raise ValueError(f"未知的 GPU_TYPE: {gpu_type}")
# ══════════════════════════════════════════════════════════
# 业务逻辑
# ══════════════════════════════════════════════════════════
def _submit_task(token: str, gpu_type: str, model_id: str) -> Tuple[bool, str]:
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {token}",
}
config_content = build_config_content(gpu_type, model_id)
payload = { payload = {
"contestApiToken": CONTEST_API_TOKEN, "contestApiToken": CONTEST_API_TOKEN,
"contributors": CONTRIBUTORS, "contributors": CONTRIBUTORS,
"gpuTypes": [GPU_TYPE], "gpuTypes": [gpu_type],
"taskType": TASK_TYPE, "taskType": TASK_TYPE,
"modelId": model_id, "modelId": model_id,
"framework": "vllm", "framework": "vllm",
"strategyId": STRATEGY_ID, # 平台要求 "strategyId": STRATEGY_ID, # 平台要求
"submissionConfig": [{ "submissionConfig": [{
"config": config_content, "config": config_content,
"gpuType": GPU_TYPE, "gpuType": gpu_type,
"taskType": TASK_TYPE, "taskType": TASK_TYPE,
}], }],
} }
print(f"[payload] gpu={gpu_type} model={model_id}", flush=True)
try: try:
resp = requests.post( resp = requests.post(
BASE_URL + SUBMIT_ENDPOINT, BASE_URL + SUBMIT_ENDPOINT,
@@ -177,13 +348,13 @@ ref_config:
result = resp.json() result = resp.json()
if result.get("code") == 0: if result.get("code") == 0:
task_id = result.get("data", {}).get("id", "") task_id = result.get("data", {}).get("id", "")
print(f"[worker] OK {model_id} task_id={task_id}", flush=True) print(f"[worker] OK {model_id} (GPU={gpu_type}) task_id={task_id}", flush=True)
return True, task_id return True, task_id
else: else:
print(f"[worker] FAIL {model_id}: {result.get('message')}", flush=True) print(f"[worker] FAIL {model_id} (GPU={gpu_type}): {result.get('message')}", flush=True)
return False, "" return False, ""
except Exception as e: except Exception as e:
print(f"[worker] ERROR {model_id}: {e}", flush=True) print(f"[worker] ERROR {model_id} (GPU={gpu_type}): {e}", flush=True)
return False, "" return False, ""
@@ -191,36 +362,39 @@ def _run_worker():
_state["started_at"] = datetime.utcnow().isoformat() _state["started_at"] = datetime.utcnow().isoformat()
_state["phase"] = "submitting" _state["phase"] = "submitting"
successful: List[Tuple[str, str]] = [] successful: List[Tuple[str, str, str]] = []
try: token = AUTH_TOKEN
token = _login() print("[worker] 使用预设 Token跳过登录", flush=True)
except Exception:
traceback.print_exc()
_state["phase"] = "error"
return
for model_id in ALL_MODEL_IDS: for gpu_type, model_list in GPU_JOBS:
if _shutdown.is_set(): if _shutdown.is_set():
break break
ok, task_id = _submit_task(token, model_id) print(f"\n{'='*60}\n🚀 开始处理 GPU={gpu_type},共 {len(model_list)} 个模型\n{'='*60}", flush=True)
if ok:
_state["submitted"] += 1 for model_id in model_list:
successful.append((task_id, model_id)) if _shutdown.is_set():
else: break
_state["failed"] += 1 ok, task_id = _submit_task(token, gpu_type, model_id)
if ok:
_state["submitted"] += 1
_state["per_gpu"][gpu_type] += 1
successful.append((task_id, gpu_type, model_id))
else:
_state["failed"] += 1
# 写入结果文件 # 写入结果文件
try: try:
with open("submitted_validation_tasks.txt", "w", encoding="utf-8") as f: with open("submitted_validation_tasks.txt", "w", encoding="utf-8") as f:
for tid, mid in successful: for tid, gpu, mid in successful:
f.write(f"{tid}\t{mid}\n") f.write(f"{tid}\t{gpu}\t{mid}\n")
except Exception: except Exception:
pass pass
_state["finished_at"] = datetime.utcnow().isoformat() _state["finished_at"] = datetime.utcnow().isoformat()
_state["phase"] = "done" _state["phase"] = "done"
print( print(
f"[worker] 完成 submitted={_state['submitted']} failed={_state['failed']}", f"[worker] 完成 submitted={_state['submitted']} failed={_state['failed']} "
f"total={_state['total']} per_gpu={_state['per_gpu']}",
flush=True, flush=True,
) )
# 提交完成后继续保持进程存活,等待平台停止 # 提交完成后继续保持进程存活,等待平台停止