Files
HSAP/platform/as_platform/api/models_routes.py
Chengfang Lu e72bc061c5 feat: HSAP platform v2 — modular navigation, quality review, audit log, world model simulation
Major changes:
- New frontend (platform/web/): Vite + React 18 + TypeScript + Tailwind
- 4-module navigation: 数据送标 / 模型管理 / 车队管理 / 系统管理
- Data catalog with charts (DMS/ADAS/Lane 3-tab view)
- Quality review workflow (标注质检): Good/Fine/Bad scoring with auto-advance
- Audit enhancements: batch operations, rejection categories, Feishu notifications
- Operation audit log (操作日志)
- World model simulation studio (仿真工坊)
- Dataset version management with snapshots and diff
- ADAS 7-class dataset integration (138K images organized + compressed)
- User management with Feishu integration and pagination
- CRUD/search/filter on all pages, card layout redesign
- PIL-optimized image overlay rendering
- Auto-snapshot on build, in_review workflow stage
- Removed embedded algorithm code (now in workspace)
2026-06-03 11:40:21 +08:00

156 lines
4.7 KiB
Python

"""模型管理 API 路由 — 模块化前缀 /api/v1/models
本路由将 training/* 功能以模块化路径重新暴露,同时保留旧路径兼容。
"""
from __future__ import annotations
from typing import Annotated, Any
from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel, Field
from as_platform.auth.deps import can_submit_action, get_current_user, require_permission
from as_platform.db.models import User
from as_platform.data.versions import create_snapshot, diff_versions, get_version, list_versions
from as_platform.training.service import (
TRAINING_ACTIONS,
create_training_submission,
get_model_registry,
get_training_record,
list_training_records,
)
router = APIRouter(prefix="/api/v1/models", tags=["models"])
class CreateTrainingBody(BaseModel):
action: str
params: dict[str, Any] = Field(default_factory=dict)
note: str | None = None
@router.get("/actions")
def api_models_actions(_user: Annotated[User, Depends(require_permission("read:jobs"))]) -> dict[str, Any]:
"""可用的训练/评估/晋级操作列表"""
from as_platform.audit.queue import ACTION_LABELS
return {
"actions": [
{"id": action, "label": ACTION_LABELS.get(action, action)}
for action in sorted(TRAINING_ACTIONS)
]
}
@router.get("/registry")
def api_models_registry(
_user: Annotated[User, Depends(require_permission("read:jobs"))],
project: str = Query("dms"),
task: str | None = None,
) -> dict[str, Any]:
"""模型注册表"""
return get_model_registry(project=project, task=task)
@router.get("/records")
def api_models_records(
_user: Annotated[User, Depends(require_permission("read:jobs"))],
project: str | None = None,
kind: str | None = None,
status: str | None = None,
task: str | None = None,
offset: int = Query(0, ge=0),
limit: int = Query(20, ge=1, le=100),
) -> dict[str, Any]:
"""训练/评估记录列表"""
return list_training_records(
project=project, kind=kind, status=status, task=task, offset=offset, limit=limit
)
@router.get("/records/{record_id}")
def api_models_record(
record_id: str,
_user: Annotated[User, Depends(require_permission("read:jobs"))],
) -> dict[str, Any]:
"""单条训练记录详情"""
rec = get_training_record(record_id)
if not rec:
raise HTTPException(404, "训练记录不存在")
return rec
@router.post("/records")
def api_models_create_record(
body: CreateTrainingBody,
user: Annotated[User, Depends(get_current_user)],
) -> dict[str, Any]:
"""提交训练/评估/晋级申请"""
if not can_submit_action(user, body.action):
raise HTTPException(403, f"无权提交: {body.action}")
try:
return create_training_submission(
body.action,
body.params,
submitted_by=user.name,
submitted_by_user_id=user.id,
note=body.note,
)
except ValueError as e:
raise HTTPException(400, str(e)) from e
# ── 数据集版本管理 ──
class CreateSnapshotBody(BaseModel):
project: str = "dms"
description: str = ""
@router.get("/datasets")
def api_list_dataset_versions(
_user: Annotated[User, Depends(require_permission("read:jobs"))],
project: str = Query("dms"),
) -> dict[str, Any]:
return {"items": list_versions(project)}
@router.post("/datasets/snapshot")
def api_create_snapshot(
body: CreateSnapshotBody,
user: Annotated[User, Depends(require_permission("write:approval_submit"))],
) -> dict[str, Any]:
try:
result = create_snapshot(body.project, body.description, author=user.name)
from as_platform.audit.log_utils import log_op
log_op(user_id=user.id, user_name=user.name, category="data", action="create_snapshot",
target_type="snapshot", target_id=result.get("version_id"), summary=f"创建快照: {result.get('version_id')} ({body.project})")
return result
except Exception as e:
raise HTTPException(500, str(e)) from e
@router.get("/datasets/{version_id}")
def api_get_version(
version_id: str,
_user: Annotated[User, Depends(require_permission("read:jobs"))],
project: str = Query("dms"),
) -> dict[str, Any]:
v = get_version(project, version_id)
if not v:
raise HTTPException(404, f"版本 {version_id} 不存在")
return v
@router.get("/datasets/{version_id}/diff")
def api_diff_versions(
version_id: str,
_user: Annotated[User, Depends(require_permission("read:jobs"))],
compare: str = Query(...),
project: str = Query("dms"),
) -> dict[str, Any]:
result = diff_versions(project, compare, version_id)
if "error" in result:
raise HTTPException(400, result["error"])
return result