22 Commits

Author SHA1 Message Date
z3st
0a4ba2e146 feat: standalone cancel-all version - startup auto cancels all waiting/running tasks 2026-07-24 18:53:24 +08:00
z3st
2222e1545b feat: cancel-all mode - cancel all waiting/running tasks on startup 2026-07-24 18:51:51 +08:00
z3st
441b540e47 feat: filter out GPTQ/AWQ formats and Qwen3.5 arch (incompatible with Iluvatar) 2026-07-24 18:50:21 +08:00
z3st
afbda884ad feat: multi-page search, filter downloads 50-5000 for cold models 2026-07-24 00:10:32 +08:00
z3st
68c7a99e68 fix: replace gpu variable with TARGET_GPU in submit record 2026-07-24 00:02:35 +08:00
z3st
8e8fd50927 chore: switch target GPU to Iluvatar_bi-150 2026-07-23 23:51:28 +08:00
z3st
5a6862f8da fix: revert to ModelScope search (HF unreachable in container), fix ACCOUNTS reference 2026-07-23 23:32:33 +08:00
z3st
727f0f678f feat: single GPU, HuggingFace only, vllm only
- Target single GPU (ppu_zw_810e) instead of 4 GPUs
- Search only from HuggingFace API (removed ModelScope)
- Framework fixed to vllm (removed llama.cpp)
- Filter out GGUF format models
- Simplified config and functions
2026-07-23 23:19:20 +08:00
z3st
207d44b8f2 fix: add nv_framework back for API safety 2026-07-23 22:43:22 +08:00
z3st
1d33394dac fix: remove nv_framework, let platform auto-assign 2026-07-23 22:42:45 +08:00
z3st
1811a66aee fix: match platform auto-generated configParams format
- Remove /bin/bash -ic wrapper from sut_config.command
- Use list format for all command arguments (matches platform auto-gen)
- Add nv_framework field
- Remove unnecessary fields: docker_image, nv_docker_image, modelFormat, model, cpu_num
- Fix ref_config to match platform format (port 80, max_model_len 4096)
2026-07-23 22:35:50 +08:00
z3st
12bf6d637f fix: move gpu_num/cpu_num outside values block to match API spec 2026-07-23 22:10:17 +08:00
z3st
85c456afe0 fix: yaml.dump width=1000 to prevent command string wrapping 2026-07-22 22:43:35 +08:00
z3st
b3b52848c2 fix: verifyResult can be None, ensure dict return 2026-07-22 01:39:33 +08:00
z3st
d6e9f2e7a0 fix: page_size limit 50 for ModelScope API 2026-07-22 01:13:26 +08:00
z3st
821edbc8f5 debug: add logging to search function 2026-07-22 01:08:03 +08:00
z3st
5e2df178e1 feat: re-enable auto-run pipeline after connectivity test 2026-07-21 20:45:26 +08:00
z3st
761e74ff82 feat: auto-run connectivity test on startup, print results to logs 2026-07-21 20:31:23 +08:00
z3st
09ac80f1ec feat: add /test endpoint for connectivity check, disable auto-run 2026-07-21 19:53:28 +08:00
z3st
3975a3370a fix: correct ModelScope API endpoint and field names 2026-07-21 19:31:35 +08:00
z3st
7313f8da67 feat: auto-run pipeline on startup 2026-07-21 19:09:41 +08:00
z3st
2955a57e2e feat: full cloud pipeline - search, filter, submit 2026-07-21 19:04:43 +08:00
3 changed files with 191 additions and 46 deletions

View File

@@ -1,16 +1,33 @@
# ModelHub 提交智能体
# ModelHub 全云端提交智能体
用于获取 `strategyId` 的最小化智能体
部署在平台容器中,从内网直接提交验证任务
## 功能
## API
- `GET /health` → 健康检查
- `GET /` → 显示 strategyId
| 端点 | 方法 | 说明 |
|------|------|------|
| `/health` | GET | 健康检查 |
| `/` | GET | 智能体信息 |
| `/status` | GET | 运行状态 |
| `/logs?lines=50` | GET | 查看日志 |
| `/run` | POST | 触发一次完整流程 |
## 平台要求
## POST /run 请求体
- ✅ 根目录 Dockerfile
- ✅ 暴露 8080 端口
-`/health` 端点返回 200
- ✅ 处理 SIGTERM 信号
- ✅ 读取 `STRATEGY_ID` 环境变量
```json
{
"gpus": ["Kunlunxin_p-800", "hygon_k100-ai"],
"limit": 30
}
```
- `gpus`: 目标 GPU 列表(可选,默认全部)
- `limit`: 每 GPU 最大提交数(可选,默认 30
## 流程
1. 搜索 ModelScopeQwen 系列关键词)
2. 下载量 ≥ 50 筛选
3. 架构检查config.json
4. 平台验证状态查重
5. 提交到 ModelHub

195
main.py
View File

@@ -1,74 +1,201 @@
"""
ModelHub 提交智能体(代理模式)
用途:获取 strategyId本地脚本直接调用 ModelHub API
ModelHub Cancel-All 智能体
启动后自动取消当前账号所有 waiting/running 状态的验证任务
用法:推送此版本到平台,选择对应 tag 运行即可
"""
import json
import os
import signal
import time
import threading
from datetime import datetime
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import requests
# ============================================================
# 配置
# ============================================================
HOST = "0.0.0.0"
PORT = 8080
shutdown_requested = False
# 目标GPU和Token跟提交版本保持一致
TARGET_GPU = "Iluvatar_bi-150"
TARGET_TOKEN = "f45f1aae2c094426be237c88b1085015"
MODELHUB_API = "https://modelhub.org.cn/api"
BATCH_SIZE = 50 # 每批取消数量API上限
# ============================================================
# 全局状态
# ============================================================
state = {'running': False, 'last_run': None, 'logs': []}
state_lock = threading.Lock()
def _config() -> dict:
strategy_id = os.getenv("STRATEGY_ID", "")
return {
"strategy_id": strategy_id,
"status": "running",
def log(msg: str):
ts = datetime.now().strftime('%H:%M:%S')
line = f"[{ts}] {msg}"
print(line, flush=True)
with state_lock:
state['logs'].append(line)
if len(state['logs']) > 500:
state['logs'] = state['logs'][-300:]
def cancel_all_tasks():
"""取消所有 waiting 和 running 状态的任务"""
headers = {
'Xc-Token': TARGET_TOKEN,
'Accept': 'application/json',
'Content-Type': 'application/json',
}
log("=" * 50)
log("开始取消所有任务")
log(f"目标GPU: {TARGET_GPU}")
class Handler(BaseHTTPRequestHandler):
def do_GET(self) -> None:
if self.path == "/health":
self._send_json({"status": "ok"})
# 1. 查询所有任务
all_tasks = []
page = 1
while True:
try:
resp = requests.get(
f"{MODELHUB_API}/adapt/task/page",
headers={'Xc-Token': TARGET_TOKEN, 'Accept': 'application/json'},
params={'current': page, 'pageSize': 100, 'onlyMine': 'true',
'gpuType': TARGET_GPU},
timeout=15
)
data = resp.json()
if data.get('code') != 0:
log(f"查询失败: code={data.get('code')}")
break
records = data['data'].get('records', [])
if not records:
break
all_tasks.extend(records)
total = data['data'].get('total', 0)
log(f"{page}页: {len(records)} 条, 累计 {len(all_tasks)} / {total}")
if len(all_tasks) >= int(total):
break
page += 1
except Exception as e:
log(f" 查询异常: {e}")
return
if self.path == "/":
self._send_json({
"name": "modelhub-submit-agent",
"config": _config(),
# 2. 筛选 waiting/running 状态
cancellable = [t for t in all_tasks if t.get('status') in ('waiting', 'running')]
if not cancellable:
log(f" 没有可取消的任务 (总任务 {len(all_tasks)} 个)")
log("=" * 50)
return
task_ids = [int(t.get('taskId')) for t in cancellable if t.get('taskId')]
log(f" 总任务 {len(all_tasks)} 个, 可取消 {len(task_ids)}")
# 3. 分批取消
url = f"{MODELHUB_API}/async/task/stop-create-contest-task"
cancelled = 0
for i in range(0, len(task_ids), BATCH_SIZE):
batch = task_ids[i:i + BATCH_SIZE]
try:
resp = requests.put(url, headers=headers, json={'taskIds': batch}, timeout=15)
data = resp.json()
if data.get('code') == 0:
cancelled += len(batch)
log(f" 批次 {i // BATCH_SIZE + 1}: 取消 {len(batch)} 个 OK")
else:
log(f" 批次 {i // BATCH_SIZE + 1}: 失败 code={data.get('code')} msg={data.get('message', '')[:80]}")
except Exception as e:
log(f" 批次 {i // BATCH_SIZE + 1}: 异常 {e}")
time.sleep(1)
log(f" 取消完成: {cancelled} / {len(task_ids)}")
log("=" * 50)
# ============================================================
# HTTP 服务
# ============================================================
class AgentHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == '/health':
self._json({'status': 'ok'})
elif self.path == '/':
self._json({
'name': 'modelhub-cancel-all',
'gpu': TARGET_GPU,
'status': 'running' if state['running'] else 'idle',
'last_result': state.get('last_result'),
})
return
elif self.path.startswith('/logs'):
lines = 100
self._json({'logs': state['logs'][-lines:]})
else:
self._json({'error': 'not found'}, 404)
self._send_json({"error": "not found"}, status=404)
def log_message(self, fmt: str, *args: object) -> None:
print(f"{self.address_string()} - {fmt % args}", flush=True)
def _send_json(self, body: dict, status: int = 200) -> None:
payload = json.dumps(body).encode()
def _json(self, body: dict, status: int = 200):
payload = json.dumps(body, ensure_ascii=False).encode()
self.send_response(status)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(payload)))
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):
pass
def _handle_signal(signum: int, _frame: object) -> None:
# ============================================================
# 启动
# ============================================================
shutdown_requested = False
def _handle_signal(signum, _frame):
global shutdown_requested
shutdown_requested = True
print(f"received signal {signum}, shutting down", flush=True)
log(f"收到信号 {signum},准备关闭")
def main() -> None:
def main():
signal.signal(signal.SIGTERM, _handle_signal)
signal.signal(signal.SIGINT, _handle_signal)
server = ThreadingHTTPServer((HOST, PORT), Handler)
server = ThreadingHTTPServer((HOST, PORT), AgentHandler)
server.timeout = 1
strategy_id = os.getenv("STRATEGY_ID", "NOT_SET")
print(f"modelhub-submit-agent listening on {HOST}:{PORT}", flush=True)
print(f"STRATEGY_ID: {strategy_id}", flush=True)
log(f"Cancel-All 智能体启动 | {HOST}:{PORT}")
log(f"目标GPU: {TARGET_GPU}")
# 启动后自动取消所有任务
def _auto_cancel():
time.sleep(2)
try:
state['running'] = True
state['last_run'] = datetime.now().isoformat()
cancel_all_tasks()
state['last_result'] = {'success': True}
except Exception as e:
log(f"异常: {e}")
state['last_result'] = {'error': str(e)}
finally:
state['running'] = False
threading.Thread(target=_auto_cancel, daemon=True).start()
while not shutdown_requested:
server.handle_request()
server.server_close()
log("智能体已关闭")
if __name__ == "__main__":
if __name__ == '__main__':
main()

View File

@@ -1 +1,2 @@
# No external dependencies needed
requests>=2.28.0
pyyaml>=6.0