2 Commits

Author SHA1 Message Date
zhouyuanxi
2f1f2d5a17 Stop waiting tasks for three accounts 2026-07-26 01:35:23 +08:00
zhouyuanxi
5c9c80fd72 Stop l112233 waiting validation tasks 2026-07-26 00:02:46 +08:00
2 changed files with 110 additions and 75 deletions

View File

@@ -1,8 +1,14 @@
# xc_validation_strategy_vllm_stop
用于停止 `zhoukaile` 账号当前状态`waiting`验证任务的 ModelHub XC 策略服务。
用于停止以下账号中状态严格`waiting` 的 ModelHub XC 验证任务:
服务启动后会调用:
- `fanyi`100 条
- `jiajing`88 条
- `i-zhouyuanxi@4paradigm.com`94 条
任务清单于 2026-07-26 通过 `/api/adapt/task/page` 分页查询生成,共 282 条。`success``failed` 等其他状态不会写入清单,也不会被停止。
服务启动后按账号及每批 50 条调用:
```text
PUT /api/async/task/stop-create-contest-task
@@ -11,15 +17,10 @@ PUT /api/async/task/stop-create-contest-task
停止请求完成后,进程继续运行,以便平台通过健康检查和状态接口读取执行结果。
## 任务清单
策略内置了 2026-07-25 通过 `zhoukaile` 账号查询得到的 100 个 `waiting` 任务 ID。总历史任务数为 838但已完成与失败任务不会写入该清单也不会被停止。
可选配置如下:
## 可选配置
| 变量 | 默认值 | 说明 |
| --- | --- | --- |
| `TASK_IDS` | 内置的 100 个 waiting 任务 ID | 可选的逗号分隔任务 ID设置后完全替换内置清单。 |
| `BASE_URL` | `https://modelhub.org.cn` | ModelHub 服务地址。 |
| `BATCH_SIZE` | `50` | 每个停止请求包含的任务数。 |
| `MAX_RETRIES` | `3` | 单批任务的最大请求次数。 |
@@ -29,13 +30,11 @@ PUT /api/async/task/stop-create-contest-task
## 平台运行接口
- `GET /health`:存活探针,成功返回 `{"status":"ok"}`
- `GET /status`:返回执行阶段、成功/失败数量、失败任务 ID 错误信息。
- `GET /status`:返回总体及每个账号的成功/失败数量、失败任务 ID 错误信息。
## 运行与构建
仓库根目录的 `Dockerfile` 使用平台 Python 基础镜像,暴露 `8080` 端口并以 `python main.py` 启动。构建成功后,策略会将内置任务 ID 按批调用停止接口
本地验证:
仓库根目录的 `Dockerfile` 使用平台 Python 基础镜像,暴露 `8080` 端口并以 `python main.py` 启动。构建成功后,策略会依次处理三个账号的内置任务清单
```bash
python main.py

162
main.py
View File

@@ -1,4 +1,4 @@
"""Stop zhoukaile's waiting ModelHub XC validation tasks.
"""Stop waiting ModelHub XC validation tasks for the configured accounts.
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``.
@@ -17,8 +17,6 @@ import requests
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 = "bd7c52f3b9604ef48a14dd6174513935"
STRATEGY_ID = os.environ.get("STRATEGY_ID", "")
HTTP_HOST = "0.0.0.0"
@@ -27,33 +25,85 @@ 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"))
# Snapshot queried from /api/adapt/task/page on 2026-07-25. These are the
# 100 records whose status was exactly "waiting"; completed/failed tasks are
# deliberately excluded.
DEFAULT_TASK_IDS = [
3467793, 3467787, 3467785, 3467784, 3467782, 3467781, 3467779,
3467777, 3467776, 3467775, 3467773, 3467772, 3467771, 3467768,
3467761, 3467759, 3467757, 3467754, 3467753, 3467752, 3467751,
3467750, 3467749, 3467748, 3467747, 3467746, 3467745, 3467744,
3467743, 3467742, 3467741, 3467740, 3467739, 3467738, 3467736,
3467735, 3467734, 3467733, 3467732, 3467731, 3467730, 3467729,
3467728, 3467727, 3467726, 3467725, 3467724, 3467723, 3467721,
3467711, 3467705, 3467704, 3467703, 3467700, 3467699, 3467698,
3467697, 3467696, 3467695, 3467694, 3467691, 3467683, 3467679,
3467674, 3467673, 3467672, 3467671, 3467670, 3467669, 3467666,
3467663, 3467662, 3467660, 3467659, 3467658, 3467657, 3467656,
3467654, 3467653, 3467651, 3467650, 3467649, 3467648, 3467647,
3467646, 3467645, 3467644, 3467642, 3467641, 3467639, 3467638,
3467637, 3467636, 3467635, 3467634, 3467633, 3467632, 3467631,
3467630, 3467629,
# Snapshot queried from /api/adapt/task/page on 2026-07-26. Only records
# whose status was exactly "waiting" are included.
TARGETS = [
{
"account": "fanyi",
"xc_token": "f2d501c9ae6543a589cd6cb789108c41",
"task_ids": [
3469928, 3469927, 3469926, 3469925, 3469924, 3469923, 3469922,
3469921, 3469920, 3469919, 3469918, 3469917, 3469916, 3469915,
3469914, 3469913, 3469911, 3469910, 3469909, 3469908, 3469907,
3469905, 3469904, 3469903, 3469902, 3469901, 3469900, 3469899,
3469898, 3469897, 3469896, 3469895, 3469894, 3469893, 3469892,
3469891, 3469890, 3469889, 3469888, 3469887, 3469886, 3469885,
3469884, 3469883, 3469882, 3469881, 3469879, 3469878, 3469877,
3469875, 3469874, 3469873, 3469872, 3469871, 3469870, 3469869,
3469868, 3469867, 3469866, 3469865, 3469864, 3469863, 3469862,
3469861, 3469860, 3469859, 3469858, 3469857, 3469856, 3469855,
3469854, 3469853, 3469852, 3469851, 3469850, 3469849, 3469848,
3469847, 3469846, 3469845, 3469844, 3469843, 3469842, 3469841,
3469840, 3469837, 3469836, 3469835, 3469834, 3469833, 3469832,
3469831, 3469830, 3469829, 3469828, 3469827, 3469826, 3469825,
3469822, 3469821,
],
},
{
"account": "jiajing",
"xc_token": "5e051e0ff8384a81af53bea780deb28a",
"task_ids": [
3469551, 3469550, 3469549, 3469548, 3469547, 3469546, 3469545,
3469544, 3469543, 3469542, 3469541, 3469540, 3469539, 3469538,
3469537, 3469536, 3469535, 3469534, 3469533, 3469532, 3469531,
3469530, 3469529, 3469528, 3469526, 3469525, 3469524, 3469523,
3469522, 3469521, 3469520, 3469519, 3469518, 3469517, 3469516,
3469515, 3469514, 3469513, 3469512, 3469511, 3469510, 3469508,
3469507, 3469503, 3469501, 3469500, 3469499, 3469497, 3469495,
3469493, 3469492, 3469491, 3469490, 3469488, 3469486, 3469485,
3469484, 3469483, 3469482, 3469480, 3469477, 3469476, 3469475,
3469474, 3469472, 3469471, 3469470, 3469469, 3469468, 3469467,
3469466, 3469465, 3469464, 3469463, 3469462, 3469461, 3469460,
3469459, 3469458, 3469457, 3469456, 3469455, 3469454, 3469453,
3469452, 3469451, 3469450, 3469449,
],
},
{
"account": "i-zhouyuanxi@4paradigm.com",
"xc_token": "62b9b487eff2488fb9f1da0b963f0b93",
"task_ids": [
3450582, 3450581, 3450580, 3450579, 3450575, 3450573, 3450572,
3450570, 3450569, 3450568, 3450567, 3450565, 3450560, 3450557,
3450556, 3450555, 3450554, 3450553, 3450552, 3450551, 3450550,
3450549, 3450548, 3450544, 3450541, 3450538, 3450537, 3450536,
3450535, 3450534, 3450533, 3450532, 3450531, 3450530, 3450529,
3450528, 3450527, 3450526, 3450525, 3450524, 3450523, 3450522,
3450521, 3450520, 3450519, 3450518, 3450517, 3450514, 3450513,
3450512, 3450511, 3450510, 3450509, 3450508, 3450507, 3450506,
3450505, 3450503, 3450501, 3450498, 3450496, 3450493, 3450483,
3450481, 3450480, 3450479, 3450478, 3450477, 3450476, 3450475,
3450474, 3450473, 3450472, 3450471, 3450470, 3450469, 3450463,
3450458, 3450455, 3450451, 3450441, 3450440, 3450439, 3450438,
3450437, 3450436, 3450435, 3450434, 3450433, 3450432, 3450431,
3450430, 3450429, 3450428,
],
},
]
_shutdown = threading.Event()
_state: dict[str, Any] = {
"strategy_id": STRATEGY_ID,
"account": USER_ACCOUNT,
"accounts": {
target["account"]: {
"total": len(target["task_ids"]),
"stopped": 0,
"failed": 0,
"failed_task_ids": [],
}
for target in TARGETS
},
"phase": "starting", # starting | stopping | done | partial_failure | error
"total": 0,
"total": sum(len(target["task_ids"]) for target in TARGETS),
"stopped": 0,
"failed": 0,
"failed_task_ids": [],
@@ -67,24 +117,6 @@ def _now() -> str:
return datetime.now(timezone.utc).isoformat()
def _task_ids_from_environment() -> list[int] | None:
"""Return an explicit TASK_IDS override, if one was supplied."""
raw_task_ids = os.environ.get("TASK_IDS", "").strip()
if not raw_task_ids:
return None
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))
class Handler(BaseHTTPRequestHandler):
def do_GET(self) -> None: # noqa: N802 - BaseHTTPRequestHandler API
if self.path == "/health":
@@ -115,10 +147,10 @@ def _run_http() -> None:
server.server_close()
def _stop_batch(task_ids: list[int]) -> bool:
def _stop_batch(account: str, xc_token: str, task_ids: list[int]) -> bool:
headers = {
"Content-Type": "application/json",
"xc-Token": XC_TOKEN,
"xc-Token": xc_token,
}
url = f"{BASE_URL}{STOP_TASK_ENDPOINT}"
for attempt in range(1, MAX_RETRIES + 1):
@@ -135,15 +167,19 @@ def _stop_batch(task_ids: list[int]) -> bool:
result = {"message": response.text[:500]}
if response.ok and result.get("code") == 0:
print(f"[stop] stopped task IDs: {task_ids}", flush=True)
print(f"[stop] {account}: stopped task IDs: {task_ids}", flush=True)
return True
print(
f"[stop] attempt {attempt}/{MAX_RETRIES} failed for {task_ids}: "
f"[stop] {account}: 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)
print(
f"[stop] {account}: attempt {attempt}/{MAX_RETRIES} "
f"request error for {task_ids}: {exc}",
flush=True,
)
if attempt < MAX_RETRIES and not _shutdown.wait(attempt):
continue
@@ -156,22 +192,22 @@ def _run_worker() -> None:
_state["started_at"] = _now()
_state["phase"] = "stopping"
try:
task_ids = _task_ids_from_environment() or DEFAULT_TASK_IDS
if not task_ids:
print("[query] no active validation tasks found", flush=True)
_state["phase"] = "done"
return
_state["total"] = len(task_ids)
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)
for target in TARGETS:
account = target["account"]
account_state = _state["accounts"][account]
task_ids = target["task_ids"]
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(account, target["xc_token"], batch):
_state["stopped"] += len(batch)
account_state["stopped"] += len(batch)
else:
_state["failed"] += len(batch)
_state["failed_task_ids"].extend(batch)
account_state["failed"] += len(batch)
account_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