"""调用可观测面板后端 API(媒体/LLM 调用归因)。 路由前缀: /app/api/admin/observability 数据来源: token_usage_logs(Phase 2.2 已加 trace_id 列,可经 X-Trace-Id 关联请求链路)。 """ from datetime import datetime, timedelta from fastapi import APIRouter, Query, Depends from ...database import get_db from ..auth import get_current_user from sqlalchemy import text from sqlalchemy.orm import Session router = APIRouter() @router.get("/summary") async def observability_summary( days: int = Query(30, description="统计天数"), db: Session = Depends(get_db), user=Depends(get_current_user), ): """媒体/LLM 调用归因:总览 + 按模型 + 按用户 + 每日趋势 + 最近调用(trace_id 下钻)。""" since = datetime.now() - timedelta(days=days) totals = db.execute(text(""" SELECT COUNT(*), COALESCE(SUM(total_tokens), 0), COALESCE(SUM(cost), 0), COUNT(DISTINCT user_id) FROM token_usage_logs WHERE created_at >= :since """), {"since": since}).fetchone() by_model = db.execute(text(""" SELECT model_name, COUNT(*), COALESCE(SUM(total_tokens), 0), COALESCE(SUM(cost), 0) FROM token_usage_logs WHERE created_at >= :since GROUP BY model_name ORDER BY COALESCE(SUM(total_tokens), 0) DESC """), {"since": since}).fetchall() by_user = db.execute(text(""" SELECT t.user_id, u.username, COUNT(*), COALESCE(SUM(t.total_tokens), 0), COALESCE(SUM(t.cost), 0) FROM token_usage_logs t LEFT JOIN users u ON u.id = t.user_id WHERE t.created_at >= :since GROUP BY t.user_id, u.username ORDER BY COALESCE(SUM(t.total_tokens), 0) DESC LIMIT 20 """), {"since": since}).fetchall() daily = db.execute(text(""" SELECT DATE(created_at) AS d, COALESCE(SUM(total_tokens), 0), COALESCE(SUM(cost), 0), COUNT(*) FROM token_usage_logs WHERE created_at >= :since GROUP BY DATE(created_at) ORDER BY d """), {"since": since}).fetchall() recent = db.execute(text(""" SELECT trace_id, model_name, total_tokens, cost, created_at FROM token_usage_logs WHERE created_at >= :since ORDER BY created_at DESC LIMIT 50 """), {"since": since}).fetchall() return { "totals": { "calls": int(totals[0] or 0), "tokens": int(totals[1] or 0), "cost": float(totals[2] or 0), "users": int(totals[3] or 0), }, "by_model": [ { "model_name": m[0] or "unknown", "calls": int(m[1] or 0), "tokens": int(m[2] or 0), "cost": float(m[3] or 0), } for m in by_model ], "by_user": [ { "user_id": u[0], "username": u[1] or u[0], "calls": int(u[2] or 0), "tokens": int(u[3] or 0), "cost": float(u[4] or 0), } for u in by_user ], "daily": [ { "date": str(d[0]), "tokens": int(d[1] or 0), "cost": float(d[2] or 0), "calls": int(d[3] or 0), } for d in daily ], "recent": [ { "trace_id": r[0], "model_name": r[1] or "unknown", "tokens": int(r[2] or 0), "cost": float(r[3] or 0), "created_at": r[4].strftime("%Y-%m-%d %H:%M:%S") if r[4] else None, } for r in recent ], }