""" 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}