1 Commits

Author SHA1 Message Date
1155ac4f4e reserve validation slots before downloading, to stop competitors front-running our downloads
The download queue (/adminApi/async/task/model-download-task) is readable by ANY
account, including low-privilege xc-Token ones, so our WAITING model ids leak.
Competitors submit those via mechanism B (which needs no download), their task
waits for the file, and once our zhoushasha download lands it runs and wins the
slot - leaving us with 60028 on our own download.

New flow:
  phase 1 (reserve, instant): bi150 via zero-credit accounts that have no bi150
    task yet, hygon via zhoukaile - both mechanism B, no download required
  phase 2 (download): zhoushasha downloads only the models we reserved

Startup now filters out accounts that already hold a bi150 task, which also makes
the rotation cursor idempotent across redeploys.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 14:33:04 +08:00

238
main.py
View File

@@ -80,34 +80,100 @@ BI150_ACCOUNTS: List[Tuple[str, str]] = [
("yg789", "c7abd6da77cb40ca82ee89b74d54b2ee"),
]
# 已经用掉的 bi150 账号数量(游标)。
# 待占位的 bi150 账号队列(启动时由 _init_bi150_queue 过滤后填充)。
# 只保留「还没有任何 bi150 任务」的零积分账号——每个账号占到 1 个就够了。
# 注意:游标只在「提交成功」后才推进(见 _commit_bi150_account
# 因为模型可能已被别人抢先注册60028或提交失败若失败也消耗名额
# 稀缺的账号额度会被无效尝试白白吃掉;失败时保留名额给下一个模型继续用。
_bi150_pending: List[Tuple[str, str]] = [] # 启动时填充,用剩下的排在前面
_bi150_used = 0
_bi150_lock = threading.Lock()
TASK_PAGE_ENDPOINT = "/api/adapt/task/page"
# 账号额度打满时平台返回的提示片段(错误码 60007
QUOTA_FULL_MSG = "当前等待中或运行中的异步模型验证任务数量已达上限"
_hygon_quota_full = False # zhoukaile 额度是否已打满
GPU_TYPE_HYGON = "hygon_k100-ai"
GPU_TYPE_BI150 = "Iluvatar_bi-150"
def _account_has_bi150(xc_token: str) -> bool:
"""查这个账号名下是否已经有 Iluvatar_bi-150 的任务(任何状态都算)。"""
try:
page = 1
while page <= 30:
resp = requests.get(
BASE_URL + TASK_PAGE_ENDPOINT,
params={"pageNum": page, "pageSize": 100},
headers={"xc-Token": xc_token},
timeout=25,
)
data = (resp.json() or {}).get("data") or {}
records = data.get("records") or []
for rec in records:
if rec.get("gpuType") == GPU_TYPE_BI150:
return True
try:
pages = int(data.get("pages") or 1)
except Exception:
pages = 1
if page >= pages or not records:
break
page += 1
except Exception as e:
# 查不到就保守当作「已占过」,避免把额度浪费在状态不明的账号上
print(f"⚠️ 查询账号 bi150 任务失败,跳过该账号: {e}", flush=True)
return True
return False
def _init_bi150_queue() -> None:
"""
启动时筛掉已经有 bi150 任务的账号,只把「还没占过位」的排进队列。
这样重新部署也不会重复消耗已经成功的账号(游标本身是进程内内存变量)。
"""
global _bi150_pending
usable, skipped = [], []
for acct, tok in BI150_ACCOUNTS:
if _account_has_bi150(tok):
skipped.append(acct)
else:
usable.append((acct, tok))
_bi150_pending = usable
_state["bi150_usable"] = len(usable)
print(f"[init] bi150 账号筛查:可用 {len(usable)} 个,已有任务跳过 {len(skipped)}", flush=True)
if skipped:
print(f"[init] 跳过的账号: {', '.join(skipped)}", flush=True)
if usable:
print(f"[init] 待占位账号: {', '.join(a for a, _ in usable)}", flush=True)
def _peek_bi150_account() -> Tuple[str, str, bool]:
"""
查看当前该用哪个 bi150 账号,但**不推进**游标。
返回 (账号, xc-Token, 是否取自 BI150_ACCOUNTS 列表)
用完后一律返回 HYGON_SUBMIT_ACCOUNT 且第三项为 False
返回 (账号, xc-Token, 是否取自待占位队列)
列用完后返回 (None, None, False),表示 bi150 占位已全部完成、无需再提交
"""
with _bi150_lock:
if _bi150_used < len(BI150_ACCOUNTS):
acct, tok = BI150_ACCOUNTS[_bi150_used]
if _bi150_used < len(_bi150_pending):
acct, tok = _bi150_pending[_bi150_used]
return acct, tok, True
return HYGON_SUBMIT_ACCOUNT, HYGON_XC_TOKEN, False
return "", "", False
def _commit_bi150_account() -> None:
"""提交成功后调用,推进游标,让这个账号的名额真正被用掉。"""
global _bi150_used
with _bi150_lock:
if _bi150_used < len(BI150_ACCOUNTS):
if _bi150_used < len(_bi150_pending):
_bi150_used += 1
def _bi150_done() -> bool:
"""所有待占位账号是否都已占到位。"""
with _bi150_lock:
return _bi150_used >= len(_bi150_pending)
CONTEST_API_TOKEN = "ef1ef82f3c9efee413d602345fbe224d"
HF_TOKEN = "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"
CONTRIBUTORS = "zhoushasha"
@@ -730,32 +796,34 @@ ALL_MODEL_IDS = [
# "mradermacher/Experiment28M7_NeuralsirkrishnaExperiment26-GGUF",
# "mradermacher/Humanised-LLMv2-GGUF",
# "mradermacher/Experiment26Neuralsirkrishna_YamPastiche-GGUF",
"mradermacher/EXALT-Baseline-GGUF",
"mradermacher/NeuralsirkrishnaShadow_Ognoexperiment27M7-GGUF",
"mradermacher/Meta-Llama-3-8B-Instruct_fictional_Korean_v1-GGUF",
"mradermacher/flammen20-mistral-7B-GGUF",
"mradermacher/sec-mistral-7b-instruct-v2-GGUF",
"mradermacher/phillama-3.8b-v0.1-GGUF",
"mradermacher/YamshadowStrangemerges_32_YamStrangemerges_32-GGUF",
"mradermacher/T3qm7xNeuralsirkrishna_Experiment31Experiment29-GGUF",
"mradermacher/Llama-2-7b-chat-hf_fictional_v2-GGUF",
"mradermacher/SmartLlama-3-8B-MS-v0.1-GGUF",
"mradermacher/NeuralsynthesisT3qm7-7B-GGUF",
"mradermacher/ClickbaitFighter-2B-GGUF",
"mradermacher/Alphacode-MALI-9B-GGUF",
"mradermacher/MeliodasPercival_01_Experiment26Experiment28-GGUF",
"mradermacher/Wukong-Mistral-7B-v0.3-GGUF",
"mradermacher/YamshadowStrangemerges_32_Inex16Calme-GGUF",
"mradermacher/YamT3qm7xp_ShadowOgno-GGUF",
"mradermacher/Ludwig_Orca_Orient-GGUF",
"mradermacher/CosMoE-Lisa-4x1b-GGUF",
"mradermacher/Experiment26T3qm7xp_Experiment26Strangemerges_30-GGUF",
"mradermacher/OpenThaiGPT-100-instruct-finetuned-GGUF",
"mradermacher/M7Yamshadowexperiment28_Experiment27Experiment24-GGUF",
"mradermacher/Experiment28M7_Experiment26Yamshadow-GGUF",
"mradermacher/T3qm7xNeuralsirkrishna_Experiment27Experiment28-GGUF",
"mradermacher/YamshadowInex12_YamT3qm7x-GGUF",
"mradermacher/T3Q-Mistral-UB-DPO-v1.0-GGUF",
# "mradermacher/EXALT-Baseline-GGUF",
# "mradermacher/NeuralsirkrishnaShadow_Ognoexperiment27M7-GGUF",
# "mradermacher/Meta-Llama-3-8B-Instruct_fictional_Korean_v1-GGUF",
# "mradermacher/flammen20-mistral-7B-GGUF",
# "mradermacher/sec-mistral-7b-instruct-v2-GGUF",
# "mradermacher/phillama-3.8b-v0.1-GGUF",
# "mradermacher/YamshadowStrangemerges_32_YamStrangemerges_32-GGUF",
# "mradermacher/T3qm7xNeuralsirkrishna_Experiment31Experiment29-GGUF",
# "mradermacher/Llama-2-7b-chat-hf_fictional_v2-GGUF",
# "mradermacher/SmartLlama-3-8B-MS-v0.1-GGUF",
# "mradermacher/NeuralsynthesisT3qm7-7B-GGUF",
# "mradermacher/ClickbaitFighter-2B-GGUF",
# "mradermacher/Alphacode-MALI-9B-GGUF",
# "mradermacher/MeliodasPercival_01_Experiment26Experiment28-GGUF",
# "mradermacher/Wukong-Mistral-7B-v0.3-GGUF",
# "mradermacher/YamshadowStrangemerges_32_Inex16Calme-GGUF",
# "mradermacher/YamT3qm7xp_ShadowOgno-GGUF",
# "mradermacher/Ludwig_Orca_Orient-GGUF",
# "mradermacher/CosMoE-Lisa-4x1b-GGUF",
# "mradermacher/Experiment26T3qm7xp_Experiment26Strangemerges_30-GGUF",
# "mradermacher/OpenThaiGPT-100-instruct-finetuned-GGUF",
# "mradermacher/M7Yamshadowexperiment28_Experiment27Experiment24-GGUF",
# "mradermacher/Experiment28M7_Experiment26Yamshadow-GGUF",
# "mradermacher/T3qm7xNeuralsirkrishna_Experiment27Experiment28-GGUF",
# "mradermacher/YamshadowInex12_YamT3qm7x-GGUF",
# "mradermacher/T3Q-Mistral-UB-DPO-v1.0-GGUF",
"mradermacher/selfbiorag-7b-dpo-full-sft-wo-kqa_silver_wogold-GGUF",
"mradermacher/NeuralKukedlc-7B-Labonned-GGUF",
"mradermacher/Calme-7B-Instruct-v0.9-GGUF",
@@ -2417,13 +2485,16 @@ HEADERS = {"Content-Type": "application/json"}
# ══════════════════════════════════════════════════════════
_state = {
"strategy_id": STRATEGY_ID,
"phase": "starting", # starting | running | done | error
"phase": "starting", # starting | reserving | downloading | done | error
"total": len(ALL_MODEL_IDS),
"downloading": [], # 当前正在下载的模型
"download_success": 0,
"download_failed": 0,
"submitted": 0, # 成功提交的验证任务数hygon + bi150
"submit_failed": 0,
"bi150_usable": 0, # 启动筛查后仍可占位的零积分账号数
"reserved_bi150": 0, # 阶段一 bi150 占位成功数
"reserved_hygon": 0, # 阶段一 hygon 占位成功数
"started_at": None,
"finished_at": None,
}
@@ -2626,7 +2697,14 @@ ref_config:
print(f"✅ 测试任务提交成功! {model_id}", flush=True)
return True
else:
print(f"❌ 测试任务业务错误: {result.get('message')}", flush=True)
msg = str(result.get("message") or "")
if QUOTA_FULL_MSG in msg:
# zhoukaile 额度打满,置位后占位阶段不再尝试 hygon
global _hygon_quota_full
_hygon_quota_full = True
print(f"⛔ hygon 提交账号额度已满,后续跳过 hygon 占位: {msg}", flush=True)
else:
print(f"❌ 测试任务业务错误: {msg}", flush=True)
return False
else:
print(f"❌ 测试任务 HTTP 错误: {resp.status_code} - {resp.text}", flush=True)
@@ -2639,9 +2717,11 @@ ref_config:
# 提交单个模型的测试任务 bi150
# ══════════════════════════════════════════════════════════
def submit_test_task_bi150(model_id: str) -> bool:
# auth_headers = {**HEADERS, "Authorization": f"Bearer {token}"}
# 队列里所有零积分账号都已占到位就不再提交——每个账号占 1 个即达成目标
if _bi150_done():
return False
model_filename = get_model_filename(model_id)
gpu_type = "Iluvatar_bi-150"
gpu_type = GPU_TYPE_BI150
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
@@ -2705,11 +2785,71 @@ def _run_worker():
_state["phase"] = "error"
return
pending_models = list(ALL_MODEL_IDS) # 尚未开始下载的模型
active_models: Set[str] = set() # 当前正在下载的模型
completed_results = {} # model_id -> status
# ══════════════════════════════════════════════════════
# 阶段一占位机制B瞬间完成不受下载队列制约
#
# 背景:下载队列 /adminApi/async/task/model-download-task 的【读取】对
# 所有账号开放(连最低权限的 xc-Token 小号都能翻),所以我方 WAITING 中
# 的模型ID 会暴露给所有人。对手看到后用机制B抢先占位——机制B不要求模型
# 已下载,任务会挂着等文件;等我方 zhoushasha 把模型下载完,他的任务立刻
# 开跑并验证成功,我方反而因「一个模型一张卡只能一个账号」而撞 60028。
#
# 对策:把提交挪到下载【之前】。先占位再下载,下载队列即使被爬也没用。
# 触发的 adminProxy 代下载很慢无所谓——我方自己的下载会先完成。
# ══════════════════════════════════════════════════════
_state["phase"] = "reserving"
_init_bi150_queue()
print(f"🚀 总共 {len(pending_models)} 个模型待下载。最大并发数: {MAX_CONCURRENT_DOWNLOADS}\n", flush=True)
reserved_models = [] # 占位成功、需要下载的模型(按占位顺序)
print(f"\n🔒 阶段一占位bi150 待占位账号 {len(_bi150_pending)} 个)\n", flush=True)
for model_id in ALL_MODEL_IDS:
if _shutdown.is_set():
break
if _bi150_done() and _hygon_quota_full:
print("🔒 bi150 占位已全部完成,且 hygon 额度已满,占位阶段结束", flush=True)
break
got = False
# bi150用还没有 bi150 任务的零积分账号逐个占位
if not _bi150_done():
if submit_test_task_bi150(model_id):
_state["submitted"] += 1
_state["reserved_bi150"] += 1
got = True
else:
_state["submit_failed"] += 1
# hygon固定用 zhoukaile同样是机制B占位
if not _hygon_quota_full:
if submit_test_task(model_id):
_state["submitted"] += 1
_state["reserved_hygon"] += 1
got = True
else:
_state["submit_failed"] += 1
if got:
reserved_models.append(model_id)
print(
f"\n🔒 占位阶段完成bi150={_state['reserved_bi150']} "
f"hygon={_state['reserved_hygon']} "
f"待下载模型={len(reserved_models)}\n",
flush=True,
)
# ══════════════════════════════════════════════════════
# 阶段二下载机制Azhoushasha8 并发)
# 只下载已经占到位的模型——这些位置已经是我方的,下载完即可开跑验证
# ══════════════════════════════════════════════════════
_state["phase"] = "downloading"
pending_models = list(reserved_models) # 尚未开始下载的模型
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. 检查活跃任务状态
@@ -2723,20 +2863,8 @@ def _run_worker():
print(f"⏹️ {model_id} 完成,状态: {status}", flush=True)
if status == "SUCCESS":
_state["download_success"] += 1
# 下载成功后立即提交该模型的验证任务
if submit_test_task(model_id):
_state["submitted"] += 1
print(f"🧪 已为 {model_id} 提交 hygon 验证任务", flush=True)
else:
_state["submit_failed"] += 1
print(f"⚠️ {model_id} hygon 验证任务提交失败", flush=True)
if submit_test_task_bi150(model_id):
_state["submitted"] += 1
print(f"🧪 已为 {model_id} 提交 bi150 验证任务", flush=True)
else:
_state["submit_failed"] += 1
print(f"⚠️ {model_id} bi150 验证任务提交失败", flush=True)
# 验证任务已在阶段一占位完成,下载落地后平台会自动开跑,这里无需再提交
print(f"🎯 {model_id} 下载完成,已占位的验证任务可以开跑了", flush=True)
else:
_state["download_failed"] += 1