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
|
```json
|
||||||
- ✅ 暴露 8080 端口
|
{
|
||||||
- ✅ `/health` 端点返回 200
|
"gpus": ["Kunlunxin_p-800", "hygon_k100-ai"],
|
||||||
- ✅ 处理 SIGTERM 信号
|
"limit": 30
|
||||||
- ✅ 读取 `STRATEGY_ID` 环境变量
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `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 提交智能体(代理模式)
|
ModelHub Cancel-All 智能体
|
||||||
用途:获取 strategyId,本地脚本直接调用 ModelHub API
|
启动后自动取消当前账号所有 waiting/running 状态的验证任务
|
||||||
|
用法:推送此版本到平台,选择对应 tag 运行即可
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import signal
|
import signal
|
||||||
|
import time
|
||||||
|
import threading
|
||||||
|
from datetime import datetime
|
||||||
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 配置
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
HOST = "0.0.0.0"
|
HOST = "0.0.0.0"
|
||||||
PORT = 8080
|
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:
|
def log(msg: str):
|
||||||
strategy_id = os.getenv("STRATEGY_ID", "")
|
ts = datetime.now().strftime('%H:%M:%S')
|
||||||
return {
|
line = f"[{ts}] {msg}"
|
||||||
"strategy_id": strategy_id,
|
print(line, flush=True)
|
||||||
"status": "running",
|
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):
|
# 1. 查询所有任务
|
||||||
def do_GET(self) -> None:
|
all_tasks = []
|
||||||
if self.path == "/health":
|
page = 1
|
||||||
self._send_json({"status": "ok"})
|
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
|
return
|
||||||
|
|
||||||
if self.path == "/":
|
# 2. 筛选 waiting/running 状态
|
||||||
self._send_json({
|
cancellable = [t for t in all_tasks if t.get('status') in ('waiting', 'running')]
|
||||||
"name": "modelhub-submit-agent",
|
if not cancellable:
|
||||||
"config": _config(),
|
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 _json(self, body: dict, status: int = 200):
|
||||||
|
payload = json.dumps(body, ensure_ascii=False).encode()
|
||||||
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()
|
|
||||||
self.send_response(status)
|
self.send_response(status)
|
||||||
self.send_header("Content-Type", "application/json")
|
self.send_header('Content-Type', 'application/json; charset=utf-8')
|
||||||
self.send_header("Content-Length", str(len(payload)))
|
self.send_header('Content-Length', str(len(payload)))
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
self.wfile.write(payload)
|
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
|
global shutdown_requested
|
||||||
shutdown_requested = True
|
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.SIGTERM, _handle_signal)
|
||||||
signal.signal(signal.SIGINT, _handle_signal)
|
signal.signal(signal.SIGINT, _handle_signal)
|
||||||
|
|
||||||
server = ThreadingHTTPServer((HOST, PORT), Handler)
|
server = ThreadingHTTPServer((HOST, PORT), AgentHandler)
|
||||||
server.timeout = 1
|
server.timeout = 1
|
||||||
|
|
||||||
strategy_id = os.getenv("STRATEGY_ID", "NOT_SET")
|
log(f"Cancel-All 智能体启动 | {HOST}:{PORT}")
|
||||||
print(f"modelhub-submit-agent listening on {HOST}:{PORT}", flush=True)
|
log(f"目标GPU: {TARGET_GPU}")
|
||||||
print(f"STRATEGY_ID: {strategy_id}", flush=True)
|
|
||||||
|
# 启动后自动取消所有任务
|
||||||
|
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:
|
while not shutdown_requested:
|
||||||
server.handle_request()
|
server.handle_request()
|
||||||
|
|
||||||
server.server_close()
|
server.server_close()
|
||||||
|
log("智能体已关闭")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
# No external dependencies needed
|
requests>=2.28.0
|
||||||
|
pyyaml>=6.0
|
||||||
|
|||||||
Reference in New Issue
Block a user