Files
qyyfw 78ff261b5a 初始提交: Qihu-AIFlow v2.0.1 完整项目
- 前端 React+TypeScript (源码+构建产物)
- 后端 Python FastAPI
- 数据库迁移 001-015
- Docker部署配置
- 微信ClawBot API频道绑定
- 项目文档 README.md
2026-07-02 14:40:46 +08:00

394 lines
13 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
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}