tiered_billing.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. package billing_setting
  2. import (
  3. "fmt"
  4. "sync"
  5. "github.com/QuantumNous/new-api/common"
  6. "github.com/QuantumNous/new-api/pkg/billingexpr"
  7. )
  8. var (
  9. mu sync.RWMutex
  10. // model -> "ratio" | "tiered_expr"
  11. billingModeMap = make(map[string]string)
  12. // model -> expr string (authored by frontend, stored directly)
  13. billingExprMap = make(map[string]string)
  14. )
  15. const (
  16. BillingModeRatio = "ratio"
  17. BillingModeTieredExpr = "tiered_expr"
  18. )
  19. // ---------------------------------------------------------------------------
  20. // Read accessors (hot path, must be fast)
  21. // ---------------------------------------------------------------------------
  22. func GetBillingMode(model string) string {
  23. mu.RLock()
  24. defer mu.RUnlock()
  25. if mode, ok := billingModeMap[model]; ok {
  26. return mode
  27. }
  28. return BillingModeRatio
  29. }
  30. func GetBillingExpr(model string) (string, bool) {
  31. mu.RLock()
  32. defer mu.RUnlock()
  33. expr, ok := billingExprMap[model]
  34. return expr, ok
  35. }
  36. func UpdateBillingModeByJSONString(jsonStr string) error {
  37. var m map[string]string
  38. if err := common.Unmarshal([]byte(jsonStr), &m); err != nil {
  39. return fmt.Errorf("parse ModelBillingMode: %w", err)
  40. }
  41. for k, v := range m {
  42. if v != BillingModeRatio && v != BillingModeTieredExpr {
  43. return fmt.Errorf("invalid billing mode %q for model %q", v, k)
  44. }
  45. }
  46. mu.Lock()
  47. billingModeMap = m
  48. mu.Unlock()
  49. return nil
  50. }
  51. func UpdateBillingExprByJSONString(jsonStr string) error {
  52. var m map[string]string
  53. if err := common.Unmarshal([]byte(jsonStr), &m); err != nil {
  54. return fmt.Errorf("parse ModelBillingExpr: %w", err)
  55. }
  56. for model, exprStr := range m {
  57. if _, err := billingexpr.CompileFromCache(exprStr); err != nil {
  58. return fmt.Errorf("model %q: %w", model, err)
  59. }
  60. if err := smokeTestExpr(exprStr); err != nil {
  61. return fmt.Errorf("model %q smoke test: %w", model, err)
  62. }
  63. }
  64. mu.Lock()
  65. billingExprMap = m
  66. mu.Unlock()
  67. billingexpr.InvalidateCache()
  68. return nil
  69. }
  70. // ---------------------------------------------------------------------------
  71. // JSON serializers (for OptionMap / API response)
  72. // ---------------------------------------------------------------------------
  73. func BillingMode2JSONString() string {
  74. mu.RLock()
  75. defer mu.RUnlock()
  76. b, err := common.Marshal(billingModeMap)
  77. if err != nil {
  78. return "{}"
  79. }
  80. return string(b)
  81. }
  82. func BillingExpr2JSONString() string {
  83. mu.RLock()
  84. defer mu.RUnlock()
  85. b, err := common.Marshal(billingExprMap)
  86. if err != nil {
  87. return "{}"
  88. }
  89. return string(b)
  90. }
  91. func smokeTestExpr(exprStr string) error {
  92. vectors := []billingexpr.TokenParams{
  93. {P: 0, C: 0},
  94. {P: 1000, C: 1000},
  95. {P: 100000, C: 100000},
  96. {P: 1000000, C: 1000000},
  97. }
  98. requests := []billingexpr.RequestInput{
  99. {},
  100. {
  101. Headers: map[string]string{
  102. "anthropic-beta": "fast-mode-2026-02-01",
  103. },
  104. Body: []byte(`{"service_tier":"fast","stream_options":{"include_usage":true},"messages":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21]}`),
  105. },
  106. }
  107. for _, v := range vectors {
  108. for _, request := range requests {
  109. result, _, err := billingexpr.RunExprWithRequest(exprStr, v, request)
  110. if err != nil {
  111. return fmt.Errorf("vector {p=%g, c=%g}: run failed: %w", v.P, v.C, err)
  112. }
  113. if result < 0 {
  114. return fmt.Errorf("vector {p=%g, c=%g}: result %f < 0", v.P, v.C, result)
  115. }
  116. }
  117. }
  118. return nil
  119. }