Files
2026-07-13 13:00:08 +08:00

259 lines
7.0 KiB
Go

package config
import (
"fmt"
"os"
"strings"
"reasonix/internal/provider"
)
func deepSeekV4FlashPrice() *provider.Pricing {
return &provider.Pricing{CacheHit: 0.02, Input: 1, Output: 2, Currency: "¥"}
}
func deepSeekV4ProPrice() *provider.Pricing {
return &provider.Pricing{CacheHit: 0.025, Input: 3, Output: 6, Currency: "¥"}
}
func deepSeekV4Prices() map[string]*provider.Pricing {
return map[string]*provider.Pricing{
"deepseek-v4-flash": deepSeekV4FlashPrice(),
"deepseek-v4-pro": deepSeekV4ProPrice(),
}
}
func deepSeekV4FlashPriceUSD() *provider.Pricing {
return &provider.Pricing{CacheHit: 0.0028, Input: 0.14, Output: 0.28, Currency: "$"}
}
func deepSeekV4ProPriceUSD() *provider.Pricing {
return &provider.Pricing{CacheHit: 0.003625, Input: 0.435, Output: 0.87, Currency: "$"}
}
func deepSeekV4PricesUSD() map[string]*provider.Pricing {
return map[string]*provider.Pricing{
"deepseek-v4-flash": deepSeekV4FlashPriceUSD(),
"deepseek-v4-pro": deepSeekV4ProPriceUSD(),
}
}
// DeepSeekV4PricesForLanguage keeps the settings/template call site stable while
// official DeepSeek defaults move to RMB. Persisted prices still win; this is
// only used for templates and missing-default backfills.
func DeepSeekV4PricesForLanguage(lang string) map[string]*provider.Pricing {
_ = lang
return deepSeekV4Prices()
}
func deepSeekV4PricesForConfig(c *Config) map[string]*provider.Pricing {
_ = c
return deepSeekV4Prices()
}
func deepSeekV4PriceForModel(lang, model string) *provider.Pricing {
_ = lang
return clonePricing(deepSeekV4Prices()[strings.TrimSpace(model)])
}
// DeepSeekOfficialPricingLanguage is retained for settings/template compatibility.
// Official DeepSeek providers now seed RMB prices by default; explicit user
// prices in config still override these defaults.
func (c *Config) DeepSeekOfficialPricingLanguage() string {
_ = c
return "zh"
}
// ApplyDeepSeekOfficialDefaultPricing refreshes built-in/official DeepSeek
// prices that still match known official defaults. Custom user prices are left
// untouched.
func (c *Config) ApplyDeepSeekOfficialDefaultPricing() {
applyDeepSeekOfficialDefaultPricing(c)
}
func applyDeepSeekOfficialDefaultPricing(c *Config) {
if c == nil {
return
}
lang := c.DeepSeekOfficialPricingLanguage()
for i := range c.Providers {
p := &c.Providers[i]
if officialProviderKind(p) != "deepseek" {
continue
}
if isKnownDeepSeekOfficialPricing(p.Model, p.Price) {
p.Price = deepSeekV4PriceForModel(lang, p.Model)
}
for model, price := range p.Prices {
if isKnownDeepSeekOfficialPricing(model, price) {
p.Prices[model] = deepSeekV4PriceForModel(lang, model)
}
}
}
}
func mimoV25ProPrice() *provider.Pricing {
return &provider.Pricing{CacheHit: 0.025, Input: 3, Output: 6, Currency: "¥"}
}
func mimoV25Price() *provider.Pricing {
return &provider.Pricing{CacheHit: 0.02, Input: 1, Output: 2, Currency: "¥"}
}
func mimoV2FlashPrice() *provider.Pricing {
return &provider.Pricing{CacheHit: 0.07, Input: 0.70, Output: 2.10, Currency: "¥"}
}
func mimoDomesticPrices(models []string) map[string]*provider.Pricing {
prices := map[string]*provider.Pricing{}
for _, model := range models {
switch strings.TrimSpace(model) {
case "mimo-v2.5-pro", "mimo-v2-pro":
prices[model] = mimoV25ProPrice()
case "mimo-v2.5", "mimo-v2-omni":
prices[model] = mimoV25Price()
case "mimo-v2-flash":
prices[model] = mimoV2FlashPrice()
}
}
return prices
}
func longCat20Price() *provider.Pricing {
return &provider.Pricing{CacheHit: 0.04, Input: 2, Output: 8, Currency: "¥"}
}
func longCat20Prices(models []string) map[string]*provider.Pricing {
prices := map[string]*provider.Pricing{}
for _, model := range models {
switch strings.TrimSpace(model) {
case "LongCat-2.0":
prices[model] = longCat20Price()
}
}
return prices
}
const (
deepSeekPricingResetConfigVersion = 3
windowsBashSandboxDefaultConfigVersion = 4
)
// ApplyUserConfigUpgradesOnStartup applies one-time startup migrations. It
// intentionally runs from the desktop and CLI startup paths, not every config
// Load(), so user edits made after the upgrade are preserved.
func ApplyUserConfigUpgradesOnStartup(path string) (bool, error) {
path = strings.TrimSpace(path)
if path == "" {
return false, nil
}
if _, err := os.Stat(path); err != nil {
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
var header Config
if _, err := decodeTOMLFile(path, &header); err != nil {
return false, fmt.Errorf("config %s: %w", path, err)
}
if header.ConfigVersion >= Default().ConfigVersion {
return false, nil
}
cfg := LoadForEdit(path)
changed := false
if header.ConfigVersion < deepSeekPricingResetConfigVersion {
resetOfficialProviderPricingDefaults(cfg)
changed = true
}
if shouldMarkWindowsBashSandboxDefaultUpgrade(header.ConfigVersion) {
resetWindowsBashSandboxDefaultOnUpgrade(cfg)
// Mark the Windows v4 migration even when the user was already on off,
// so a later manual enforce choice is not treated as the old template default.
changed = true
}
if !changed {
return false, nil
}
cfg.ConfigVersion = Default().ConfigVersion
if err := cfg.SaveTo(path); err != nil {
return false, err
}
return true, nil
}
// ResetOfficialProviderPricingOnUpgrade is retained for older call sites.
func ResetOfficialProviderPricingOnUpgrade(path string) (bool, error) {
return ApplyUserConfigUpgradesOnStartup(path)
}
func shouldMarkWindowsBashSandboxDefaultUpgrade(fromVersion int) bool {
return runtimeGOOS == "windows" && fromVersion < windowsBashSandboxDefaultConfigVersion
}
func resetWindowsBashSandboxDefaultOnUpgrade(c *Config) {
if c == nil {
return
}
if strings.TrimSpace(c.Sandbox.Bash) != "enforce" {
return
}
c.Sandbox.Bash = "off"
}
func resetOfficialProviderPricingDefaults(c *Config) {
if c == nil {
return
}
for i := range c.Providers {
p := &c.Providers[i]
switch {
case officialProviderKind(p) == "deepseek":
resetDeepSeekOfficialPricing(p)
}
}
}
func resetDeepSeekOfficialPricing(p *ProviderEntry) {
if p == nil {
return
}
defaults := deepSeekV4Prices()
p.Price = nil
if strings.TrimSpace(p.Model) != "" && len(p.Models) == 0 {
if price := defaults[strings.TrimSpace(p.Model)]; price != nil {
p.Price = clonePricing(price)
p.Prices = nil
return
}
}
if p.Prices == nil {
p.Prices = map[string]*provider.Pricing{}
}
for model, price := range defaults {
if p.HasModel(model) {
p.Prices[model] = clonePricing(price)
}
}
}
func isKnownDeepSeekOfficialPricing(model string, price *provider.Pricing) bool {
model = strings.TrimSpace(model)
if model == "" || price == nil {
return false
}
for _, prices := range []map[string]*provider.Pricing{deepSeekV4Prices(), deepSeekV4PricesUSD()} {
if samePricing(price, prices[model]) {
return true
}
}
return false
}
func samePricing(a, b *provider.Pricing) bool {
if a == nil || b == nil {
return false
}
return a.CacheHit == b.CacheHit && a.Input == b.Input && a.Output == b.Output && a.Currency == b.Currency
}