| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778 |
- """用量与计费路由。"""
- from __future__ import annotations
- import asyncio
- from fastapi import APIRouter
- from api.models import QuotaUpdateRequest
- from billing import guard, pricing
- from db import billing_store, db_conn
- from obs import get_logger
- logger = get_logger(__name__)
- router = APIRouter(tags=["用量"])
- @router.get("/usage/today")
- async def usage_today(user_name: str | None = None):
- """当日用量与额度。
- 计费库不可用时不报 5xx——这个接口是给用户看「我还剩多少额度」的,
- 因为一个次要故障就返回错误会让用户以为整个服务挂了。所以返回
- ``available=False`` 并附原因。
- """
- user = (user_name or "default").strip() or "default"
- snapshot = await asyncio.to_thread(guard.snapshot, user, None)
- return snapshot
- @router.get("/usage/recent")
- async def usage_recent(user_name: str | None = None, limit: int = 20):
- """最近若干条用量流水。"""
- user = (user_name or "default").strip() or "default"
- items = await asyncio.to_thread(billing_store.recent_usage, user, min(limit, 200))
- return {"user_name": user, "items": items, "count": len(items)}
- @router.put("/usage/quota")
- async def update_quota(payload: QuotaUpdateRequest):
- """调整某用户当日额度(0 = 不限)。"""
- user = (payload.user_name or "default").strip() or "default"
- day = db_conn.day_key()
- quota = await asyncio.to_thread(
- billing_store.set_quota_limit, user, day, payload.limit_tokens
- )
- logger.info("额度调整 user=%s day=%s limit=%d", user, day, payload.limit_tokens)
- return quota
- @router.get("/usage/pricing")
- async def get_pricing():
- """模型单价表。"""
- items = await asyncio.to_thread(pricing.pricing_table)
- return {"items": items, "count": len(items), 'enabled': pricing.enabled()}
- @router.get("/health/billing")
- async def billing_health():
- """计费子系统自检:单价表是否覆盖当前配置的模型。"""
- import config
- if not pricing.enabled():
- return {'ok': True, 'enabled': False, 'missing': [], 'hint': '计费已关闭,仅记录Token用量'}
- models = {config.MODEL}
- table = await asyncio.to_thread(pricing.pricing_table)
- priced = {row["model"] for row in table}
- missing = sorted(m for m in models if m and m not in priced)
- return {
- "ok": not missing,
- 'enabled': True,
- "configured_models": sorted(m for m in models if m),
- "priced_models": sorted(priced),
- "missing": missing,
- "hint": "缺单价的模型会在请求开始时 fail-closed;补 billing_pricing 或设 BILLING_ALLOW_UNPRICED=1",
- }
|