96 lines
4.1 KiB
Go
96 lines
4.1 KiB
Go
package common
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
)
|
|
|
|
// ErrModelPricingNotConfigured 模型未配置定价(fail-closed 哨兵错误)。
|
|
// billing 层预扣估价发现零价定价时返回,handler 层用 errors.Is 识别后
|
|
// 返回明确的请求错误,而非误导性的"余额不足"。
|
|
var ErrModelPricingNotConfigured = errors.New("model pricing not configured")
|
|
|
|
// BillingProvider 计费接口
|
|
// 实现在 internal/logic/billing/ 中,通过接口解耦 relay 层和 GoFrame
|
|
type BillingProvider interface {
|
|
// PreDeduct 预扣费用
|
|
// 检查余额 → 冻结预扣金额 → 返回是否成功
|
|
PreDeduct(ctx context.Context, tenantID int64, modelName string, inputTokens, maxTokens int, isStream bool, requestID string) (preDeductAmount float64, err error)
|
|
|
|
// Settle 结算费用(成功请求)。billAt 为请求受理时刻(时段定价按该时刻评估)
|
|
Settle(ctx context.Context, tenantID, userID, apiKeyID, channelID int64, modelName, requestID, relayMode string, usage *Usage, preDeductAmount float64, projectID int64, billAt time.Time) (*SettlementResult, error)
|
|
|
|
// SettleWithUsage 完整 Usage 结算(含 cache token + 计费快照)
|
|
SettleWithUsage(ctx context.Context, tenantID, userID, apiKeyID, channelID int64,
|
|
modelName, requestID, relayMode string,
|
|
usage *Usage, preDeductAmount float64, relayInfo *RelayInfo) *SettlementResult
|
|
|
|
// SettleFailed 失败请求结算(退还预扣)
|
|
SettleFailed(ctx context.Context, tenantID int64, requestID string, preDeductAmount float64) error
|
|
|
|
// SettleStreamInterrupted 流式中断结算。billAt 为请求受理时刻(时段定价按该时刻评估)
|
|
SettleStreamInterrupted(ctx context.Context, tenantID, userID, apiKeyID, channelID int64, modelName, requestID, relayMode string, usage *Usage, preDeductAmount float64, projectID int64, billAt time.Time) (*SettlementResult, error)
|
|
|
|
// CheckRateLimit QPS 限流检查
|
|
CheckRateLimit(ctx context.Context, tenantID, userID, apiKeyID int64, keyQPS int) (allowed bool, limitLevel string, limit int, remaining int, resetAt int64)
|
|
|
|
// AcquireConcurrent 并发限制检查(含租户级 + 模型级)
|
|
AcquireConcurrent(ctx context.Context, tenantID, userID, apiKeyID int64, modelName string) bool
|
|
|
|
// ReleaseConcurrent 释放并发限制(含租户级 + 模型级)
|
|
ReleaseConcurrent(ctx context.Context, tenantID, userID, apiKeyID int64, modelName string)
|
|
|
|
// AcquireApiKeyConcurrent 获取 API Key 级并发许可
|
|
AcquireApiKeyConcurrent(ctx context.Context, apiKeyID int64, limit int) bool
|
|
|
|
// ReleaseApiKeyConcurrent 释放 API Key 级并发许可
|
|
ReleaseApiKeyConcurrent(ctx context.Context, apiKeyID int64)
|
|
|
|
// CheckScope 检查 API Key scope
|
|
CheckScope(scope string, relayMode string) bool
|
|
|
|
// CheckIPWhitelist 检查 IP 白名单
|
|
CheckIPWhitelist(whitelist string, clientIP string) bool
|
|
|
|
// CheckMemberQuota 检查成员额度是否足够
|
|
CheckMemberQuota(ctx context.Context, tenantID, userID int64, preDeductAmount float64) error
|
|
|
|
// CheckApiKeyQuota 检查 API Key 额度是否足够
|
|
CheckApiKeyQuota(ctx context.Context, apiKeyID int64, preDeductAmount float64) error
|
|
|
|
// IncrMemberQuotaUsed 结算后累加成员已用额度
|
|
IncrMemberQuotaUsed(ctx context.Context, tenantID, userID int64, amount float64)
|
|
|
|
// IncrApiKeyQuotaUsed 结算后累加 API Key 已用额度
|
|
IncrApiKeyQuotaUsed(ctx context.Context, apiKeyID int64, amount float64)
|
|
}
|
|
|
|
// RateLimitInfo 限流信息(用于设置响应头)
|
|
type RateLimitInfo struct {
|
|
Limit int
|
|
Remaining int
|
|
ResetAt int64
|
|
}
|
|
|
|
// SettlementResult 结算结果(从 billing 层返回)
|
|
type SettlementResult struct {
|
|
PreDeductAmount float64
|
|
BaseCost float64
|
|
ActualCost float64
|
|
RefundAmount float64
|
|
SupplementAmount float64
|
|
BillingRecordID int64
|
|
BillingSnapshot string
|
|
BillingSummary string
|
|
InputCost float64
|
|
OutputCost float64
|
|
CacheCreationCost float64
|
|
CacheReadCost float64
|
|
TotalCost float64
|
|
BillingMode string
|
|
BillingSource string
|
|
RateMultiplier float64
|
|
DuplicateSkip bool // 幂等跳过:本次为重复结算(未扣款未写账单),调用方不得再累加成员/Key 额度
|
|
}
|