- 前端 React+TypeScript (源码+构建产物) - 后端 Python FastAPI - 数据库迁移 001-015 - Docker部署配置 - 微信ClawBot API频道绑定 - 项目文档 README.md
394 lines
13 KiB
Python
394 lines
13 KiB
Python
"""
|
||
AI自进化 API路由
|
||
"""
|
||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||
from sqlalchemy.orm import Session
|
||
from typing import Optional, List
|
||
from pydantic import BaseModel
|
||
|
||
from ..auth import get_current_user
|
||
from ..models import User
|
||
from ..services.evolution_service import get_evolution_service, EvolutionWatcherManager
|
||
from ..database import get_db, SessionLocal
|
||
|
||
router = APIRouter()
|
||
|
||
# ============================================================
|
||
# Request/Response Models
|
||
# ============================================================
|
||
|
||
class SkillUpdateRequest(BaseModel):
|
||
description: Optional[str] = None
|
||
tags: Optional[List[str]] = None
|
||
category: Optional[str] = None
|
||
|
||
class TrajectorySubmitRequest(BaseModel):
|
||
conversation_id: str
|
||
messages: List[dict] = []
|
||
|
||
class PublishRequest(BaseModel):
|
||
price: float = 0.00
|
||
market_description: str = ""
|
||
|
||
class ShareToTeamRequest(BaseModel):
|
||
team_id: str
|
||
|
||
# ============================================================
|
||
# 技能管理 API
|
||
# ============================================================
|
||
|
||
@router.get("/skills")
|
||
def list_skills(
|
||
state: Optional[str] = Query(None, description="技能状态过滤"),
|
||
keyword: Optional[str] = Query(None, description="关键词搜索"),
|
||
sort_by: str = Query("updated_at", description="排序字段"),
|
||
order: str = Query("desc", description="排序方向"),
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""获取用户技能列表"""
|
||
svc = get_evolution_service(db, current_user.id)
|
||
return {"code": 0, "data": svc.get_skills(state=state, keyword=keyword, sort_by=sort_by, order=order)}
|
||
|
||
|
||
@router.get("/skills/{skill_name}")
|
||
def get_skill_detail(
|
||
skill_name: str,
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""获取技能详情"""
|
||
svc = get_evolution_service(db, current_user.id)
|
||
result = svc.get_skill_detail(skill_name)
|
||
if not result:
|
||
raise HTTPException(status_code=404, detail="技能不存在")
|
||
return {"code": 0, "data": result}
|
||
|
||
|
||
@router.put("/skills/{skill_name}")
|
||
def update_skill(
|
||
skill_name: str,
|
||
data: SkillUpdateRequest,
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""编辑技能"""
|
||
svc = get_evolution_service(db, current_user.id)
|
||
result = svc.update_skill(skill_name, data.model_dump(exclude_none=True))
|
||
if not result:
|
||
raise HTTPException(status_code=404, detail="技能不存在")
|
||
return {"code": 0, "data": result}
|
||
|
||
|
||
@router.delete("/skills/{skill_name}")
|
||
def delete_skill(
|
||
skill_name: str,
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""删除技能"""
|
||
svc = get_evolution_service(db, current_user.id)
|
||
if not svc.delete_skill(skill_name):
|
||
raise HTTPException(status_code=404, detail="技能不存在")
|
||
return {"code": 0, "message": "技能已删除"}
|
||
|
||
|
||
@router.post("/skills/{skill_name}/freeze")
|
||
def freeze_skill(
|
||
skill_name: str,
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""冻结技能"""
|
||
svc = get_evolution_service(db, current_user.id)
|
||
result = svc.freeze_skill(skill_name)
|
||
if not result:
|
||
raise HTTPException(status_code=404, detail="技能不存在")
|
||
return {"code": 0, "data": result}
|
||
|
||
|
||
@router.post("/skills/{skill_name}/unfreeze")
|
||
def unfreeze_skill(
|
||
skill_name: str,
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""解冻技能"""
|
||
svc = get_evolution_service(db, current_user.id)
|
||
result = svc.unfreeze_skill(skill_name)
|
||
if not result:
|
||
raise HTTPException(status_code=404, detail="技能不存在")
|
||
return {"code": 0, "data": result}
|
||
|
||
|
||
@router.get("/skills/{skill_name}/history")
|
||
def get_skill_history(
|
||
skill_name: str,
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""获取技能版本历史"""
|
||
svc = get_evolution_service(db, current_user.id)
|
||
return {"code": 0, "data": svc.get_skill_history(skill_name)}
|
||
|
||
|
||
@router.get("/skills/{skill_name}/trajectories")
|
||
def get_skill_trajectories(
|
||
skill_name: str,
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""获取技能来源轨迹列表"""
|
||
svc = get_evolution_service(db, current_user.id)
|
||
return {"code": 0, "data": svc.get_skill_trajectories(skill_name)}
|
||
|
||
|
||
# ============================================================
|
||
# 进化事件 API
|
||
# ============================================================
|
||
|
||
@router.get("/events")
|
||
def list_events(
|
||
event_type: Optional[str] = Query(None),
|
||
skill_name: Optional[str] = Query(None),
|
||
limit: int = Query(50, ge=1, le=200),
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""获取进化事件列表"""
|
||
svc = get_evolution_service(db, current_user.id)
|
||
return {"code": 0, "data": svc.get_events(event_type=event_type, skill_name=skill_name, limit=limit)}
|
||
|
||
|
||
# ============================================================
|
||
# 驾驶舱数据 API
|
||
# ============================================================
|
||
|
||
@router.get("/dashboard/skill-tree")
|
||
def get_skill_tree(
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""获取技能图谱数据(面板A)"""
|
||
svc = get_evolution_service(db, current_user.id)
|
||
return {"code": 0, "data": svc.get_skill_tree()}
|
||
|
||
|
||
@router.get("/dashboard/timeline")
|
||
def get_timeline(
|
||
event_type: Optional[str] = Query(None),
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""获取进化时间线数据(面板B)"""
|
||
svc = get_evolution_service(db, current_user.id)
|
||
return {"code": 0, "data": svc.get_timeline(event_type=event_type)}
|
||
|
||
|
||
@router.get("/dashboard/statistics")
|
||
def get_statistics(
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""获取技能使用统计数据(面板C)"""
|
||
svc = get_evolution_service(db, current_user.id)
|
||
return {"code": 0, "data": svc.get_statistics()}
|
||
|
||
|
||
@router.get("/dashboard/profile")
|
||
def get_profile(
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""获取AI能力画像数据(面板D)"""
|
||
svc = get_evolution_service(db, current_user.id)
|
||
return {"code": 0, "data": svc.get_profile()}
|
||
|
||
|
||
# ============================================================
|
||
# 轨迹提交 API
|
||
# ============================================================
|
||
|
||
@router.post("/trajectory/submit")
|
||
def submit_trajectory(
|
||
data: TrajectorySubmitRequest,
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""提交对话轨迹到进化流水线"""
|
||
svc = get_evolution_service(db, current_user.id)
|
||
result = svc.submit_trajectory(data.conversation_id, data.messages)
|
||
return {"code": 0, "data": result, "message": "轨迹已提交"}
|
||
|
||
|
||
# ============================================================
|
||
# v2 新增 API
|
||
# ============================================================
|
||
|
||
|
||
@router.post("/skills/{skill_name}/rollback/{version}")
|
||
def rollback_skill(
|
||
skill_name: str,
|
||
version: int,
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""技能回滚到指定版本"""
|
||
svc = get_evolution_service(db, current_user.id)
|
||
result = svc.rollback_skill(skill_name, version)
|
||
if not result:
|
||
raise HTTPException(status_code=404, detail="技能或版本不存在")
|
||
return {"code": 0, "data": result}
|
||
|
||
|
||
@router.post("/skills/{skill_name}/publish")
|
||
def publish_skill(
|
||
skill_name: str,
|
||
data: PublishRequest,
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""发布技能到技能市场"""
|
||
svc = get_evolution_service(db, current_user.id)
|
||
result = svc.publish_to_market(skill_name, data.price, data.market_description)
|
||
if not result:
|
||
raise HTTPException(status_code=404, detail="技能不存在")
|
||
return {"code": 0, "data": result}
|
||
|
||
|
||
@router.post("/skills/{skill_name}/share-to-team")
|
||
def share_skill_to_team(
|
||
skill_name: str,
|
||
data: ShareToTeamRequest,
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""将技能共享给团队"""
|
||
svc = get_evolution_service(db, current_user.id)
|
||
result = svc.share_to_team(skill_name, data.team_id)
|
||
if not result:
|
||
raise HTTPException(status_code=404, detail="技能或团队不存在")
|
||
return {"code": 0, "data": result}
|
||
|
||
|
||
@router.get("/pipeline/status")
|
||
def get_pipeline_status(
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""获取流水线状态"""
|
||
svc = get_evolution_service(db, current_user.id)
|
||
return {"code": 0, "data": svc.get_pipeline_status()}
|
||
|
||
|
||
@router.get("/reports")
|
||
def list_reports(
|
||
report_type: Optional[str] = Query(None, description="报告类型过滤"),
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""获取报告列表"""
|
||
svc = get_evolution_service(db, current_user.id)
|
||
return {"code": 0, "data": svc.get_reports(report_type=report_type)}
|
||
|
||
|
||
@router.post("/reports/generate")
|
||
def generate_report(
|
||
report_type: str = Query("weekly", description="报告类型: weekly/monthly"),
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""手动生成进化报告"""
|
||
svc = get_evolution_service(db, current_user.id)
|
||
result = svc.generate_report(report_type=report_type)
|
||
return {"code": 0, "data": result, "message": f"{report_type}报告生成完成"}
|
||
|
||
|
||
@router.get("/reports/{report_id}")
|
||
def get_report_detail(
|
||
report_id: str,
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""获取报告详情"""
|
||
svc = get_evolution_service(db, current_user.id)
|
||
result = svc.get_report_detail(report_id)
|
||
if not result:
|
||
raise HTTPException(status_code=404, detail="报告不存在")
|
||
return {"code": 0, "data": result}
|
||
|
||
|
||
@router.get("/skills/{skill_name}/versions")
|
||
def get_skill_versions(
|
||
skill_name: str,
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""获取技能所有版本记录(版本快照列表)"""
|
||
from ..models import SkillVersion
|
||
versions = db.query(SkillVersion).filter(
|
||
SkillVersion.user_id == current_user.id,
|
||
SkillVersion.skill_name == skill_name
|
||
).order_by(SkillVersion.version.desc()).all()
|
||
return {"code": 0, "data": [{
|
||
"id": v.id,
|
||
"skill_name": v.skill_name,
|
||
"version": v.version,
|
||
"git_commit_sha": v.git_commit_sha,
|
||
"description": v.description,
|
||
"created_at": v.created_at.isoformat() if v.created_at else None,
|
||
} for v in versions]}
|
||
|
||
|
||
@router.post("/pipeline/trigger")
|
||
def trigger_pipeline(
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""手动触发进化流水线(如果未运行则启动,如果已运行则重启)"""
|
||
watcher = EvolutionWatcherManager()
|
||
# 先尝试启动,如果已存在则重启
|
||
if not watcher.start_user_evolution(current_user.id, lambda: SessionLocal()):
|
||
watcher.restart_user_evolution(current_user.id, lambda: SessionLocal())
|
||
return {"code": 0, "message": "流水线已触发"}
|
||
|
||
|
||
@router.get("/watcher/status")
|
||
def get_watcher_status(
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""获取当前用户的Watcher状态"""
|
||
watcher = EvolutionWatcherManager()
|
||
status = watcher.get_user_status(current_user.id, lambda: SessionLocal())
|
||
return {"code": 0, "data": status}
|
||
|
||
|
||
@router.post("/watcher/restart")
|
||
def restart_watcher(
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""重启用户进化引擎"""
|
||
watcher = EvolutionWatcherManager()
|
||
ok = watcher.restart_user_evolution(current_user.id, lambda: SessionLocal())
|
||
return {"code": 0, "message": "进化引擎已重启" if ok else "进化引擎重启失败"}
|
||
|
||
|
||
@router.get("/admin/watchers")
|
||
def get_all_watchers(
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""管理员端点 - 获取所有用户的Watcher状态"""
|
||
if current_user.role != "admin":
|
||
raise HTTPException(status_code=403, detail="无权限访问")
|
||
watcher = EvolutionWatcherManager()
|
||
all_status = {}
|
||
for uid, scheduler in watcher._schedulers.items():
|
||
all_status[str(uid)] = {
|
||
"user_id": uid,
|
||
"running": scheduler.is_running(),
|
||
}
|
||
return {"code": 0, "data": all_status}
|