diff --git a/types/model_pricing.go b/types/model_pricing.go new file mode 100644 index 0000000..3d3a301 --- /dev/null +++ b/types/model_pricing.go @@ -0,0 +1,133 @@ +package types + +import ( + "fmt" + + "github.com/QuantumNous/new-api/common" +) + +const ( + PricingSchemaVersion = 1 + + PricingScopeModel = "model" + + BillingUnitPerCall = "per_call" + BillingUnitPer1MTokens = "per_1m_tokens" + + PreconsumeStrategyExact = "exact" + PreconsumeStrategyMinimum = "minimum" + + PricingFallbackReject = "reject" + PricingFallbackMax = "max" + PricingFallbackDefault = "default" + + PricingRowSourceManual = "manual" + PricingRowSourceGenerated = "generated" + + BillingModeMatrix = "matrix" +) + +type PricingConfig struct { + SchemaVersion int `json:"schema_version"` + Scope string `json:"scope"` + BillingUnit string `json:"billing_unit,omitempty"` + PreconsumeStrategy string `json:"preconsume_strategy,omitempty"` + Dimensions []PricingDimension `json:"dimensions"` + Table []PricingRow `json:"table"` + Fallback PricingFallback `json:"fallback"` + GeneratorSnapshot map[string]any `json:"generator_snapshot,omitempty"` +} + +type PricingDimension struct { + Key string `json:"key"` + Source string `json:"source"` + Type string `json:"type"` + Optional bool `json:"optional,omitempty"` + Default any `json:"default"` +} + +type PricingRow map[string]any + +type PricingFallback struct { + Strategy string `json:"strategy"` + DefaultPrice float64 `json:"default_price,omitempty"` +} + +type OriginPricingSnapshot struct { + BillingMode string + BillingUnit string + PriceUSD float64 + TokenUnitPriceUSD float64 + Snapshot map[string]any + OtherRatios map[string]float64 + PerCallBilling bool +} + +func ClonePricingConfig(src *PricingConfig) *PricingConfig { + if src == nil { + return nil + } + return &PricingConfig{ + SchemaVersion: src.SchemaVersion, + Scope: src.Scope, + BillingUnit: src.BillingUnit, + PreconsumeStrategy: src.PreconsumeStrategy, + Dimensions: append([]PricingDimension(nil), src.Dimensions...), + Table: ClonePricingRows(src.Table), + Fallback: src.Fallback, + GeneratorSnapshot: CloneMapAny(src.GeneratorSnapshot), + } +} + +func ClonePricingRows(src []PricingRow) []PricingRow { + if src == nil { + return nil + } + dst := make([]PricingRow, len(src)) + for i := range src { + dst[i] = PricingRow(CloneMapAny(map[string]any(src[i]))) + } + return dst +} + +func CloneMapAny(src map[string]any) map[string]any { + if src == nil { + return nil + } + bytes, err := common.Marshal(src) + if err != nil { + dst := make(map[string]any, len(src)) + for k, v := range src { + dst[k] = v + } + return dst + } + var dst map[string]any + if err := common.Unmarshal(bytes, &dst); err != nil { + panic(fmt.Sprintf("clone map failed after successful marshal: %v", err)) + } + return dst +} + +func CloneRatios(src map[string]float64) map[string]float64 { + if src == nil { + return nil + } + dst := make(map[string]float64, len(src)) + for k, v := range src { + dst[k] = v + } + return dst +} + +func NormalizePricingConfigDefaults(cfg *PricingConfig) { + if cfg == nil { + return + } + if cfg.BillingUnit == "" { + cfg.BillingUnit = BillingUnitPerCall + } + if cfg.PreconsumeStrategy == "" { + cfg.PreconsumeStrategy = PreconsumeStrategyExact + } +} diff --git a/types/price_data.go b/types/price_data.go index 6076f3b..c554282 100644 --- a/types/price_data.go +++ b/types/price_data.go @@ -2,7 +2,8 @@ package types import "fmt" -// ClaudeCacheCreation1hMultiplier 1小时缓存写入价格相对于5分钟的比例 +// ClaudeCacheCreation1hMultiplier is the 1-hour cache write price multiplier +// relative to the 5-minute cache write price. // https://docs.claude.com/en/docs/build-with-claude/prompt-caching#1-hour-cache-duration const ClaudeCacheCreation1hMultiplier = 6 / 3.75 @@ -12,6 +13,33 @@ type GroupRatioInfo struct { HasSpecialRatio bool } +type PricingMode string + +const ( + PricingModeMatched PricingMode = "matched" + PricingModeFallbackMax PricingMode = "fallback_max" + PricingModeFallbackDefault PricingMode = "fallback_default" + PricingModeLegacy PricingMode = "legacy" + PricingModeRemix PricingMode = "remix" +) + +type PricingDecision struct { + Mode PricingMode + BillingMode string + BillingUnit string + PriceUSD float64 + TokenUnitPriceUSD float64 + Quota int + GroupRatioInfo GroupRatioInfo + Snapshot map[string]any + FreeModel bool + LegacyModelPrice float64 + OtherRatios map[string]float64 + UsePrice bool + PerCallBilling bool + Finalized bool +} + type PriceData struct { FreeModel bool ModelPrice float64 @@ -26,13 +54,14 @@ type PriceData struct { AudioCompletionRatio float64 OtherRatios map[string]float64 UsePrice bool - Quota int // 按次计费的最终额度(MJ / Task) - QuotaToPreConsume int // 按量计费的预消耗额度 + Quota int + QuotaToPreConsume int GroupRatioInfo GroupRatioInfo + PricingDecision *PricingDecision } -// ApplyChannelPricingRatios 将渠道定价的扩展比率应用到 PriceData(非零值覆盖) -// cacheRatio, cacheCreationRatio, imageRatio, audioRatio, audioCompletionRatio +// ApplyChannelPricingRatios applies channel-level pricing ratios when the +// channel value is non-zero. func (p *PriceData) ApplyChannelPricingRatios(cacheRatio, cacheCreationRatio, imageRatio, audioRatio, audioCompletionRatio float64) { if cacheRatio != 0 { p.CacheRatio = cacheRatio