Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a4ba2e146 | ||
|
|
2222e1545b | ||
|
|
441b540e47 | ||
|
|
afbda884ad | ||
|
|
68c7a99e68 | ||
|
|
8e8fd50927 | ||
|
|
5a6862f8da | ||
|
|
727f0f678f | ||
|
|
207d44b8f2 | ||
|
|
1d33394dac | ||
|
|
1811a66aee | ||
|
|
12bf6d637f | ||
|
|
85c456afe0 | ||
|
|
b3b52848c2 | ||
|
|
d6e9f2e7a0 | ||
|
|
821edbc8f5 | ||
|
|
5e2df178e1 | ||
|
|
761e74ff82 | ||
|
|
09ac80f1ec | ||
|
|
3975a3370a | ||
|
|
7313f8da67 | ||
|
|
2955a57e2e |
39
README.md
39
README.md
@@ -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. 搜索 ModelScope(Qwen 系列关键词)
|
||||
2. 下载量 ≥ 50 筛选
|
||||
3. 架构检查(config.json)
|
||||
4. 平台验证状态查重
|
||||
5. 提交到 ModelHub
|
||||
|
||||
195
main.py
195
main.py
@@ -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()
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
# No external dependencies needed
|
||||
requests>=2.28.0
|
||||
pyyaml>=6.0
|
||||
|
||||
Reference in New Issue
Block a user