Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 

183 řádky
7.4 KiB

  1. package ratio_setting
  2. import (
  3. "testing"
  4. "github.com/QuantumNous/new-api/types"
  5. "github.com/stretchr/testify/require"
  6. )
  7. func validPricingConfig() *types.PricingConfig {
  8. return &types.PricingConfig{
  9. SchemaVersion: types.PricingSchemaVersion,
  10. Scope: types.PricingScopeModel,
  11. Dimensions: []types.PricingDimension{
  12. {Key: "resolution", Source: "request.resolution", Type: "string", Default: nil},
  13. {Key: "duration", Source: "request.duration", Type: "number", Default: nil},
  14. },
  15. Table: []types.PricingRow{
  16. {"resolution": "720P", "duration": float64(5), "price": float64(0.10), "source": types.PricingRowSourceManual},
  17. {"resolution": "720P", "duration": float64(10), "price": float64(0.20), "source": types.PricingRowSourceGenerated},
  18. },
  19. Fallback: types.PricingFallback{Strategy: types.PricingFallbackReject},
  20. }
  21. }
  22. func TestValidatePricingConfigValidDefaults(t *testing.T) {
  23. cfg := validPricingConfig()
  24. require.NoError(t, ValidatePricingConfig("hailuo-test", cfg))
  25. require.Equal(t, types.BillingUnitPerCall, cfg.BillingUnit)
  26. require.Equal(t, types.PreconsumeStrategyExact, cfg.PreconsumeStrategy)
  27. }
  28. func TestValidatePricingConfigSchemaRules(t *testing.T) {
  29. tests := []struct {
  30. name string
  31. mutate func(*types.PricingConfig)
  32. wantErr string
  33. }{
  34. {"schema_version", func(c *types.PricingConfig) { c.SchemaVersion = 2 }, "schema_version"},
  35. {"scope", func(c *types.PricingConfig) { c.Scope = "channel" }, "scope"},
  36. {"dimensions", func(c *types.PricingConfig) { c.Dimensions = nil }, "dimensions"},
  37. {"table", func(c *types.PricingConfig) { c.Table = nil }, "table"},
  38. {"fallback", func(c *types.PricingConfig) { c.Fallback.Strategy = "nearest" }, "fallback.strategy"},
  39. {"default_price", func(c *types.PricingConfig) {
  40. c.Fallback = types.PricingFallback{Strategy: types.PricingFallbackDefault, DefaultPrice: -1}
  41. }, "default_price"},
  42. {"per_call_strategy", func(c *types.PricingConfig) {
  43. c.BillingUnit = types.BillingUnitPerCall
  44. c.PreconsumeStrategy = types.PreconsumeStrategyMinimum
  45. }, "per_call"},
  46. {"usage_strategy", func(c *types.PricingConfig) {
  47. c.BillingUnit = types.BillingUnitPer1MTokens
  48. c.PreconsumeStrategy = types.PreconsumeStrategyExact
  49. }, "per_1m_tokens"},
  50. {"usage_allowlist", func(c *types.PricingConfig) {
  51. c.BillingUnit = types.BillingUnitPer1MTokens
  52. c.PreconsumeStrategy = types.PreconsumeStrategyMinimum
  53. }, "does not support"},
  54. }
  55. for _, tt := range tests {
  56. t.Run(tt.name, func(t *testing.T) {
  57. cfg := validPricingConfig()
  58. tt.mutate(cfg)
  59. err := ValidatePricingConfig("hailuo-test", cfg)
  60. require.Error(t, err)
  61. require.Contains(t, err.Error(), tt.wantErr)
  62. })
  63. }
  64. }
  65. func TestValidatePricingConfigUsageAllowlist(t *testing.T) {
  66. cfg := validPricingConfig()
  67. cfg.BillingUnit = types.BillingUnitPer1MTokens
  68. cfg.PreconsumeStrategy = types.PreconsumeStrategyMinimum
  69. require.NoError(t, ValidatePricingConfig("seedance-2", cfg))
  70. }
  71. func TestValidatePricingConfigAllowsDerivedVideoInput(t *testing.T) {
  72. cfg := &types.PricingConfig{
  73. SchemaVersion: types.PricingSchemaVersion,
  74. Scope: types.PricingScopeModel,
  75. BillingUnit: types.BillingUnitPer1MTokens,
  76. PreconsumeStrategy: types.PreconsumeStrategyMinimum,
  77. Dimensions: []types.PricingDimension{
  78. {Key: "video_input", Source: "derived.video_input", Type: "boolean", Default: nil},
  79. },
  80. Table: []types.PricingRow{
  81. {"video_input": false, "price": float64(46), "source": types.PricingRowSourceManual},
  82. {"video_input": true, "price": float64(28), "source": types.PricingRowSourceManual},
  83. },
  84. Fallback: types.PricingFallback{Strategy: types.PricingFallbackReject},
  85. }
  86. require.NoError(t, ValidatePricingConfig("seedance-2", cfg))
  87. }
  88. func TestValidatePricingConfigDimensionRules(t *testing.T) {
  89. tests := []struct {
  90. name string
  91. dim types.PricingDimension
  92. wantErr string
  93. }{
  94. {"bad_key", types.PricingDimension{Key: "Bad", Source: "request.x", Type: "string", Default: nil}, "invalid"},
  95. {"reserved", types.PricingDimension{Key: "price", Source: "request.x", Type: "string", Default: nil}, "reserved"},
  96. {"bad_type", types.PricingDimension{Key: "quality", Source: "request.x", Type: "object", Default: nil}, "type"},
  97. {"bad_source", types.PricingDimension{Key: "quality", Source: "response.usage.total_tokens", Type: "number", Default: nil}, "source"},
  98. {"optional_default_missing", types.PricingDimension{Key: "quality", Source: "request.quality", Type: "string", Optional: true, Default: nil}, "default required"},
  99. {"optional_default_type", types.PricingDimension{Key: "quality", Source: "request.quality", Type: "number", Optional: true, Default: "fast"}, "default type"},
  100. {"required_default", types.PricingDimension{Key: "quality", Source: "request.quality", Type: "string", Default: "fast"}, "default must be null"},
  101. }
  102. for _, tt := range tests {
  103. t.Run(tt.name, func(t *testing.T) {
  104. cfg := validPricingConfig()
  105. cfg.Dimensions = []types.PricingDimension{tt.dim}
  106. cfg.Table = []types.PricingRow{{tt.dim.Key: "x", "price": float64(1), "source": types.PricingRowSourceManual}}
  107. if tt.dim.Type == "number" {
  108. cfg.Table[0][tt.dim.Key] = float64(1)
  109. }
  110. err := ValidatePricingConfig("hailuo-test", cfg)
  111. require.Error(t, err)
  112. require.Contains(t, err.Error(), tt.wantErr)
  113. })
  114. }
  115. }
  116. func TestValidatePricingConfigRowRules(t *testing.T) {
  117. tests := []struct {
  118. name string
  119. mutate func(*types.PricingConfig)
  120. wantErr string
  121. }{
  122. {"missing_dim", func(c *types.PricingConfig) { delete(c.Table[0], "duration") }, "missing dimension"},
  123. {"extra_field", func(c *types.PricingConfig) { c.Table[0]["price_formula"] = "x" }, "unexpected field"},
  124. {"bad_dim_type", func(c *types.PricingConfig) { c.Table[0]["duration"] = "five" }, "value type"},
  125. {"bad_price", func(c *types.PricingConfig) { c.Table[0]["price"] = -1.0 }, "price"},
  126. {"bad_source", func(c *types.PricingConfig) { c.Table[0]["source"] = "api" }, "source"},
  127. {"duplicate", func(c *types.PricingConfig) {
  128. c.Table = append(c.Table, types.PricingRow(types.CloneMapAny(c.Table[0])))
  129. }, "duplicate"},
  130. {"ambiguous", func(c *types.PricingConfig) {
  131. c.Dimensions = []types.PricingDimension{
  132. {Key: "res", Source: "request.res", Type: "string", Default: nil},
  133. {Key: "dur", Source: "request.dur", Type: "number", Default: nil},
  134. }
  135. c.Table = []types.PricingRow{
  136. {"res": "*", "dur": float64(5), "price": float64(0.10), "source": types.PricingRowSourceManual},
  137. {"res": "720P", "dur": "*", "price": float64(0.12), "source": types.PricingRowSourceManual},
  138. }
  139. }, "ambiguous"},
  140. }
  141. for _, tt := range tests {
  142. t.Run(tt.name, func(t *testing.T) {
  143. cfg := validPricingConfig()
  144. tt.mutate(cfg)
  145. err := ValidatePricingConfig("hailuo-test", cfg)
  146. require.Error(t, err)
  147. require.Contains(t, err.Error(), tt.wantErr)
  148. })
  149. }
  150. }
  151. func TestModelPricingRulesRoundTripAndClone(t *testing.T) {
  152. require.NoError(t, UpdateModelPricingRulesByJSONString(`{}`))
  153. cfg := validPricingConfig()
  154. require.NoError(t, SetPricingConfig("hailuo-test", cfg))
  155. cfg.Table[0]["price"] = float64(999)
  156. got := GetPricingConfig("hailuo-test")
  157. require.NotNil(t, got)
  158. require.Equal(t, float64(0.10), got.Table[0]["price"])
  159. jsonStr := ModelPricingRules2JSONString()
  160. require.Contains(t, jsonStr, "hailuo-test")
  161. require.NoError(t, UpdateModelPricingRulesByJSONString(jsonStr))
  162. require.NotNil(t, GetPricingConfig("hailuo-test"))
  163. require.NoError(t, DeletePricingConfig("hailuo-test"))
  164. require.Nil(t, GetPricingConfig("hailuo-test"))
  165. }