fix(channel): use upstream model for account stats pricing and remove channel pricing fallback
- resolveAccountStatsCost now uses the final upstream model (after account-level mapping) to match custom pricing rules, fixing the issue where requested model (e.g. claude-sonnet-4-5) didn't match rules configured for upstream model (e.g. claude-opus-4-6) - Remove tryChannelPricing fallback — only custom rules are applied, unmatched requests use default formula (total_cost × rate) - Remove unused billingService and serviceTier parameters - Update description: "启用后将支持自定义账号统计的模型价格"
This commit is contained in:
parent
95f9b27e70
commit
11c4606874
@ -8,23 +8,18 @@ import (
|
|||||||
|
|
||||||
// resolveAccountStatsCost 计算账号统计定价费用。
|
// resolveAccountStatsCost 计算账号统计定价费用。
|
||||||
// 返回 nil 表示不覆盖,使用默认公式(total_cost × account_rate_multiplier)。
|
// 返回 nil 表示不覆盖,使用默认公式(total_cost × account_rate_multiplier)。
|
||||||
//
|
// 仅匹配自定义规则(AccountStatsPricingRules),按数组顺序先命中为准。
|
||||||
// 匹配优先级(先命中为准):
|
// upstreamModel 是最终发往上游的模型 ID,用于匹配自定义规则中的模型定价。
|
||||||
// 1. 自定义规则(AccountStatsPricingRules,按数组顺序遍历)
|
|
||||||
// 2. 渠道已有的模型定价(ApplyPricingToAccountStats 开启时)
|
|
||||||
// 3. nil → 走默认公式
|
|
||||||
func resolveAccountStatsCost(
|
func resolveAccountStatsCost(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
channelService *ChannelService,
|
channelService *ChannelService,
|
||||||
billingService *BillingService,
|
|
||||||
accountID int64,
|
accountID int64,
|
||||||
groupID int64,
|
groupID int64,
|
||||||
billingModel string,
|
upstreamModel string,
|
||||||
tokens UsageTokens,
|
tokens UsageTokens,
|
||||||
requestCount int,
|
requestCount int,
|
||||||
serviceTier string,
|
|
||||||
) *float64 {
|
) *float64 {
|
||||||
if channelService == nil || billingService == nil {
|
if channelService == nil || upstreamModel == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
channel, err := channelService.GetChannelForGroup(ctx, groupID)
|
channel, err := channelService.GetChannelForGroup(ctx, groupID)
|
||||||
@ -33,22 +28,15 @@ func resolveAccountStatsCost(
|
|||||||
}
|
}
|
||||||
|
|
||||||
platform := channelService.GetGroupPlatform(ctx, groupID)
|
platform := channelService.GetGroupPlatform(ctx, groupID)
|
||||||
modelLower := strings.ToLower(billingModel)
|
return tryCustomRules(channel, accountID, groupID, platform, upstreamModel, tokens, requestCount)
|
||||||
|
|
||||||
// 优先级 1:自定义规则
|
|
||||||
if cost := tryCustomRules(channel, accountID, groupID, platform, modelLower, tokens, requestCount); cost != nil {
|
|
||||||
return cost
|
|
||||||
}
|
|
||||||
|
|
||||||
// 优先级 2:渠道已有模型定价
|
|
||||||
return tryChannelPricing(ctx, channelService, groupID, billingModel, tokens, requestCount)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// tryCustomRules 遍历自定义规则,按数组顺序先命中为准。
|
// tryCustomRules 遍历自定义规则,按数组顺序先命中为准。
|
||||||
func tryCustomRules(
|
func tryCustomRules(
|
||||||
channel *Channel, accountID, groupID int64,
|
channel *Channel, accountID, groupID int64,
|
||||||
platform, modelLower string, tokens UsageTokens, requestCount int,
|
platform, model string, tokens UsageTokens, requestCount int,
|
||||||
) *float64 {
|
) *float64 {
|
||||||
|
modelLower := strings.ToLower(model)
|
||||||
for _, rule := range channel.AccountStatsPricingRules {
|
for _, rule := range channel.AccountStatsPricingRules {
|
||||||
if !matchAccountStatsRule(&rule, accountID, groupID) {
|
if !matchAccountStatsRule(&rule, accountID, groupID) {
|
||||||
continue
|
continue
|
||||||
@ -62,18 +50,6 @@ func tryCustomRules(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// tryChannelPricing 使用渠道已有的模型定价计算账号统计费用。
|
|
||||||
func tryChannelPricing(
|
|
||||||
ctx context.Context, channelService *ChannelService,
|
|
||||||
groupID int64, billingModel string, tokens UsageTokens, requestCount int,
|
|
||||||
) *float64 {
|
|
||||||
pricing := channelService.GetChannelModelPricing(ctx, groupID, billingModel)
|
|
||||||
if pricing == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return calculateStatsCost(pricing, tokens, requestCount)
|
|
||||||
}
|
|
||||||
|
|
||||||
// matchAccountStatsRule 检查规则是否匹配指定的 accountID 和 groupID。
|
// matchAccountStatsRule 检查规则是否匹配指定的 accountID 和 groupID。
|
||||||
// 匹配条件:accountID ∈ rule.AccountIDs 或 groupID ∈ rule.GroupIDs。
|
// 匹配条件:accountID ∈ rule.AccountIDs 或 groupID ∈ rule.GroupIDs。
|
||||||
// 如果规则的 AccountIDs 和 GroupIDs 都为空,视为不匹配。
|
// 如果规则的 AccountIDs 和 GroupIDs 都为空,视为不匹配。
|
||||||
|
|||||||
@ -7581,11 +7581,15 @@ func (s *GatewayService) recordUsageCore(ctx context.Context, input *recordUsage
|
|||||||
usageLog := s.buildRecordUsageLog(ctx, input, result, apiKey, user, account, subscription,
|
usageLog := s.buildRecordUsageLog(ctx, input, result, apiKey, user, account, subscription,
|
||||||
requestedModel, multiplier, accountRateMultiplier, billingType, cacheTTLOverridden, cost, opts)
|
requestedModel, multiplier, accountRateMultiplier, billingType, cacheTTLOverridden, cost, opts)
|
||||||
|
|
||||||
// 计算账号统计定价费用
|
// 计算账号统计定价费用(使用最终上游模型匹配自定义规则)
|
||||||
if apiKey.GroupID != nil {
|
if apiKey.GroupID != nil {
|
||||||
|
upstreamModel := result.UpstreamModel
|
||||||
|
if upstreamModel == "" {
|
||||||
|
upstreamModel = result.Model
|
||||||
|
}
|
||||||
usageLog.AccountStatsCost = resolveAccountStatsCost(
|
usageLog.AccountStatsCost = resolveAccountStatsCost(
|
||||||
ctx, s.channelService, s.billingService,
|
ctx, s.channelService,
|
||||||
account.ID, *apiKey.GroupID, billingModel,
|
account.ID, *apiKey.GroupID, upstreamModel,
|
||||||
UsageTokens{
|
UsageTokens{
|
||||||
InputTokens: result.Usage.InputTokens,
|
InputTokens: result.Usage.InputTokens,
|
||||||
OutputTokens: result.Usage.OutputTokens,
|
OutputTokens: result.Usage.OutputTokens,
|
||||||
@ -7593,8 +7597,7 @@ func (s *GatewayService) recordUsageCore(ctx context.Context, input *recordUsage
|
|||||||
CacheReadTokens: result.Usage.CacheReadInputTokens,
|
CacheReadTokens: result.Usage.CacheReadInputTokens,
|
||||||
ImageOutputTokens: result.Usage.ImageOutputTokens,
|
ImageOutputTokens: result.Usage.ImageOutputTokens,
|
||||||
},
|
},
|
||||||
1, // requestCount
|
1, // requestCount
|
||||||
"", // serviceTier: Anthropic 平台不使用 service tier
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4573,12 +4573,16 @@ func (s *OpenAIGatewayService) RecordUsage(ctx context.Context, input *OpenAIRec
|
|||||||
usageLog.SubscriptionID = &subscription.ID
|
usageLog.SubscriptionID = &subscription.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算账号统计定价费用
|
// 计算账号统计定价费用(使用最终上游模型匹配自定义规则)
|
||||||
if apiKey.GroupID != nil {
|
if apiKey.GroupID != nil {
|
||||||
|
statsModel := result.UpstreamModel
|
||||||
|
if statsModel == "" {
|
||||||
|
statsModel = result.Model
|
||||||
|
}
|
||||||
usageLog.AccountStatsCost = resolveAccountStatsCost(
|
usageLog.AccountStatsCost = resolveAccountStatsCost(
|
||||||
ctx, s.channelService, s.billingService,
|
ctx, s.channelService,
|
||||||
account.ID, *apiKey.GroupID, billingModel,
|
account.ID, *apiKey.GroupID, statsModel,
|
||||||
tokens, 1, serviceTier,
|
tokens, 1,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1877,7 +1877,7 @@ export default {
|
|||||||
pricingEntry: 'Pricing Entry',
|
pricingEntry: 'Pricing Entry',
|
||||||
noModels: 'No models added',
|
noModels: 'No models added',
|
||||||
applyPricingToAccountStats: 'Apply Pricing to Account Stats',
|
applyPricingToAccountStats: 'Apply Pricing to Account Stats',
|
||||||
applyPricingToAccountStatsDesc: 'When enabled, account statistics cost will use channel model pricing. Account rate multiplier still applies.',
|
applyPricingToAccountStatsDesc: 'When enabled, custom account stats model pricing rules will be applied.',
|
||||||
accountStatsPricingRules: 'Custom Account Stats Pricing Rules',
|
accountStatsPricingRules: 'Custom Account Stats Pricing Rules',
|
||||||
addRule: 'Add Rule',
|
addRule: 'Add Rule',
|
||||||
noRulesConfigured: 'No custom rules configured. Channel model pricing above will be used.',
|
noRulesConfigured: 'No custom rules configured. Channel model pricing above will be used.',
|
||||||
|
|||||||
@ -1956,7 +1956,7 @@ export default {
|
|||||||
pricingEntry: '定价配置',
|
pricingEntry: '定价配置',
|
||||||
noModels: '未添加模型',
|
noModels: '未添加模型',
|
||||||
applyPricingToAccountStats: '应用模型定价到账号统计',
|
applyPricingToAccountStats: '应用模型定价到账号统计',
|
||||||
applyPricingToAccountStatsDesc: '启用后,账号统计费用将使用渠道模型定价计算。账号自身的统计倍率仍然生效。',
|
applyPricingToAccountStatsDesc: '启用后将支持自定义账号统计的模型价格',
|
||||||
accountStatsPricingRules: '自定义账号统计定价规则',
|
accountStatsPricingRules: '自定义账号统计定价规则',
|
||||||
addRule: '添加规则',
|
addRule: '添加规则',
|
||||||
noRulesConfigured: '未配置自定义规则,将使用上方的模型定价。',
|
noRulesConfigured: '未配置自定义规则,将使用上方的模型定价。',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user