9 Commits

Author SHA1 Message Date
a9fe1f6393 update model list
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 17:03:42 +08:00
9aba1595db init 2026-07-14 16:44:33 +08:00
85f41bba58 Replace main.py with GGUF download + validation submit pipeline
Port logic from continuous_pipeline_download_gguf_zhoushasha_and_submit.py
into the strategy-deployable framework (health/status HTTP server,
STRATEGY_ID env, SIGTERM handling).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 15:45:51 +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
4 changed files with 583 additions and 121 deletions

2
.gitignore vendored Normal file
View File

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

View File

@@ -2,6 +2,7 @@ FROM modelhubxc-4pd.tencentcloudcr.com/xc_agent_platform/python:3.11-slim
ENV PYTHONUNBUFFERED=1
WORKDIR /app
COPY requirements.txt .

View File

@@ -1,22 +1,24 @@
# xc_validation_strategy
# xc_validation_strategy_gguf
批量向 ModelHub XC 平台提交模型验证任务的策略服务,之后保持 HTTP 服务存活供平台探活。
GGUF 模型下载 + 验证任务提交流水线策略服务:批量创建 GGUF 模型下载任务(最大并发 8
每个模型下载成功后立即提交 hygon / bi150 两个验证任务,之后保持 HTTP 服务存活供平台探活。
## 功能
- 自动登录 ModelHub 获取 Token
- 批量提交模型验证任务vLLM 框架Cambricon MLU-370-x8
- 提交结果写入 `submitted_validation_tasks.txt`
- 自动登录 ModelHub 获取 Token(失败时回退到预设 Token
- 按流水线批量创建 GGUF 模型下载任务HuggingFace 源,最大并发 8
- 每个模型下载成功后,立即提交 hygon_k100-ai 与 Iluvatar_bi-150 两个验证任务llamacpp 框架)
- 下载成功的模型 ID 写入 `downloaded_success_models.txt`
- 暴露 `/health``/status` 接口满足平台运行时契约
## 项目结构
```
.
├── main.py # 主入口HTTP 服务 + 提交逻辑
├── main.py # 主入口HTTP 服务 + 下载/提交流水线
├── Dockerfile # 平台镜像构建配置
├── requirements.txt # Python 依赖
└── submitted_validation_tasks.txt # 运行后自动生成,记录提交结果
└── downloaded_success_models.txt # 运行后自动生成,记录下载成功的模型
```
## 平台契约说明

643
main.py
View File

@@ -1,34 +1,48 @@
"""
xc_validation_strategy — 主入口
xc_validation_strategy_gguf — 主入口
启动后执行一次模型验证任务批量提交,之后保持 HTTP 服务存活
GGUF 模型下载 + 验证任务提交流水线(部署框架与 xc_validation_strategy 一致)
启动后运行流水线:批量创建 GGUF 模型下载任务(最大并发 8
每个模型下载成功后立即提交 hygon / bi150 两个验证任务;
同时暴露 /healthK8s 探活)和 /status运行状态
"""
import json
import os
import re
import signal
import threading
from datetime import datetime
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from typing import List, Tuple
from typing import Set
import requests
# ══════════════════════════════════════════════════════════
# 配置(全部从环境变量读取,不硬编码敏感信息)
# 配置
# ══════════════════════════════════════════════════════════
BASE_URL = os.environ.get("BASE_URL", "https://modelhub.org.cn")
SUBMIT_ENDPOINT = "/adminApi/async/task/create-contest-task"
LOGIN_ENDPOINT = "/adminApi/user/login"
CREATE_DOWNLOAD_TASK_ENDPOINT = "/adminApi/async/task/model-download-task"
SUBMIT_TEST_TASK_ENDPOINT = "/adminApi/async/task/create-contest-task"
# 登录账号:启动时优先用账号密码换取新 token流水线运行时间长预设 token 可能过期)
USER_ACCOUNT = "zhoushasha@4paradigm.com"
USER_PASSWORD = "ganshenme0"
# 通过 curl -X POST https://modelhub.org.cn/adminApi/user/login 获取后填入(登录失败时的回退)
AUTH_TOKEN = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyQWNjb3VudCI6Inpob3VzaGFzaGEiLCJpZCI6MTQsInVzZXJSb2xlIjoibGVhZGVyYm9hcmQiLCJleHAiOjE3ODQ1NDc1NDYsImlhdCI6MTc4Mzk0Mjc0Nn0.ZcOqcrfI22LPi4mGMnt164nZGhi61ZxtJGYsoO7fZdM"
# 通过 curl -X POST https://modelhub.org.cn/adminApi/user/login 获取后填入
AUTH_TOKEN = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyQWNjb3VudCI6Inpob3VzaGFzaGEiLCJpZCI6MTQsInVzZXJSb2xlIjoibGVhZGVyYm9hcmQiLCJleHAiOjE3ODE4NTE0NzcsImlhdCI6MTc4MTI0NjY3N30.p3uvCpG50aLNifNVVXxvzmWJahbLM5K1671FVCtj8E8"
CONTEST_API_TOKEN = "ef1ef82f3c9efee413d602345fbe224d"
HF_TOKEN = "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"
CONTRIBUTORS = "zhoushasha"
GPU_TYPE = "Cambricon_mlu-370-x8"
TASK_TYPE = "text-generation"
STRATEGY_ID = os.environ.get("STRATEGY_ID", "") # 平台自动注入,无需修改
MAX_CONCURRENT_DOWNLOADS = 8 # 同时下载的模型数上限
CHECK_INTERVAL_SECONDS = 10 # 下载状态轮询间隔(秒)
HTTP_HOST = "0.0.0.0"
HTTP_PORT = 8080
@@ -36,45 +50,255 @@ HTTP_PORT = 8080
# 模型列表
# ══════════════════════════════════════════════════════════
ALL_MODEL_IDS = [
"NovaSky-AI/Sky-T1-7B",
"prithivMLmods/TESS-QwenRe-Fact-0.5B",
"Magpie-Align/Llama-3.1-8B-Magpie-Align-v0.1",
"Magpie-Align/Llama-3-8B-Magpie-Align-SFT-v0.1",
"Magpie-Align/Llama-3.1-8B-Magpie-Align-SFT-v0.2",
"LLM-Research/layerskip-llama2-7B",
"PowerInfer/SmallThinker-3B-Preview",
"HuggingFaceTB/SmolLM2-360M",
"ModelCloud.AI/Llama3.2-1B-Instruct",
"NousResearch/DeepHermes-3-Llama-3-8B-Preview",
"PAI/DistilQwen2.5-DS3-0324-14B",
"OpenBMB/MiniCPM-1B-sft-bf16",
"OpenBMB/MiniCPM4-8B-marlin-vLLM",
"OpenBMB/MiniCPM-2B-sft-fp32",
"unsloth/gemma-3-1b-it",
"LGAI-EXAONE/EXAONE-3.5-7.8B-Instruct",
"Magpie-Align/Llama-3-8B-WildChat",
"allenai/tulu-2-7b",
"argilla/distilabeled-OpenHermes-2.5-Mistral-7B",
"Fengshenbang/Ziya2-13B-Chat",
"AI-ModelScope/starcoder2-15b",
"AI-ModelScope/phi-2",
"KoboldAI/OPT-13B-Erebus",
"NousResearch/OLMo-Bitnet-1B",
"AI-ModelScope/merlinite-7b",
"IndexTeam/Index-1.9B-Chat",
"AI-ModelScope/gpt2",
"togethercomputer/GPT-JT-6B-v1",
"mradermacher/Llama-3.2-3B-Della-GGUF",
"mradermacher/Aspire_V2_ALT-8B-Model_Stock-GGUF",
"mradermacher/Aspire_V2_ALT_ROW-8B-Model_Stock-GGUF",
"mradermacher/OTG1.6-Instruct-sft-dpo-7B-rc-2025-01-20-GGUF",
"mradermacher/llama-3.2-3b-it-Ehealthcare-ChatBot-v3-GGUF",
"mradermacher/nsfw_plz_gguf_me-GGUF",
"mradermacher/mistral-7b-sci-arc_reasoning_v2-GGUF",
"mradermacher/Jaja-small-GGUF",
"mradermacher/euclid-3B-GGUF",
"mradermacher/Llama-3.2-3B-Blend-GGUF",
"mradermacher/Llama-3.1-8B-KG-Extraction-v2-GGUF",
"mradermacher/Minerva-8b-GGUF",
"mradermacher/RTLCoder-Deepseek_OB25-GGUF",
"mradermacher/RTLCoder-Deepseek_OB50-GGUF",
"mradermacher/MedGPT_Finetuned-GGUF",
"mradermacher/mistral-irl-iter2-iterative-dpo-GGUF",
"mradermacher/Llama-3.2-Taiwan-3B-Instruct-GGUF",
"mradermacher/llama3-8b-breadcrumbs-ties-v4-GGUF",
"mradermacher/llama3-8b-della-v2-GGUF",
"mradermacher/hermes-GGUF",
"mradermacher/Orpo-oman-ar-stablelm-2-chat-GGUF",
"mradermacher/cocoruta-8b-llama3.1-GGUF",
"mradermacher/FalconSlerp6-7B-GGUF",
"mradermacher/mistral-7b-arc_reasoning-GGUF",
"mradermacher/Llama3.1-8B-relu-stage-1-dolma-v1_7-50B-4096-GGUF",
"mradermacher/mistral-7b-CoT-GGUF",
"mradermacher/llama3-8b-dare-ties-v3-GGUF",
"mradermacher/hopefully_humanish-rp-nsfw-test-v1-GGUF",
"mradermacher/mistral-7b-lima_v2-GGUF",
"mradermacher/Mistral-AFT-Off-Policy-GGUF",
"mradermacher/Mistral-AFT-On-Policy-GGUF",
"mradermacher/Llama-AFT-Off-Policy-GGUF",
"mradermacher/Llama-AFT-On-Policy-GGUF",
"mradermacher/OrangeJ-8B-Model_Stock-GGUF",
"mradermacher/Llama3-8b-alpaca-GGUF",
"mradermacher/hitchens-3-1-GGUF",
"mradermacher/Yearn-8B-Model_Stock-GGUF",
"mradermacher/DavidAU-Dark-Planet-of-Davids-8B-64k-GGUF",
"mradermacher/Qwen-2.5-3B-Tiny-Story-GGUF",
"mradermacher/CodeLlama-7b-Instruct-hf-SAP-RAP-GGUF",
"mradermacher/R1-ImpishMind-8B-GGUF",
"mradermacher/llama3-8b-slerp-v2-GGUF",
"mradermacher/layer-skip-vanill-3.2-1b-GGUF",
"mradermacher/AlexeyRyzhikov-Mistral-7b-TXT_to_JSON-V5.2-GGUF",
"mradermacher/SJT-2.5B-GGUF",
"mradermacher/liujx-78k-GGUF",
"mradermacher/Vangelus-Secundus-GGUF",
"mradermacher/bloomz-7b1-p3-GGUF",
"mradermacher/Llama-3.1-8B-price-GGUF",
"mradermacher/asm2asm-deepseek-1.3b-500k-mac-x86-O0-arm-2-GGUF",
"mradermacher/SJT-2.3B-GGUF",
"mradermacher/Llama-3-ELYZA-JP-8B-ojousama-chosen-after-SFTboth-GGUF",
"mradermacher/LLaMA-3-8B-SFR-SFT-R-GGUF",
"mradermacher/ELN-Llama-1B-base-GGUF",
"mradermacher/Qwen0.5b-RagSemanticChunker-GGUF",
"mradermacher/Llama-3-ELYZA-JP-8B-normal-chosen-after-SFTboth-GGUF",
"mradermacher/LLaMA-3-8B-SFR-Iterative-DPO-Concise-R-GGUF",
"mradermacher/Qwen2.7-7B-Instruct-QwQ-PRIME-1k-GGUF",
"mradermacher/flora-v1-GGUF",
"mradermacher/exaone-3.5-2.4b-instruct-dacon-llm2-GGUF",
"mradermacher/RPMash-8B-Model_Stock-GGUF",
"mradermacher/qwen2.5-0.5B_ichikara_4802-GGUF",
"mradermacher/payroll-teacher-model-2-GGUF",
"mradermacher/Phi-3.5-mini-instruct-3x-v1-GGUF",
"mradermacher/ImmyV2.7-GGUF",
"mradermacher/Qwen2.5-3B-Renoia-GGUF",
"mradermacher/ImmyV2.5-GGUF",
"mradermacher/mistral-7b-CoT_v2-GGUF",
"mradermacher/gpt2_individuated_zero_chaos-GGUF",
"mradermacher/ImmyV2.6-GGUF",
"mradermacher/gpt2_trickster-GGUF",
"mradermacher/Phi-3.5-mini-instruct-2x-v1-GGUF",
"mradermacher/llama-3.1-8b-dacon-GGUF",
"mradermacher/Llama3.1-8B-relu-stage-2-dolma-v1_7-50B-4096-GGUF",
"mradermacher/QwQ-LCoT1-Merged-GGUF",
"mradermacher/Magdala-9B-GGUF",
"mradermacher/fireblossom-32K-7B-GGUF",
"mradermacher/RPMash_V2-8B-Model_Stock-GGUF",
"mradermacher/Qwen2-7B-FullBirdnTiger-SmallDB-GGUF",
"mradermacher/Llama-8B-Distill-CoT-GGUF",
"mradermacher/Vangelus-Poetic-9B-GGUF",
"mradermacher/Unbound-Llama3-8B-GGUF",
"mradermacher/DeepSolana-GPT2-GGUF",
"mradermacher/layerskip-llama2-7b-topv1-v1-GGUF",
"mradermacher/QwQ-R1-Distill-7B-CoT-GGUF",
"mradermacher/DeepSeek-R1-MFANN-TIES-unretrained-7b-GGUF",
"mradermacher/mergekit-model_stock-zengbax-GGUF",
"mradermacher/Llama-2-7b-sft-SPIN-Llama-2-70b-Instruct-rm-GGUF",
"mradermacher/ZEUS-8B-V24-GGUF",
"mradermacher/YamshadowInex12_ShadowExperiment24-GGUF",
"mradermacher/SJT-4B-v1.1-GGUF",
"mradermacher/Qwen1.5-0.4B-Chat-GGUF",
"mradermacher/CogitoDistil-GGUF",
"mradermacher/MeliodasPercival_01_AlloyingotneoyInex12-GGUF",
"mradermacher/Experiment26Neuralsirkrishna_Experiment29Experiment24-GGUF",
"mradermacher/Llama-3.1-SISaAI-Ko-merge-8B-Instruct-GGUF",
"mradermacher/asm2asm-deepseek-1.3b-500k-mac-x86-O3-arm-GGUF",
"mradermacher/Soaring-3B-V2-GGUF",
"mradermacher/SJT-2.4B-GGUF",
"mradermacher/Qwen2-7B-sft-SPIN-gpt4o-rm-GGUF",
"mradermacher/Qwen2.5-DeepSeek-R1-MFANN-Slerp-7b-GGUF",
"mradermacher/sara_finetuned-GGUF",
"mradermacher/mistral-7b-v0.1-social_iqa-GGUF",
"mradermacher/Qwen2-7B-sft-SPIN-Qwen2.5-72B-Instruct-rm-GGUF",
"mradermacher/LlamaUz-3.1-8b-ct-GGUF",
"mradermacher/qwen2vl-model-2b-instruct-spatial-information-v1-GGUF",
"mradermacher/qwen2vl-model-2b-instruct-spatial-information-v2-GGUF",
"mradermacher/Aspire_V4-8B-Model_Stock-GGUF",
"mradermacher/kyutech5-jp-GGUF",
"mradermacher/Aspire_V4_ALT-8B-Model_Stock-GGUF",
"mradermacher/SRole_3181-GGUF",
"mradermacher/Bespoke-Stratos-17k-GGUF",
"mradermacher/SineAgentRL-vog-GGUF",
"mradermacher/LLAMA3-ReasoningCOT-GGUF",
"mradermacher/Intelligence-R1-Distill-7B-GGUF",
"mradermacher/Bitnet-M7-resized-GGUF",
"mradermacher/NeuralsirkrishnaShadow_PasticheInex12-GGUF",
"mradermacher/SineAgentRL-v0.2-GGUF",
"mradermacher/LonAI_20250122-GGUF",
"mradermacher/Experiment28M7_Experiment29Experiment24-GGUF",
"mradermacher/Mergerix-7b-v0.1-GGUF",
"mradermacher/Llama-3-8B-Instruct-v0.5-GGUF",
"mradermacher/Experiment26Yam_Multi_verse_modelM7-GGUF",
"mradermacher/Experiment28T3q_OgnoShadow-GGUF",
"mradermacher/Llama-2-7b-chat-hf_fictional_chinese_v2-GGUF",
"mradermacher/M7T3qm7xp_T3qm7xpStrangemerges_32-GGUF",
"mradermacher/Mistral-7B-v0.1-sft-SPIN-gpt4o-rm-GGUF",
"mradermacher/MeliodasPercival_01_Experiment28Experiment29-GGUF",
"mradermacher/Llama-2-7b-sft-SPIN-gpt4o-rm-GGUF",
"mradermacher/Experiment28M7_Strangemerges_32Ogno-GGUF",
"mradermacher/M7Yamshadowexperiment28_Strangemerges_32Strangemerges_30-GGUF",
"mradermacher/YamshadowStrangemerges_32_Inex12Yam-GGUF",
"mradermacher/Commonsense-QA-Mistral-7B-GGUF",
"mradermacher/NeuralsirkrishnaShadow_NeuralExperiment26-GGUF",
"mradermacher/llama2-7b-dpo-full-wo-healthsearch_qa-ep3-GGUF",
"mradermacher/Meta-Llama-3-8B-Instruct_fictional_arc_French_v2-GGUF",
"mradermacher/M7Yamshadowexperiment28_Experiment26Strangemerges_30-GGUF",
"mradermacher/CalmexperimentT3q-7B-GGUF",
"mradermacher/granite-8b-rpgle-GGUF",
"mradermacher/llama3-8b-tofutune-GGUF",
"mradermacher/Bart-finetuned-QA-GGUF",
"mradermacher/TinyLlama-1.1B-Chat-v1.0-mt-GGUF",
"mradermacher/h2o-dpo-merge2-GGUF",
"mradermacher/MKLLM-7B-Instruct-GGUF",
"mradermacher/T3Q-LLM3-Llama3-sft1.0-dpo1.0-GGUF",
"mradermacher/numfalm-3b-GGUF",
"mradermacher/NeuralsirkrishnaShadow_Experiment26Experiment24-GGUF",
"mradermacher/M7Yamshadowexperiment28_Strangemerges_32T3qm7xp-GGUF",
"mradermacher/SparrowMind-8B-GGUF",
"mradermacher/SmolLM-360M-TigerMath-Evaluated-SFT-GGUF",
"mradermacher/BreakingBadLlama-3-8B-GGUF",
"mradermacher/YamshadowInex12_Strangemerges_32Alloyingotneoy-GGUF",
"mradermacher/flammen17-py-DPO-v1-7B-GGUF",
"mradermacher/MetaAligner-HH-RLHF-1.1B-GGUF",
"mradermacher/Experiment26Yam_YamYam-GGUF",
"mradermacher/SmartQwen1.5-1.8B-orpo-v1-GGUF",
"mradermacher/TinyLlama-1.1B-chat-ties-v1-GGUF",
"mradermacher/al-baka-llama3-8b-experimental-GGUF",
"mradermacher/Mistral-7B-Instruct-v0.3-pruned-GGUF",
"mradermacher/Apollo2-3.8B-GGUF",
"mradermacher/Llama-3-DARE-v1-8B-GGUF",
"mradermacher/Experiment28T3q_Experiment27Inex12-GGUF",
"mradermacher/coder-2b-v0.1-hfrl-GGUF",
"mradermacher/myalee-v3-L31-8B-GGUF",
"mradermacher/TinyMoE-DopeykarasuMoe-xdareties2-GGUF",
"mradermacher/llama-3-bophades-v1-8B-GGUF",
"mradermacher/NeuralMonarchCoderPearlBeagle-GGUF",
"mradermacher/An4-7Bv2.3-GGUF",
"mradermacher/bruphin-lambda-GGUF",
"mradermacher/Qwen-2.5-7B-R1-Stock-GGUF",
"mradermacher/Llama-3.1-8B-sft-SPIN-gpt4o-rm-GGUF",
"mradermacher/LunarPass-1-GGUF",
"mradermacher/Llama-3.1-8B-sft-SPIN-Llama-3.1-70B-Instruct-rm-GGUF",
"mradermacher/game-play-point25-50-GGUF",
"mradermacher/woollie-7b-GGUF",
"mradermacher/Bespoke-Stratos-17k-v2-GGUF",
"mradermacher/DeepSeek-R1-Distill-Llama-UK-Legislation-8B-GGUF",
"mradermacher/Taiwan-tinyllama-v1.1-base-GGUF",
"mradermacher/M7Yamshadowexperiment28_YamExperiment26-GGUF",
"mradermacher/latin_english_translation_model-GGUF",
"mradermacher/K2S3-Mistral-7b-v1.3-GGUF",
"mradermacher/zephyr-7b-beta-ExPO-GGUF",
"mradermacher/mine-3B-GGUF",
"mradermacher/Ice0.64-24.01-RP-GGUF",
"mradermacher/Mistral-7B-v0.1-sft-SPIN-Mistral-8x7B-Instruct-v0.1-rm-GGUF",
"mradermacher/Mistral-7B-v0.3-sft-SPIN-gpt4o-rm-GGUF",
"mradermacher/Oolel-Small-v0.1-GGUF",
"mradermacher/Deepseek-qwen-modelstock-7B-GGUF",
"mradermacher/ZEUS-8B-V25-GGUF",
"mradermacher/MathSageFR-DeepSeek-R1-Distill-Qwen-1.5B-GGUF",
"mradermacher/LunarPass-2-GGUF",
"mradermacher/SJT-2.4B-Alpha-GGUF",
"mradermacher/Ice0.62.1-24.01-RP-GGUF",
"mradermacher/Trendyol-Turkcell-7b-mixture-GGUF",
"mradermacher/DeepSeek-R1-Distill-Qwen-MFANN-Slerp-7b-GGUF",
"mradermacher/Artifact_1-GGUF",
"mradermacher/gemma-2-9b-HangulFixer-GGUF",
"mradermacher/mistral-7b-v0.3-instruct-norobots-GGUF",
"mradermacher/Mistral7B-ASQA-GGUF",
"mradermacher/EEVE-Ko-8B-Instruct-hr_250124_ver1-GGUF",
"mradermacher/salamandra-2B-instruct-ultrachat-GGUF",
"mradermacher/salamandra-2B-instruct-smoltalk-GGUF",
"mradermacher/SJTpass-1-GGUF",
"mradermacher/QwenPass-4-GGUF",
"mradermacher/Llama-3.2-3B-Instruct-MedicalQA-GGUF",
"mradermacher/SJTPass-2-GGUF",
"mradermacher/Qwen2.5-7B-R1-Bespoke-Stock-GGUF",
"mradermacher/QwenTies-3-GGUF",
"mradermacher/Ice0.64.1-24.01-RP-GGUF",
"mradermacher/Qwen2.5-7B-R1-Bespoke-Task-GGUF",
"mradermacher/albert-spp-8b-GGUF",
"mradermacher/QwenLinear-1-GGUF",
"mradermacher/Llama-3.2-3B-Math-Oct-GGUF",
]
# 去重(保持原有顺序)
_seen = set()
_deduplicated = []
for _mid in ALL_MODEL_IDS:
if _mid not in _seen:
_deduplicated.append(_mid)
_seen.add(_mid)
ALL_MODEL_IDS = _deduplicated
print(f"[INFO] 去重后模型数量: {len(ALL_MODEL_IDS)}", flush=True)
HEADERS = {"Content-Type": "application/json"}
# ══════════════════════════════════════════════════════════
# 全局状态(供 /status 展示)
# ══════════════════════════════════════════════════════════
_state = {
"strategy_id": STRATEGY_ID,
"phase": "starting", # starting | submitting | done | error
"phase": "starting", # starting | running | done | error
"total": len(ALL_MODEL_IDS),
"submitted": 0,
"failed": 0,
"downloading": [], # 当前正在下载的模型
"download_success": 0,
"download_failed": 0,
"submitted": 0, # 成功提交的验证任务数hygon + bi150
"submit_failed": 0,
"started_at": None,
"finished_at": None,
}
@@ -114,103 +338,336 @@ def _run_http():
print("[http] 已关闭", flush=True)
# ══════════════════════════════════════════════════════════
# 业务逻辑
# 工具函数:生成模型文件名
# ══════════════════════════════════════════════════════════
def _submit_task(token: str, model_id: str) -> Tuple[bool, str]:
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {token}",
def get_model_filename(model_id: str) -> str:
"""
从 model_id 生成标准 GGUF 模型文件名。
规则:
- 移除组织名(/ 前部分)
- 处理 '_-_' 分割(保留原有逻辑)
- 移除末尾 '-GGUF'(不区分大小写)
- 若移除后以 -i1, -i2, ..., -i99 结尾:
→ 替换为 .i1, .i2, ... 并添加 '-Q4_0.gguf'
否则:
→ 直接添加 '.f16.gguf'
示例:
'mradermacher/Qwen3-8B-makisu-v2.0.1-i1-GGUF'
'Qwen3-8B-makisu-v2.0.1.i1-Q4_0.gguf'
'QuantFactory/Apollo2-9B-GGUF'
'Apollo2-9B.f16.gguf'
"""
# 1. 提取模型名部分(/ 后)
base_name = model_id.split("/")[-1]
# 2. 处理 '_-_' 分割
if '_-_' in base_name:
base_name = base_name.split('_-_')[-1]
# 3. 移除末尾的 -GGUF不区分大小写
if base_name.lower().endswith("-gguf"):
base_name = base_name[:-5]
# 4. 检查是否以 -i<数字> 结尾(支持 i1i99 等)
match = re.search(r'-i(\d+)$', base_name)
if match:
number = match.group(1)
base_name = base_name[:match.start()] + f".i{number}-Q4_0.gguf"
else:
base_name = base_name + ".f16.gguf"
return base_name
# ══════════════════════════════════════════════════════════
# 登录获取 token失败时回退到预设 AUTH_TOKEN
# ══════════════════════════════════════════════════════════
def login() -> str:
payload = {"userAccount": USER_ACCOUNT, "userPassword": USER_PASSWORD}
print("[login] 正在登录...", flush=True)
try:
resp = requests.post(BASE_URL + LOGIN_ENDPOINT, headers=HEADERS, json=payload, timeout=15)
data = resp.json()
if resp.status_code == 200 and data.get("code") == 0:
print("[login] 登录成功", flush=True)
return data["data"]["token"]
print(f"[login] 登录失败: {data.get('message')},回退使用预设 Token", flush=True)
except Exception as e:
print(f"[login] 登录异常: {e},回退使用预设 Token", flush=True)
return AUTH_TOKEN
# ══════════════════════════════════════════════════════════
# 创建单个模型的下载任务
# ══════════════════════════════════════════════════════════
def create_download_task(token: str, model_id: str) -> bool:
filename = get_model_filename(model_id)
auth_headers = {**HEADERS, "Authorization": f"Bearer {token}"}
payload = {
"allowPatterns": [filename],
"hfToken": HF_TOKEN,
"modelId": model_id,
"source": "HUGGING_FACE",
"stillDownloadAlreadySuccessDownloadedModel": False
}
config_content = 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
framework: vllm
print(f"📥 创建下载任务: {model_id}{filename}", flush=True)
try:
resp = requests.post(BASE_URL + CREATE_DOWNLOAD_TASK_ENDPOINT, headers=auth_headers, json=payload, timeout=15)
if resp.status_code == 200:
data = resp.json()
if data.get("code") == 0:
print(f"✅ 下载任务已提交: {model_id}", flush=True)
return True
else:
print(f"⚠️ 下载任务业务失败 ({model_id}): {data.get('message')}", flush=True)
return False
else:
print(f"❌ HTTP 错误 ({model_id}): {resp.status_code} - {resp.text}", flush=True)
return False
except Exception as e:
print(f"💥 创建下载任务异常 ({model_id}): {e}", flush=True)
return False
# ══════════════════════════════════════════════════════════
# 查询单个模型的最新下载任务状态
# ══════════════════════════════════════════════════════════
def check_model_status(token: str, model_id: str) -> str:
"""
返回状态: 'WAITING', 'RUNNING', 'SUCCESS', 'FAILED', 'UNKNOWN'
"""
url = BASE_URL + CREATE_DOWNLOAD_TASK_ENDPOINT
auth_headers = {**HEADERS, "Authorization": f"Bearer {token}"}
params = {"modelId": model_id, "current": 1, "pageSize": 1}
try:
resp = requests.get(url, headers=auth_headers, params=params, timeout=10)
if resp.status_code != 200:
return "UNKNOWN"
data = resp.json()
if data.get("code") != 0:
return "UNKNOWN"
records = data.get("data", {}).get("records", [])
if not records:
return "UNKNOWN"
status = records[0].get("status", "UNKNOWN").upper()
return status
except Exception as e:
print(f"⚠️ 查询状态异常 ({model_id}): {e}", flush=True)
return "UNKNOWN"
# ══════════════════════════════════════════════════════════
# 提交单个模型的测试任务 hygon
# ══════════════════════════════════════════════════════════
def submit_test_task(token: str, model_id: str) -> bool:
auth_headers = {**HEADERS, "Authorization": f"Bearer {token}"}
model_filename = get_model_filename(model_id)
gpu_type = "hygon_k100-ai"
config_content = f"""docker_image: git.modelhub.org.cn:9443/enginex-hygon/hygon-llama.cpp:b7516
nv_docker_image: harbor-contest.4pd.io/luxinlong02/llama-cpp:b7003-cuda-full-12.3
framework: llamacpp
storage: gpfs
modelhub_options:
srcRelativePath: leaderboard/modelHubXC/{model_id}
mountPoint: /model
api: completion
temperature: 0
repetition_penalty: 1.1
top_p: 0.9
max_model_len: 4096
sut_config:
values:
gpu_num: 1
env:
- name: MAX_MODEL_LEN
value: 8192
command: ["vllm", "serve", "/model", "--port", "8000", "--served-model-name", "llm", "--max-model-len", "8192", "--trust-remote-code", "--dtype", "float16"]
values:
command: ['/app/llama-server','--model', '/model/{model_filename}', '--alias', 'llm', '--threads', '20','--n-gpu-layers', '999', '--prio', '3', '--min_p', '0.01', '--ctx-size', '4096', '--host', '0.0.0.0', '--port', '8000', '--jinja', '--flash-attn', 'off']
ref_config:
values:
cpu_num: 2
gpu_num: 1
env:
- name: MAX_MODEL_LEN
value: 8192
command: ["vllm", "serve", "/model", "--port", "80", "--served-model-name", "llm", "--max-model-len", "8192", "--trust-remote-code", "--dtype", "float16"]
values:
command: ['/workspace/llama.cpp/build/bin/llama-server','--model', '/model/{model_filename}', '--alias', 'llm', '--threads', '20', '--n-gpu-layers', '999', '--prio', '3', '--min_p', '0.01', '--ctx-size', '4096', '--host', '0.0.0.0', '--port', '8000', '--jinja', '--flash-attn', 'off']
"""
payload = {
task_data = {
"contestApiToken": CONTEST_API_TOKEN,
"contributors": CONTRIBUTORS,
"gpuTypes": [GPU_TYPE],
"gpuTypes": [gpu_type],
"taskType": TASK_TYPE,
"modelId": model_id,
"framework": "vllm",
"strategyId": STRATEGY_ID, # 平台要求
"submissionConfig": [{
"config": config_content,
"gpuType": GPU_TYPE,
"taskType": TASK_TYPE,
}],
"gpuType": gpu_type,
"taskType": TASK_TYPE
}]
}
print(f"📤 提交测试任务 (hygon): {model_id}", flush=True)
try:
resp = requests.post(
BASE_URL + SUBMIT_ENDPOINT,
headers=headers,
json=payload,
timeout=15,
)
resp = requests.post(BASE_URL + SUBMIT_TEST_TASK_ENDPOINT, json=task_data, headers=auth_headers, timeout=15)
if resp.status_code == 200:
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
task_id = result.get("data", {}).get("taskId")
print(f"✅ 测试任务提交成功! Task ID: {task_id}", flush=True)
return True
else:
print(f"[worker] FAIL {model_id}: {result.get('message')}", flush=True)
return False, ""
print(f"❌ 测试任务业务错误: {result.get('message')}", flush=True)
return False
else:
print(f"❌ 测试任务 HTTP 错误: {resp.status_code} - {resp.text}", flush=True)
return False
except Exception as e:
print(f"[worker] ERROR {model_id}: {e}", flush=True)
return False, ""
print(f"💥 提交测试任务异常 ({model_id}): {e}", flush=True)
return False
# ══════════════════════════════════════════════════════════
# 提交单个模型的测试任务 bi150
# ══════════════════════════════════════════════════════════
def submit_test_task_bi150(token: str, model_id: str) -> bool:
auth_headers = {**HEADERS, "Authorization": f"Bearer {token}"}
model_filename = get_model_filename(model_id)
gpu_type = "Iluvatar_bi-150"
config_content = f"""docker_image: git.modelhub.org.cn:9443/enginex-iluvatar/iluvatar-llama.cpp:b7516-bi150
nv_docker_image: harbor-contest.4pd.io/luxinlong02/llama-cpp:b7003-cuda-full-12.3
framework: llamacpp
storage: gpfs
modelhub_options:
srcRelativePath: leaderboard/modelHubXC/{model_id}
mountPoint: /model
max_model_len: 4096
sut_config:
gpu_num: 1
values:
command: ['/app/llama-server','--model', '/model/{model_filename}', '--alias', 'llm', '--threads', '20','--n-gpu-layers','128', '--ctx-size', '4096', '--host', '0.0.0.0', '--port', '8000', '--jinja', '--flash-attn', 'off', '--no-mmap', '--sync-to-temp']
ref_config:
gpu_num: 1
values:
command: ['/workspace/llama.cpp/build/bin/llama-server','--model', '/model/{model_filename}', '--alias', 'llm', '--threads', '20','--n-gpu-layers','128', '--ctx-size', '4096', '--host', '0.0.0.0', '--port', '8000', '--jinja', '--flash-attn', 'off']
"""
task_data = {
"contestApiToken": CONTEST_API_TOKEN,
"contributors": CONTRIBUTORS,
"gpuTypes": [gpu_type],
"taskType": TASK_TYPE,
"modelId": model_id,
"strategyId": STRATEGY_ID, # 平台要求
"submissionConfig": [{
"config": config_content,
"gpuType": gpu_type,
"taskType": TASK_TYPE
}]
}
print(f"📤 提交测试任务 (bi150): {model_id}", flush=True)
try:
resp = requests.post(BASE_URL + SUBMIT_TEST_TASK_ENDPOINT, json=task_data, headers=auth_headers, timeout=15)
if resp.status_code == 200:
result = resp.json()
if result.get("code") == 0:
task_id = result.get("data", {}).get("taskId")
print(f"✅ 测试任务提交成功! Task ID: {task_id}", flush=True)
return True
else:
print(f"❌ 测试任务业务错误: {result.get('message')}", flush=True)
return False
else:
print(f"❌ 测试任务 HTTP 错误: {resp.status_code} - {resp.text}", 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"
_state["phase"] = "running"
successful: List[Tuple[str, str]] = []
token = AUTH_TOKEN
print("[worker] 使用预设 Token跳过登录", flush=True)
try:
token = login()
except Exception as e:
print(f"[worker] 登录失败: {e}", flush=True)
_state["phase"] = "error"
return
for model_id in ALL_MODEL_IDS:
pending_models = list(ALL_MODEL_IDS) # 尚未开始下载的模型
active_models: Set[str] = set() # 当前正在下载的模型
completed_results = {} # model_id -> status
print(f"🚀 总共 {len(pending_models)} 个模型待下载。最大并发数: {MAX_CONCURRENT_DOWNLOADS}\n", flush=True)
while (pending_models or active_models) and not _shutdown.is_set():
# 1. 检查活跃任务状态
for model_id in list(active_models):
if _shutdown.is_set():
break
ok, task_id = _submit_task(token, model_id)
if ok:
status = check_model_status(token, model_id)
if status in ("SUCCESS", "FAILED"):
completed_results[model_id] = status
active_models.remove(model_id)
print(f"⏹️ {model_id} 完成,状态: {status}", flush=True)
if status == "SUCCESS":
_state["download_success"] += 1
# 下载成功后立即提交该模型的验证任务
if submit_test_task(token, model_id):
_state["submitted"] += 1
successful.append((task_id, model_id))
print(f"🧪 已为 {model_id} 提交 hygon 验证任务", flush=True)
else:
_state["failed"] += 1
_state["submit_failed"] += 1
print(f"⚠️ {model_id} hygon 验证任务提交失败", flush=True)
# 写入结果文件
if submit_test_task_bi150(token, model_id):
_state["submitted"] += 1
print(f"🧪 已为 {model_id} 提交 bi150 验证任务", flush=True)
else:
_state["submit_failed"] += 1
print(f"⚠️ {model_id} bi150 验证任务提交失败", flush=True)
else:
_state["download_failed"] += 1
# 2. 补充新任务(最多补到 MAX_CONCURRENT_DOWNLOADS 个)
while len(active_models) < MAX_CONCURRENT_DOWNLOADS and pending_models and not _shutdown.is_set():
next_model = pending_models.pop(0)
if create_download_task(token, next_model):
active_models.add(next_model)
print(f"▶️ 启动下载: {next_model} (当前活跃: {len(active_models)})", flush=True)
else:
# 创建失败也视为完成(避免卡住)
completed_results[next_model] = "CREATE_FAILED"
_state["download_failed"] += 1
print(f"❌ 创建失败: {next_model}", flush=True)
_state["downloading"] = sorted(active_models)
# 3. 稍作等待,避免频繁查询(可被 shutdown 信号打断)
if active_models or pending_models:
_shutdown.wait(CHECK_INTERVAL_SECONDS)
print("\n✅ 所有模型处理完毕!\n", flush=True)
# 4. 收集所有成功下载的模型ID并写入结果文件
success_models = [
mid for mid, status in completed_results.items()
if status == "SUCCESS"
]
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")
with open("downloaded_success_models.txt", "w", encoding="utf-8") as f:
for mid in success_models:
f.write(f"{mid}\n")
except Exception:
pass
print("🎉 下载成功的模型ID列表", flush=True)
print("[", flush=True)
for mid in success_models:
print(f' "{mid}",', flush=True)
print("]", flush=True)
_state["downloading"] = []
_state["finished_at"] = datetime.utcnow().isoformat()
_state["phase"] = "done"
print(
f"[worker] 完成 submitted={_state['submitted']} failed={_state['failed']}",
f"[worker] 完成 download_success={_state['download_success']} "
f"download_failed={_state['download_failed']} "
f"submitted={_state['submitted']} submit_failed={_state['submit_failed']}",
flush=True,
)
# 提交完成后继续保持进程存活,等待平台停止
# 流水线完成后继续保持进程存活,等待平台停止
# ══════════════════════════════════════════════════════════
# 入口
@@ -228,7 +685,7 @@ def main():
http_thread = threading.Thread(target=_run_http, daemon=False)
http_thread.start()
# 提交任务线程
# 流水线线程
worker_thread = threading.Thread(target=_run_worker, daemon=True)
worker_thread.start()