package ratio_setting import ( "fmt" "sort" "strings" "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/types" ) const ModelDisplayPricingOptionKey = "ModelDisplayPricing" var displayPricingMap = types.NewRWMap[string, []types.ModelDisplayPricingItem]() func GetModelDisplayPricing(model string) []types.ModelDisplayPricingItem { items, ok := displayPricingMap.Get(model) if !ok { return nil } return types.CloneModelDisplayPricingItems(items) } func GetModelDisplayPricingCopy() map[string][]types.ModelDisplayPricingItem { src := displayPricingMap.ReadAll() dst := make(map[string][]types.ModelDisplayPricingItem, len(src)) for model, items := range src { dst[model] = types.CloneModelDisplayPricingItems(items) } return dst } func SetModelDisplayPricing(model string, items []types.ModelDisplayPricingItem) error { model = strings.TrimSpace(model) if model == "" { return fmt.Errorf("model name is required") } normalized, err := normalizeModelDisplayPricingItems(items) if err != nil { return err } displayPricingMap.Set(model, normalized) InvalidateExposedDataCache() return nil } func DeleteModelDisplayPricing(model string) error { all := displayPricingMap.ReadAll() delete(all, model) displayPricingMap.Clear() displayPricingMap.AddAll(all) InvalidateExposedDataCache() return nil } func ModelDisplayPricing2JSONString() string { bytes, err := common.Marshal(GetModelDisplayPricingCopy()) if err != nil { return "{}" } return string(bytes) } func UpdateModelDisplayPricingByJSONString(jsonStr string) error { if strings.TrimSpace(jsonStr) == "" { jsonStr = "{}" } raw := map[string][]types.ModelDisplayPricingItem{} if err := common.UnmarshalJsonStr(jsonStr, &raw); err != nil { return err } next := make(map[string][]types.ModelDisplayPricingItem, len(raw)) for model, items := range raw { model = strings.TrimSpace(model) if model == "" { return fmt.Errorf("model name is required") } normalized, err := normalizeModelDisplayPricingItems(items) if err != nil { return fmt.Errorf("model %s: %w", model, err) } next[model] = normalized } displayPricingMap.Clear() displayPricingMap.AddAll(next) InvalidateExposedDataCache() return nil } func normalizeModelDisplayPricingItems(items []types.ModelDisplayPricingItem) ([]types.ModelDisplayPricingItem, error) { normalized := types.CloneModelDisplayPricingItems(items) for i := range normalized { item := &normalized[i] item.Specification = strings.TrimSpace(item.Specification) item.OfficialSupplierTip = strings.TrimSpace(item.OfficialSupplierTip) item.Unit = strings.TrimSpace(item.Unit) if item.Specification == "" { return nil, fmt.Errorf("row %d: specification is required", i) } if item.Price < 0 { return nil, fmt.Errorf("row %d: price must be >= 0", i) } if item.Unit == "" { return nil, fmt.Errorf("row %d: unit is required", i) } if item.DiscountRate == 0 { item.DiscountRate = 1 } if item.DiscountRate <= 0 || item.DiscountRate > 1 { return nil, fmt.Errorf("row %d: discount_rate must be > 0 and <= 1", i) } } sort.SliceStable(normalized, func(i, j int) bool { if normalized[i].SortOrder != normalized[j].SortOrder { return normalized[i].SortOrder < normalized[j].SortOrder } return normalized[i].Specification < normalized[j].Specification }) return normalized, nil }