@@ -21,14 +21,16 @@ import (
2121
2222 "github.com/QuantumNous/new-api/dto"
2323 "github.com/QuantumNous/new-api/model"
24+ "github.com/QuantumNous/new-api/setting/billing_setting"
2425 "github.com/QuantumNous/new-api/setting/ratio_setting"
26+ "github.com/samber/lo"
2527
2628 "github.com/gin-gonic/gin"
2729)
2830
2931const (
3032 defaultTimeoutSeconds = 10
31- defaultEndpoint = "/api/ratio_config "
33+ defaultEndpoint = "/api/pricing "
3234 maxConcurrentFetches = 8
3335 maxRatioConfigBytes = 10 << 20 // 10MB
3436 floatEpsilon = 1e-9
@@ -59,14 +61,84 @@ func valuesEqual(a, b interface{}) bool {
5961 return a == b
6062}
6163
62- var ratioTypes = []string {"model_ratio" , "completion_ratio" , "cache_ratio" , "model_price" }
64+ var pricingSyncFields = []string {
65+ "model_ratio" ,
66+ "completion_ratio" ,
67+ "cache_ratio" ,
68+ "create_cache_ratio" ,
69+ "image_ratio" ,
70+ "audio_ratio" ,
71+ "audio_completion_ratio" ,
72+ "model_price" ,
73+ billing_setting .BillingModeField ,
74+ billing_setting .BillingExprField ,
75+ }
76+
77+ var numericPricingSyncFields = map [string ]bool {
78+ "model_ratio" : true ,
79+ "completion_ratio" : true ,
80+ "cache_ratio" : true ,
81+ "create_cache_ratio" : true ,
82+ "image_ratio" : true ,
83+ "audio_ratio" : true ,
84+ "audio_completion_ratio" : true ,
85+ "model_price" : true ,
86+ }
6387
6488type upstreamResult struct {
6589 Name string `json:"name"`
6690 Data map [string ]any `json:"data,omitempty"`
6791 Err string `json:"err,omitempty"`
6892}
6993
94+ func valueMap (value any ) map [string ]any {
95+ switch typed := value .(type ) {
96+ case map [string ]any :
97+ return typed
98+ case map [string ]float64 :
99+ return lo .MapValues (typed , func (value float64 , _ string ) any { return value })
100+ case map [string ]string :
101+ return lo .MapValues (typed , func (value string , _ string ) any { return value })
102+ default :
103+ return nil
104+ }
105+ }
106+
107+ func asFloat64 (value any ) (float64 , bool ) {
108+ switch typed := value .(type ) {
109+ case float64 :
110+ return typed , true
111+ case float32 :
112+ return float64 (typed ), true
113+ case int :
114+ return float64 (typed ), true
115+ case int64 :
116+ return float64 (typed ), true
117+ case json.Number :
118+ parsed , err := typed .Float64 ()
119+ return parsed , err == nil
120+ default :
121+ return 0 , false
122+ }
123+ }
124+
125+ func normalizeSyncValue (field string , value any ) any {
126+ if numericPricingSyncFields [field ] {
127+ if parsed , ok := asFloat64 (value ); ok {
128+ return parsed
129+ }
130+ }
131+ return value
132+ }
133+
134+ func getLocalPricingSyncData () map [string ]any {
135+ data := billing_setting .GetPricingSyncData (map [string ]any (ratio_setting .GetExposedData ()))
136+ data ["image_ratio" ] = ratio_setting .GetImageRatioCopy ()
137+ data ["audio_ratio" ] = ratio_setting .GetAudioRatioCopy ()
138+ data ["audio_completion_ratio" ] = ratio_setting .GetAudioCompletionRatioCopy ()
139+ return data
140+ }
141+
70142func FetchUpstreamRatios (c * gin.Context ) {
71143 var req dto.UpstreamRequest
72144 if err := c .ShouldBindJSON (& req ); err != nil {
@@ -293,7 +365,7 @@ func FetchUpstreamRatios(c *gin.Context) {
293365 if err := common .Unmarshal (body .Data , & type1Data ); err == nil {
294366 // 如果包含至少一个 ratioTypes 字段,则认为是 type1
295367 isType1 := false
296- for _ , rt := range ratioTypes {
368+ for _ , rt := range pricingSyncFields {
297369 if _ , ok := type1Data [rt ]; ok {
298370 isType1 = true
299371 break
@@ -307,11 +379,18 @@ func FetchUpstreamRatios(c *gin.Context) {
307379
308380 // 如果不是 type1,则尝试按 type2 (/api/pricing) 解析
309381 var pricingItems []struct {
310- ModelName string `json:"model_name"`
311- QuotaType int `json:"quota_type"`
312- ModelRatio float64 `json:"model_ratio"`
313- ModelPrice float64 `json:"model_price"`
314- CompletionRatio float64 `json:"completion_ratio"`
382+ ModelName string `json:"model_name"`
383+ QuotaType int `json:"quota_type"`
384+ ModelRatio float64 `json:"model_ratio"`
385+ ModelPrice float64 `json:"model_price"`
386+ CompletionRatio float64 `json:"completion_ratio"`
387+ CacheRatio * float64 `json:"cache_ratio"`
388+ CreateCacheRatio * float64 `json:"create_cache_ratio"`
389+ ImageRatio * float64 `json:"image_ratio"`
390+ AudioRatio * float64 `json:"audio_ratio"`
391+ AudioCompletionRatio * float64 `json:"audio_completion_ratio"`
392+ BillingMode string `json:"billing_mode"`
393+ BillingExpr string `json:"billing_expr"`
315394 }
316395 if err := common .Unmarshal (body .Data , & pricingItems ); err != nil {
317396 logger .LogWarn (c .Request .Context (), "unrecognized data format from " + chItem .Name + ": " + err .Error ())
@@ -321,16 +400,45 @@ func FetchUpstreamRatios(c *gin.Context) {
321400
322401 modelRatioMap := make (map [string ]float64 )
323402 completionRatioMap := make (map [string ]float64 )
403+ cacheRatioMap := make (map [string ]float64 )
404+ createCacheRatioMap := make (map [string ]float64 )
405+ imageRatioMap := make (map [string ]float64 )
406+ audioRatioMap := make (map [string ]float64 )
407+ audioCompletionRatioMap := make (map [string ]float64 )
324408 modelPriceMap := make (map [string ]float64 )
409+ billingModeMap := make (map [string ]string )
410+ billingExprMap := make (map [string ]string )
325411
326412 for _ , item := range pricingItems {
413+ if item .ModelName == "" {
414+ continue
415+ }
416+ if item .BillingMode == billing_setting .BillingModeTieredExpr && strings .TrimSpace (item .BillingExpr ) != "" {
417+ billingModeMap [item .ModelName ] = billing_setting .BillingModeTieredExpr
418+ billingExprMap [item .ModelName ] = item .BillingExpr
419+ }
327420 if item .QuotaType == 1 {
328421 modelPriceMap [item .ModelName ] = item .ModelPrice
329422 } else {
330423 modelRatioMap [item .ModelName ] = item .ModelRatio
331424 // completionRatio 可能为 0,此时也直接赋值,保持与上游一致
332425 completionRatioMap [item .ModelName ] = item .CompletionRatio
333426 }
427+ if item .CacheRatio != nil {
428+ cacheRatioMap [item .ModelName ] = * item .CacheRatio
429+ }
430+ if item .CreateCacheRatio != nil {
431+ createCacheRatioMap [item .ModelName ] = * item .CreateCacheRatio
432+ }
433+ if item .ImageRatio != nil {
434+ imageRatioMap [item .ModelName ] = * item .ImageRatio
435+ }
436+ if item .AudioRatio != nil {
437+ audioRatioMap [item .ModelName ] = * item .AudioRatio
438+ }
439+ if item .AudioCompletionRatio != nil {
440+ audioCompletionRatioMap [item .ModelName ] = * item .AudioCompletionRatio
441+ }
334442 }
335443
336444 converted := make (map [string ]any )
@@ -350,6 +458,21 @@ func FetchUpstreamRatios(c *gin.Context) {
350458 }
351459 converted ["completion_ratio" ] = compAny
352460 }
461+ if len (cacheRatioMap ) > 0 {
462+ converted ["cache_ratio" ] = valueMap (cacheRatioMap )
463+ }
464+ if len (createCacheRatioMap ) > 0 {
465+ converted ["create_cache_ratio" ] = valueMap (createCacheRatioMap )
466+ }
467+ if len (imageRatioMap ) > 0 {
468+ converted ["image_ratio" ] = valueMap (imageRatioMap )
469+ }
470+ if len (audioRatioMap ) > 0 {
471+ converted ["audio_ratio" ] = valueMap (audioRatioMap )
472+ }
473+ if len (audioCompletionRatioMap ) > 0 {
474+ converted ["audio_completion_ratio" ] = valueMap (audioCompletionRatioMap )
475+ }
353476
354477 if len (modelPriceMap ) > 0 {
355478 priceAny := make (map [string ]any , len (modelPriceMap ))
@@ -358,6 +481,12 @@ func FetchUpstreamRatios(c *gin.Context) {
358481 }
359482 converted ["model_price" ] = priceAny
360483 }
484+ if len (billingModeMap ) > 0 {
485+ converted [billing_setting .BillingModeField ] = valueMap (billingModeMap )
486+ }
487+ if len (billingExprMap ) > 0 {
488+ converted [billing_setting .BillingExprField ] = valueMap (billingExprMap )
489+ }
361490
362491 ch <- upstreamResult {Name : uniqueName , Data : converted }
363492 }(chn )
@@ -366,7 +495,7 @@ func FetchUpstreamRatios(c *gin.Context) {
366495 wg .Wait ()
367496 close (ch )
368497
369- localData := ratio_setting . GetExposedData ()
498+ localData := getLocalPricingSyncData ()
370499
371500 var testResults []dto.TestResult
372501 var successfulChannels []struct {
@@ -412,22 +541,16 @@ func buildDifferences(localData map[string]any, successfulChannels []struct {
412541
413542 allModels := make (map [string ]struct {})
414543
415- for _ , ratioType := range ratioTypes {
416- if localRatioAny , ok := localData [ratioType ]; ok {
417- if localRatio , ok := localRatioAny .(map [string ]float64 ); ok {
418- for modelName := range localRatio {
419- allModels [modelName ] = struct {}{}
420- }
421- }
544+ for _ , field := range pricingSyncFields {
545+ for modelName := range valueMap (localData [field ]) {
546+ allModels [modelName ] = struct {}{}
422547 }
423548 }
424549
425550 for _ , channel := range successfulChannels {
426- for _ , ratioType := range ratioTypes {
427- if upstreamRatio , ok := channel .data [ratioType ].(map [string ]any ); ok {
428- for modelName := range upstreamRatio {
429- allModels [modelName ] = struct {}{}
430- }
551+ for _ , field := range pricingSyncFields {
552+ for modelName := range valueMap (channel .data [field ]) {
553+ allModels [modelName ] = struct {}{}
431554 }
432555 }
433556 }
@@ -438,10 +561,10 @@ func buildDifferences(localData map[string]any, successfulChannels []struct {
438561 for _ , channel := range successfulChannels {
439562 confidenceMap [channel .name ] = make (map [string ]bool )
440563
441- modelRatios , hasModelRatio := channel .data ["model_ratio" ].( map [ string ] any )
442- completionRatios , hasCompletionRatio := channel .data ["completion_ratio" ].( map [ string ] any )
564+ modelRatios := valueMap ( channel .data ["model_ratio" ])
565+ completionRatios := valueMap ( channel .data ["completion_ratio" ])
443566
444- if hasModelRatio && hasCompletionRatio {
567+ if len ( modelRatios ) > 0 && len ( completionRatios ) > 0 {
445568 // 遍历所有模型,检查是否满足不可信条件
446569 for modelName := range allModels {
447570 // 默认为可信
@@ -451,12 +574,10 @@ func buildDifferences(localData map[string]any, successfulChannels []struct {
451574 if modelRatioVal , ok := modelRatios [modelName ]; ok {
452575 if completionRatioVal , ok := completionRatios [modelName ]; ok {
453576 // 转换为float64进行比较
454- if modelRatioFloat , ok := modelRatioVal .(float64 ); ok {
455- if completionRatioFloat , ok := completionRatioVal .(float64 ); ok {
456- if modelRatioFloat == 37.5 && completionRatioFloat == 1.0 {
457- confidenceMap [channel.name ][modelName ] = false
458- }
459- }
577+ modelRatioFloat , modelRatioOK := asFloat64 (modelRatioVal )
578+ completionRatioFloat , completionRatioOK := asFloat64 (completionRatioVal )
579+ if modelRatioOK && completionRatioOK && nearlyEqual (modelRatioFloat , 37.5 ) && nearlyEqual (completionRatioFloat , 1.0 ) {
580+ confidenceMap [channel.name ][modelName ] = false
460581 }
461582 }
462583 }
@@ -470,14 +591,10 @@ func buildDifferences(localData map[string]any, successfulChannels []struct {
470591 }
471592
472593 for modelName := range allModels {
473- for _ , ratioType := range ratioTypes {
594+ for _ , ratioType := range pricingSyncFields {
474595 var localValue interface {} = nil
475- if localRatioAny , ok := localData [ratioType ]; ok {
476- if localRatio , ok := localRatioAny .(map [string ]float64 ); ok {
477- if val , exists := localRatio [modelName ]; exists {
478- localValue = val
479- }
480- }
596+ if val , exists := valueMap (localData [ratioType ])[modelName ]; exists {
597+ localValue = normalizeSyncValue (ratioType , val )
481598 }
482599
483600 upstreamValues := make (map [string ]interface {})
@@ -488,16 +605,14 @@ func buildDifferences(localData map[string]any, successfulChannels []struct {
488605 for _ , channel := range successfulChannels {
489606 var upstreamValue interface {} = nil
490607
491- if upstreamRatio , ok := channel .data [ratioType ].(map [string ]any ); ok {
492- if val , exists := upstreamRatio [modelName ]; exists {
493- upstreamValue = val
494- hasUpstreamValue = true
608+ if val , exists := valueMap (channel .data [ratioType ])[modelName ]; exists {
609+ upstreamValue = normalizeSyncValue (ratioType , val )
610+ hasUpstreamValue = true
495611
496- if localValue != nil && ! valuesEqual (localValue , val ) {
497- hasDifference = true
498- } else if valuesEqual (localValue , val ) {
499- upstreamValue = "same"
500- }
612+ if localValue != nil && ! valuesEqual (localValue , upstreamValue ) {
613+ hasDifference = true
614+ } else if valuesEqual (localValue , upstreamValue ) {
615+ upstreamValue = "same"
501616 }
502617 }
503618 if upstreamValue == nil && localValue == nil {
0 commit comments