feat: add admin provider analytics dashboard

This commit is contained in:
2026-04-19 18:56:17 +08:00
parent b89ca96e4b
commit 395cdf4edd
12 changed files with 886 additions and 51 deletions

View File

@@ -1,4 +1,4 @@
from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel, ConfigDict, Field
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
@@ -8,6 +8,7 @@ from app.db.admin_models import Provider
from app.db.database import get_db
from app.services.adapters.registry import AdapterRegistry
from app.services.cost_tracker import cost_tracker
from app.services.generation_jobs import get_admin_provider_analytics
from app.services.provider_policy import DEFAULT_PROVIDERS, list_capability_policies
from app.services.secret_service import SecretService
@@ -56,6 +57,48 @@ class ProviderResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
class ProviderAnalyticsBucket(BaseModel):
capability: str
adapter: str
call_count: int
success_count: int
failure_count: int
avg_latency_ms: float | None = None
estimated_cost_usd: float
class ProviderAnalyticsUserBucket(BaseModel):
user_id: str
call_count: int
success_count: int
failure_count: int
job_count: int
story_count: int
estimated_cost_usd: float
class ProviderAnalyticsFailureReason(BaseModel):
reason: str
count: int
class ProviderAnalyticsResponse(BaseModel):
scope: str
window_days: int | None = None
capability: str | None = None
total_calls: int
successful_calls: int
failed_calls: int
avg_latency_ms: float | None = None
estimated_cost_usd: float
user_count: int
job_count: int
story_count: int
by_provider: list[ProviderAnalyticsBucket]
by_user: list[ProviderAnalyticsUserBucket]
failure_reasons: list[ProviderAnalyticsFailureReason]
@router.get("/providers/adapters")
async def list_available_adapters():
"""获取所有可用的适配器类型 (定义的类)。"""
@@ -74,6 +117,20 @@ async def list_provider_capabilities():
return list_capability_policies()
@router.get("/providers/analytics", response_model=ProviderAnalyticsResponse)
async def get_provider_analytics(
days: int | None = Query(default=None, ge=1, le=365),
capability: str | None = Query(default=None),
db: AsyncSession = Depends(get_db),
):
"""获取当前环境跨用户的 Provider 运营摘要。"""
return await get_admin_provider_analytics(
db,
days=days,
capability=capability,
)
@router.get("/providers", response_model=list[ProviderResponse])
async def list_providers(db: AsyncSession = Depends(get_db)):
result = await db.execute(select(Provider))