Fix DeepSeek cost attribution and pricing
This commit is contained in:
parent
71b6a2ae12
commit
877a4f08e7
4 changed files with 35 additions and 6 deletions
|
|
@ -43,10 +43,13 @@ var providerPrices = map[string][]modelPrice{
|
||||||
{Pattern: "claude-sonnet*", InputUSDPerMTok: 3.00, OutputUSDPerMTok: 15.00},
|
{Pattern: "claude-sonnet*", InputUSDPerMTok: 3.00, OutputUSDPerMTok: 15.00},
|
||||||
{Pattern: "claude-haiku*", InputUSDPerMTok: 0.25, OutputUSDPerMTok: 1.25},
|
{Pattern: "claude-haiku*", InputUSDPerMTok: 0.25, OutputUSDPerMTok: 1.25},
|
||||||
},
|
},
|
||||||
|
"deepseek": {
|
||||||
|
// DeepSeek docs include an "input cache hit" discount; this uses cache-miss rates for conservative estimates.
|
||||||
|
{Pattern: "deepseek-*", InputUSDPerMTok: 0.28, OutputUSDPerMTok: 0.42},
|
||||||
|
},
|
||||||
"ollama": {
|
"ollama": {
|
||||||
{Pattern: "*", InputUSDPerMTok: 0, OutputUSDPerMTok: 0},
|
{Pattern: "*", InputUSDPerMTok: 0, OutputUSDPerMTok: 0},
|
||||||
},
|
},
|
||||||
// deepseek pricing intentionally omitted until stable public pricing is confirmed.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func lookupPrice(provider, model string) (TokenPrice, bool) {
|
func lookupPrice(provider, model string) (TokenPrice, bool) {
|
||||||
|
|
|
||||||
|
|
@ -122,8 +122,9 @@ func (s *Store) GetSummary(days int) Summary {
|
||||||
var totalInput, totalOutput int64
|
var totalInput, totalOutput int64
|
||||||
|
|
||||||
for _, e := range events {
|
for _, e := range events {
|
||||||
provider := e.Provider
|
provider := strings.ToLower(strings.TrimSpace(e.Provider))
|
||||||
model := normalizeModel(provider, e.RequestModel, e.ResponseModel)
|
model := normalizeModel(provider, e.RequestModel, e.ResponseModel)
|
||||||
|
provider, model = inferProviderAndModel(provider, model)
|
||||||
|
|
||||||
k := pmKey{provider: provider, model: model}
|
k := pmKey{provider: provider, model: model}
|
||||||
pm := pmTotals[k]
|
pm := pmTotals[k]
|
||||||
|
|
@ -278,6 +279,19 @@ func normalizeModel(provider, requestModel, responseModel string) string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func inferProviderAndModel(provider, model string) (string, string) {
|
||||||
|
if provider == "openai" {
|
||||||
|
parts := strings.SplitN(strings.TrimSpace(model), ":", 2)
|
||||||
|
if len(parts) == 2 && strings.ToLower(parts[0]) == "deepseek" {
|
||||||
|
return "deepseek", parts[1]
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(strings.ToLower(strings.TrimSpace(model)), "deepseek") {
|
||||||
|
return "deepseek", model
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return provider, model
|
||||||
|
}
|
||||||
|
|
||||||
// ProviderModelSummary is a rollup for a provider/model pair.
|
// ProviderModelSummary is a rollup for a provider/model pair.
|
||||||
type ProviderModelSummary struct {
|
type ProviderModelSummary struct {
|
||||||
Provider string `json:"provider"`
|
Provider string `json:"provider"`
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,11 @@ func TestEstimateUSDKnownAndUnknownModels(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
usd, ok, _ = EstimateUSD("deepseek", "deepseek-reasoner", 1_000_000, 1_000_000)
|
usd, ok, _ = EstimateUSD("deepseek", "deepseek-reasoner", 1_000_000, 1_000_000)
|
||||||
if ok || usd != 0 {
|
if !ok {
|
||||||
t.Fatalf("expected deepseek pricing to be unknown, got ok=%v usd=%.4f", ok, usd)
|
t.Fatalf("expected deepseek pricing to be known for deepseek-reasoner")
|
||||||
|
}
|
||||||
|
expected = 0.70 // 1M input * $0.28 + 1M output * $0.42
|
||||||
|
if math.Abs(usd-expected) > 0.0001 {
|
||||||
|
t.Fatalf("expected usd %.4f, got %.4f", expected, usd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1002,9 +1002,13 @@ Always execute the commands rather than telling the user how to do it.`
|
||||||
}
|
}
|
||||||
|
|
||||||
if costStore != nil {
|
if costStore != nil {
|
||||||
|
providerName, _ := config.ParseModelString(modelString)
|
||||||
|
if providerName == "" {
|
||||||
|
providerName = provider.Name()
|
||||||
|
}
|
||||||
costStore.Record(cost.UsageEvent{
|
costStore.Record(cost.UsageEvent{
|
||||||
Timestamp: time.Now(),
|
Timestamp: time.Now(),
|
||||||
Provider: provider.Name(),
|
Provider: providerName,
|
||||||
RequestModel: modelString,
|
RequestModel: modelString,
|
||||||
ResponseModel: resp.Model,
|
ResponseModel: resp.Model,
|
||||||
UseCase: req.UseCase,
|
UseCase: req.UseCase,
|
||||||
|
|
@ -1189,9 +1193,13 @@ Always execute the commands rather than telling the user how to do it.`
|
||||||
}
|
}
|
||||||
|
|
||||||
if costStore != nil {
|
if costStore != nil {
|
||||||
|
providerName, _ := config.ParseModelString(modelString)
|
||||||
|
if providerName == "" {
|
||||||
|
providerName = provider.Name()
|
||||||
|
}
|
||||||
costStore.Record(cost.UsageEvent{
|
costStore.Record(cost.UsageEvent{
|
||||||
Timestamp: time.Now(),
|
Timestamp: time.Now(),
|
||||||
Provider: provider.Name(),
|
Provider: providerName,
|
||||||
RequestModel: modelString,
|
RequestModel: modelString,
|
||||||
ResponseModel: resp.Model,
|
ResponseModel: resp.Model,
|
||||||
UseCase: req.UseCase,
|
UseCase: req.UseCase,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue