Files
server-core/app/api/routers/compute_internal.py
T
Pine edca0dcfa7 P1/P2 计费体系修复:统一扣费回调+请求前预检+对账接口+折扣同步
P1-1 统一扣费回调:
- 新增 POST /api/compute/internal/deduct(COMPUTE_ADMIN_TOKEN鉴权,engine_log_id幂等)
- compute 扣费后异步回调,按引擎实际费用(微元)扣平台来源账本
- 删除 relay.py 中 _bill_usage/_bill_usage_anthropic 调用(改由compute回调记账)
- ComputeUsageRecord 加 engine_log_id 字段,alembic 0079 迁移

P1-2 请求前预检:
- _user_engine_pat 中查 compute 余额(5s缓存),余额<=0直接403
- 避免无余额请求仍转发到引擎

P2-1 对账接口:
- GET /admin/compute/reconcile 对比引擎used_quota vs 平台累计扣费
- 支持分页、only_mismatch过滤

P2-2 折扣统一:
- 用户级折扣设置/删除时异步同步等效系数到引擎users.discount
- compute /api/user/manage 支持 username 定位用户
- compute_client 新增 set_user_discount_by_username
2026-09-13 17:41:02 +08:00

62 lines
2.3 KiB
Python

# -*- coding: utf-8 -*-
"""compute 引擎 → server-core 内部回调接口。
compute 扣费完成后异步回调此接口,按引擎实际费用(微元)扣平台来源账本。
鉴权:Authorization: Bearer <COMPUTE_ADMIN_TOKEN>(与引擎侧 PINEAGENTS_INTERNAL_TOKEN 一致)。
幂等:engine_log_id 去重,防网络重试重复扣费。
"""
from __future__ import annotations
import logging
from fastapi import APIRouter, Depends, Header, HTTPException
from pydantic import BaseModel
from ... import config
from ...infrastructure.repositories import Database
from ..dependencies import get_db
from ...services.compute_pricing_service import deduct_by_engine_cost
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/compute/internal", tags=["compute-internal"])
class EngineDeductRequest(BaseModel):
username: str
actual_cost_micro: int
engine_log_id: int
model_name: str = ""
token_count: int = 0
def _verify_internal_token(authorization: str = Header(default="")) -> None:
"""校验 compute 引擎内部回调令牌。"""
token = authorization[7:].strip() if authorization.lower().startswith("bearer ") else authorization.strip()
expected = config.COMPUTE_ADMIN_TOKEN or ""
if not expected or token != expected:
raise HTTPException(status_code=401, detail="invalid internal token")
@router.post("/deduct", summary="compute 引擎扣费回调(按实际费用扣平台账本)")
async def engine_deduct(
req: EngineDeductRequest,
db: Database = Depends(get_db),
_auth: None = Depends(_verify_internal_token),
):
"""compute 扣费后回调:actual_cost_micro(微元)→ 扣企业分配/个人余额,engine_log_id 幂等。"""
try:
result = await deduct_by_engine_cost(
db,
username=req.username,
actual_cost_micro=req.actual_cost_micro,
engine_log_id=req.engine_log_id,
model_name=req.model_name,
token_count=req.token_count,
)
return {"ok": result.get("ok", True), **result}
except Exception as exc: # noqa: BLE001
logger.warning("[compute-internal] deduct callback failed username=%s engine_log_id=%s err=%s",
req.username, req.engine_log_id, exc)
raise HTTPException(status_code=500, detail=str(exc)) from exc