feat(billing): 统一计价与候选冻结估算

新增 effective-pricing-v2、9 位十进制定点计算、显式免费校验和结构化缺价错误。估价与生产预处理覆盖全部可用候选,并按最大候选费用冻结;规则优先级为平台模型、平台、基准模型。\n\n同步计价契约和 OpenAPI,补充 Token 参数别名、视频五秒向上取整及缺价回归测试。\n\n验证:go test ./...、pnpm openapi、Web 测试与构建通过。
This commit is contained in:
2026-07-20 23:22:45 +08:00
parent 01a013c809
commit 5114686c35
14 changed files with 1192 additions and 28 deletions
+23 -13
View File
@@ -11,10 +11,14 @@ import (
)
type EstimateResult struct {
Items []any `json:"items"`
Resolver string `json:"resolver"`
TotalAmount float64 `json:"totalAmount"`
Currency string `json:"currency"`
Items []any `json:"items"`
Resolver string `json:"resolver"`
TotalAmount float64 `json:"totalAmount"`
ReservationAmount float64 `json:"reservationAmount"`
Currency string `json:"currency"`
CandidateCount int `json:"candidateCount"`
PricingVersion string `json:"pricingVersion"`
RequestFingerprint string `json:"requestFingerprint"`
}
func (s *Service) Estimate(ctx context.Context, kind string, model string, body map[string]any, user *auth.User) (EstimateResult, error) {
@@ -32,15 +36,21 @@ func (s *Service) Estimate(ctx context.Context, kind string, model string, body
if err != nil {
return EstimateResult{}, err
}
candidate := candidates[0]
body = preprocessRequest(kind, body, candidate)
items := s.estimatedBillings(ctx, user, kind, body, candidate)
return EstimateResult{
Items: items,
Resolver: "effective-pricing-v1",
TotalAmount: totalBillingAmount(items),
Currency: billingCurrency(items),
}, nil
estimates := make([]candidateEstimate, 0, len(candidates))
var pricingErr error
for _, candidate := range candidates {
candidateBody := preprocessRequest(kind, cloneMap(body), candidate)
estimate, candidateErr := s.estimateCandidateV2(ctx, user, kind, candidateBody, candidate)
if candidateErr != nil {
pricingErr = candidateErr
continue
}
estimates = append(estimates, estimate)
}
if len(estimates) == 0 && pricingErr != nil {
return EstimateResult{}, pricingErr
}
return buildEstimateResult(estimates, pricingRequestFingerprint(kind, model, body))
}
func (s *Service) estimatedBillings(ctx context.Context, user *auth.User, kind string, body map[string]any, candidate store.RuntimeModelCandidate) []any {