feat: publish sanitized custom.97 source
This commit is contained in:
1022 files changed
+69903
-19207
No files matched your search
@@ -1,5 +1,32 @@
|
||||
package billing_setting
|
||||
|
||||
import "github.com/QuantumNous/new-api/constant"
|
||||
|
||||
// deepSeekPeak is the legacy built-in schedule used by the original DeepSeek
|
||||
// model expressions. Provider-specific pricing below uses deepSeekPeakAllDays.
|
||||
const deepSeekPeak = `weekday("Asia/Shanghai") >= 1 && weekday("Asia/Shanghai") <= 5 && ((hour("Asia/Shanghai") >= 9 && hour("Asia/Shanghai") < 12) || (hour("Asia/Shanghai") >= 14 && hour("Asia/Shanghai") < 18))`
|
||||
|
||||
// deepSeekPeakAllDays follows the actual upstream schedule configured for the
|
||||
// official token group: the two peak windows apply every day of the week.
|
||||
const deepSeekPeakAllDays = `((hour("Asia/Shanghai") >= 9 && hour("Asia/Shanghai") < 12) || (hour("Asia/Shanghai") >= 14 && hour("Asia/Shanghai") < 18))`
|
||||
|
||||
const deepSeekFlashExpr = deepSeekPeak + ` ? tier("peak", p * 0.3 + cr * 0.006 + c * 1.2) : tier("off_peak", p * 0.15 + cr * 0.003 + c * 0.6)`
|
||||
|
||||
const (
|
||||
// DeepSeekOfficialGroup is the user-token group whose official pricing is
|
||||
// selected for compatible/custom channels as well as native DeepSeek ones.
|
||||
DeepSeekOfficialGroup = "Deepseek官Key"
|
||||
DeepSeekOfficialGroupRatio = 0.45
|
||||
)
|
||||
|
||||
// deepSeekFlashOfficialExpr uses the official Flash base prices. The cache
|
||||
// prices are raised slightly so that the shared 0.45 group ratio still covers
|
||||
// the upstream cache-read prices shown for the official key.
|
||||
const deepSeekFlashOfficialExpr = deepSeekPeakAllDays + ` ? tier("peak", p * 2 + cr * 0.066667 + c * 8) : tier("off_peak", p * 1 + cr * 0.033334 + c * 4)`
|
||||
|
||||
// deepSeekV4ProOfficialExpr follows the official DeepSeek V4 Pro base prices.
|
||||
const deepSeekV4ProOfficialExpr = deepSeekPeakAllDays + ` ? tier("peak", p * 9 + cr * 0.30 + c * 27) : tier("off_peak", p * 4.5 + cr * 0.15 + c * 13.5)`
|
||||
|
||||
// Built-in token prices use actual USD per million tokens. Keep new model
|
||||
// defaults here instead of splitting them across the legacy ratio tables.
|
||||
var builtinBillingExpr = map[string]string{
|
||||
@@ -13,4 +40,33 @@ var builtinBillingExpr = map[string]string{
|
||||
// Do not infer service-tier discounts from incoming request parameters:
|
||||
// channels filter service_tier by default, so it may not reach the upstream.
|
||||
"gpt-6-astra": `len <= 272000 ? tier("standard", p * 10 + c * 50 + cr * 1 + cc * 12.5) : tier("long_context", p * 20 + c * 75 + cr * 2 + cc * 25)`,
|
||||
// https://api-docs.deepseek.com/quick_start/pricing (2026-10-17). Off-peak
|
||||
// is half the peak price. deepseek-v4-flash and deepseek-v4-flash-vision-exp
|
||||
// run on V4.1 Flash and bill at the Flash price. Images bill as input
|
||||
// tokens, and there is no cache-write price.
|
||||
"deepseek-flash": deepSeekFlashExpr,
|
||||
"deepseek-v4-flash": deepSeekFlashExpr,
|
||||
"deepseek-v4-flash-vision-exp": deepSeekFlashExpr,
|
||||
"deepseek-v4-pro": deepSeekPeak + ` ? tier("peak", p * 1.32 + cr * 0.044 + c * 3.96) : tier("off_peak", p * 0.66 + cr * 0.022 + c * 1.98)`,
|
||||
// The Search and Fetch API routing names: $0.75 per 1,000 successful
|
||||
// requests each, the same pricing page. The calls carry no tokens.
|
||||
constant.DeepSeekSearchModel: `tier("request", fixed(0.00075))`,
|
||||
constant.DeepSeekFetchModel: `tier("request", fixed(0.00075))`,
|
||||
}
|
||||
|
||||
// GetChannelBillingExpr returns provider-specific built-in pricing that must
|
||||
// not replace the model's regular or group pricing for other channels.
|
||||
func GetChannelBillingExpr(channelType int, model string) (string, bool) {
|
||||
if channelType != constant.ChannelTypeDeepSeek {
|
||||
return "", false
|
||||
}
|
||||
|
||||
switch model {
|
||||
case "deepseek-flash", "deepseek-v4.1-flash", "deepseek-v4-flash", "deepseek-v4-flash-vision-exp":
|
||||
return deepSeekFlashOfficialExpr, true
|
||||
case "deepseek-v4-pro":
|
||||
return deepSeekV4ProOfficialExpr, true
|
||||
default:
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,10 @@ package billing_setting_test
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/constant"
|
||||
"github.com/QuantumNous/new-api/controller"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
"github.com/QuantumNous/new-api/pkg/billingexpr"
|
||||
@@ -18,6 +20,71 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDeepSeekOfficialChannelBillingExprIsChannelScoped(t *testing.T) {
|
||||
for _, model := range []string{"deepseek-flash", "deepseek-v4.1-flash", "deepseek-v4-flash", "deepseek-v4-flash-vision-exp"} {
|
||||
expression, ok := billing_setting.GetChannelBillingExpr(constant.ChannelTypeDeepSeek, model)
|
||||
require.True(t, ok, model)
|
||||
_, err := billingexpr.CompileFromCache(expression)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, expression, `p * 2`, model)
|
||||
assert.Contains(t, expression, `p * 1`, model)
|
||||
assert.Contains(t, expression, `cr * 0.066667`, model)
|
||||
assert.Contains(t, expression, `cr * 0.033334`, model)
|
||||
assert.NotContains(t, expression, `weekday("Asia/Shanghai")`, model)
|
||||
}
|
||||
|
||||
proExpression, ok := billing_setting.GetChannelBillingExpr(constant.ChannelTypeDeepSeek, "deepseek-v4-pro")
|
||||
require.True(t, ok)
|
||||
_, err := billingexpr.CompileFromCache(proExpression)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, proExpression, `p * 9`)
|
||||
assert.Contains(t, proExpression, `p * 4.5`)
|
||||
assert.Contains(t, proExpression, `cr * 0.30`)
|
||||
assert.Contains(t, proExpression, `cr * 0.15`)
|
||||
assert.Contains(t, proExpression, `c * 27`)
|
||||
assert.Contains(t, proExpression, `c * 13.5`)
|
||||
assert.NotContains(t, proExpression, `weekday("Asia/Shanghai")`)
|
||||
|
||||
_, ok = billing_setting.GetChannelBillingExpr(constant.ChannelTypeOpenAI, "deepseek-v4.1-flash")
|
||||
assert.False(t, ok)
|
||||
}
|
||||
|
||||
func TestDeepSeekOfficialChannelPricingUsesAllDayWindows(t *testing.T) {
|
||||
expression, ok := billing_setting.GetChannelBillingExpr(constant.ChannelTypeDeepSeek, "deepseek-v4.1-flash")
|
||||
require.True(t, ok)
|
||||
params := billingexpr.TokenParams{P: 1_000_000, CR: 1_000_000, C: 1_000_000, Len: 1_000_000}
|
||||
snapshot := &billingexpr.BillingSnapshot{ExprString: expression, ExprHash: billingexpr.ExprHashString(expression), GroupRatio: 0.45, QuotaPerUnit: 1_000_000}
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
at time.Time
|
||||
tier string
|
||||
want int
|
||||
}{
|
||||
{name: "monday peak", at: time.Date(2026, 10, 26, 2, 0, 0, 0, time.UTC), tier: "peak", want: 4_530_000},
|
||||
{name: "monday off peak", at: time.Date(2026, 10, 26, 5, 0, 0, 0, time.UTC), tier: "off_peak", want: 2_265_000},
|
||||
{name: "tuesday peak", at: time.Date(2026, 10, 27, 2, 0, 0, 0, time.UTC), tier: "peak", want: 4_530_000},
|
||||
{name: "tuesday off peak", at: time.Date(2026, 10, 27, 5, 0, 0, 0, time.UTC), tier: "off_peak", want: 2_265_000},
|
||||
{name: "wednesday peak", at: time.Date(2026, 10, 28, 2, 0, 0, 0, time.UTC), tier: "peak", want: 4_530_000},
|
||||
{name: "wednesday off peak", at: time.Date(2026, 10, 28, 5, 0, 0, 0, time.UTC), tier: "off_peak", want: 2_265_000},
|
||||
{name: "thursday peak", at: time.Date(2026, 10, 29, 2, 0, 0, 0, time.UTC), tier: "peak", want: 4_530_000},
|
||||
{name: "thursday off peak", at: time.Date(2026, 10, 29, 5, 0, 0, 0, time.UTC), tier: "off_peak", want: 2_265_000},
|
||||
{name: "friday peak", at: time.Date(2026, 10, 30, 2, 0, 0, 0, time.UTC), tier: "peak", want: 4_530_000},
|
||||
{name: "friday off peak", at: time.Date(2026, 10, 30, 5, 0, 0, 0, time.UTC), tier: "off_peak", want: 2_265_000},
|
||||
{name: "saturday peak", at: time.Date(2026, 10, 31, 2, 0, 0, 0, time.UTC), tier: "peak", want: 4_530_000},
|
||||
{name: "saturday off peak", at: time.Date(2026, 10, 31, 5, 0, 0, 0, time.UTC), tier: "off_peak", want: 2_265_000},
|
||||
{name: "sunday peak", at: time.Date(2026, 11, 1, 2, 0, 0, 0, time.UTC), tier: "peak", want: 4_530_000},
|
||||
{name: "sunday off peak", at: time.Date(2026, 11, 1, 5, 0, 0, 0, time.UTC), tier: "off_peak", want: 2_265_000},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result, err := billingexpr.ComputeTieredQuotaWithRequest(snapshot, params, billingexpr.RequestInput{At: &tc.at})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tc.tier, result.MatchedTier)
|
||||
assert.Equal(t, tc.want, result.ActualQuotaAfterGroup)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGPT6AstraBuiltinBilling(t *testing.T) {
|
||||
settings := config.GlobalConfig.Get("billing_setting").(*billing_setting.BillingSetting)
|
||||
saved := *settings
|
||||
@@ -148,3 +215,61 @@ func TestImageModelBuiltinPricesAndOverrides(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// DeepSeek prices are peak/off-peak expressions in USD per 1M tokens; peak is
|
||||
// twice off-peak. The same usage settles to the same quota from a Chat,
|
||||
// Responses or Anthropic response, and the Search/Fetch routing names cost
|
||||
// $0.75 per 1,000 requests with no token price. Which tier runs depends on the clock; both are pinned here.
|
||||
func TestDeepSeekBuiltinBilling(t *testing.T) {
|
||||
settings := config.GlobalConfig.Get("billing_setting").(*billing_setting.BillingSetting)
|
||||
saved := *settings
|
||||
savedRatios, savedPrices := ratio_setting.ModelRatio2JSONString(), ratio_setting.ModelPrice2JSONString()
|
||||
t.Cleanup(func() {
|
||||
*settings = saved
|
||||
require.NoError(t, ratio_setting.UpdateModelRatioByJSONString(savedRatios))
|
||||
require.NoError(t, ratio_setting.UpdateModelPriceByJSONString(savedPrices))
|
||||
})
|
||||
*settings = billing_setting.BillingSetting{BillingMode: map[string]string{}, BillingExpr: map[string]string{}}
|
||||
require.NoError(t, ratio_setting.UpdateModelRatioByJSONString(`{}`))
|
||||
require.NoError(t, ratio_setting.UpdateModelPriceByJSONString(`{}`))
|
||||
|
||||
// 1000 cache-miss + 2000 cache-hit input tokens and 500 output tokens.
|
||||
chat := &dto.Usage{PromptTokens: 3000, CompletionTokens: 500, PromptTokensDetails: dto.InputTokenDetails{CachedTokens: 2000}}
|
||||
responses := &dto.Usage{}
|
||||
service.ApplyResponsesUsage(responses, &dto.Usage{InputTokens: 3000, OutputTokens: 500, InputTokensDetails: &dto.InputTokenDetails{CachedTokens: 2000}})
|
||||
anthropic := &dto.Usage{PromptTokens: 1000, CompletionTokens: 500, PromptTokensDetails: dto.InputTokenDetails{CachedTokens: 2000}, UsageSemantic: "anthropic"}
|
||||
quota := func(expression string, usage *dto.Usage) billingexpr.TieredResult {
|
||||
params := service.BuildTieredTokenParams(usage, usage.UsageSemantic == "anthropic", billingexpr.UsedVars(expression))
|
||||
result, err := billingexpr.ComputeTieredQuota(&billingexpr.BillingSnapshot{
|
||||
ExprString: expression, ExprHash: billingexpr.ExprHashString(expression), GroupRatio: 1, QuotaPerUnit: 500000,
|
||||
}, params)
|
||||
require.NoError(t, err)
|
||||
return result
|
||||
}
|
||||
for model, want := range map[string]map[string]int{
|
||||
// flash: 1000*0.15 + 2000*0.003 + 500*0.6 = $0.000456 off-peak.
|
||||
"deepseek-flash": {"off_peak": 228, "peak": 456},
|
||||
"deepseek-v4-flash": {"off_peak": 228, "peak": 456},
|
||||
"deepseek-v4-flash-vision-exp": {"off_peak": 228, "peak": 456},
|
||||
// pro: 1000*0.66 + 2000*0.022 + 500*1.98 = $0.001694 off-peak.
|
||||
"deepseek-v4-pro": {"off_peak": 847, "peak": 1694},
|
||||
} {
|
||||
t.Run(model, func(t *testing.T) {
|
||||
assert.Equal(t, billing_setting.BillingModeTieredExpr, billing_setting.GetBillingMode(model))
|
||||
expression, ok := billing_setting.GetBillingExpr(model)
|
||||
require.True(t, ok)
|
||||
for name, usage := range map[string]*dto.Usage{"chat": chat, "responses": responses, "anthropic": anthropic} {
|
||||
result := quota(expression, usage)
|
||||
require.Contains(t, want, result.MatchedTier, name)
|
||||
assert.Equal(t, want[result.MatchedTier], result.ActualQuotaAfterGroup, name)
|
||||
}
|
||||
})
|
||||
}
|
||||
for _, model := range []string{"deepseek-search", "deepseek-fetch"} {
|
||||
expression, ok := billing_setting.GetBillingExpr(model)
|
||||
require.True(t, ok, model)
|
||||
result := quota(expression, &dto.Usage{})
|
||||
assert.Equal(t, billingexpr.BillingUnitRequest, result.BillingUnit, model)
|
||||
assert.Equal(t, 375, result.ActualQuotaAfterGroup, model)
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/constant"
|
||||
"github.com/QuantumNous/new-api/pkg/billingexpr"
|
||||
"github.com/QuantumNous/new-api/pkg/jsplugin"
|
||||
relaycommon "github.com/QuantumNous/new-api/relay/common"
|
||||
@@ -30,14 +31,14 @@ const (
|
||||
// DB keys: billing_setting.billing_mode, billing_setting.billing_expr,
|
||||
// billing_setting.plugin_billing_expr
|
||||
type BillingSetting struct {
|
||||
GroupBillingExpr map[string]map[string]string `json:"group_billing_expr"`
|
||||
BillingMode map[string]string `json:"billing_mode"`
|
||||
BillingExpr map[string]string `json:"billing_expr"`
|
||||
PluginBillingExpr map[string]string `json:"plugin_billing_expr"`
|
||||
GroupBillingExpr map[string]map[string]string `json:"group_billing_expr"`
|
||||
BillingMode map[string]string `json:"billing_mode"`
|
||||
BillingExpr map[string]string `json:"billing_expr"`
|
||||
PluginBillingExpr map[string]string `json:"plugin_billing_expr"`
|
||||
}
|
||||
|
||||
var billingSetting = BillingSetting{
|
||||
GroupBillingExpr: make(map[string]map[string]string),
|
||||
GroupBillingExpr: make(map[string]map[string]string),
|
||||
BillingMode: make(map[string]string),
|
||||
BillingExpr: make(map[string]string),
|
||||
PluginBillingExpr: make(map[string]string),
|
||||
@@ -152,6 +153,9 @@ func GetBuiltinBillingExprCopy() map[string]string {
|
||||
|
||||
func GetGroupBillingExpr(group, model string) (string, bool) {
|
||||
expr, ok := billingSetting.GroupBillingExpr[group][model]
|
||||
if !ok && group == DeepSeekOfficialGroup {
|
||||
return GetChannelBillingExpr(constant.ChannelTypeDeepSeek, model)
|
||||
}
|
||||
return expr, ok
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user