package diff import ( "fmt" "sort" "strings" "github.com/router-for-me/CLIProxyAPI/v6/internal/config" ) // DiffOpenAICompatibility produces human-readable change descriptions. func DiffOpenAICompatibility(oldList, newList []config.OpenAICompatibility) []string { changes := make([]string, 0) oldMap := make(map[string]config.OpenAICompatibility, len(oldList)) oldLabels := make(map[string]string, len(oldList)) for idx, entry := range oldList { key, label := openAICompatKey(entry, idx) oldMap[key] = entry oldLabels[key] = label } newMap := make(map[string]config.OpenAICompatibility, len(newList)) newLabels := make(map[string]string, len(newList)) for idx, entry := range newList { key, label := openAICompatKey(entry, idx) newMap[key] = entry newLabels[key] = label } keySet := make(map[string]struct{}, len(oldMap)+len(newMap)) for key := range oldMap { keySet[key] = struct{}{} } for key := range newMap { keySet[key] = struct{}{} } orderedKeys := make([]string, 0, len(keySet)) for key := range keySet { orderedKeys = append(orderedKeys, key) } sort.Strings(orderedKeys) for _, key := range orderedKeys { oldEntry, oldOk := oldMap[key] newEntry, newOk := newMap[key] label := oldLabels[key] if label == "" { label = newLabels[key] } switch { case !oldOk: changes = append(changes, fmt.Sprintf("provider added: %s (api-keys=%d, models=%d)", label, countAPIKeys(newEntry), countOpenAIModels(newEntry.Models))) case !newOk: changes = append(changes, fmt.Sprintf("provider removed: %s (api-keys=%d, models=%d)", label, countAPIKeys(oldEntry), countOpenAIModels(oldEntry.Models))) default: if detail := describeOpenAICompatibilityUpdate(oldEntry, newEntry); detail != "" { changes = append(changes, fmt.Sprintf("provider updated: %s %s", label, detail)) } } } return changes } func describeOpenAICompatibilityUpdate(oldEntry, newEntry config.OpenAICompatibility) string { oldKeyCount := countAPIKeys(oldEntry) newKeyCount := countAPIKeys(newEntry) oldModelCount := countOpenAIModels(oldEntry.Models) newModelCount := countOpenAIModels(newEntry.Models) details := make([]string, 0, 3) if oldKeyCount != newKeyCount { details = append(details, fmt.Sprintf("api-keys %d -> %d", oldKeyCount, newKeyCount)) } if oldModelCount != newModelCount { details = append(details, fmt.Sprintf("models %d -> %d", oldModelCount, newModelCount)) } if !equalStringMap(oldEntry.Headers, newEntry.Headers) { details = append(details, "headers updated") } if len(details) == 0 { return "" } return "(" + strings.Join(details, ", ") + ")" } func countAPIKeys(entry config.OpenAICompatibility) int { count := 0 for _, keyEntry := range entry.APIKeyEntries { if strings.TrimSpace(keyEntry.APIKey) != "" { count++ } } return count } func countOpenAIModels(models []config.OpenAICompatibilityModel) int { count := 0 for _, model := range models { name := strings.TrimSpace(model.Name) alias := strings.TrimSpace(model.Alias) if name == "" && alias == "" { continue } count++ } return count } func openAICompatKey(entry config.OpenAICompatibility, index int) (string, string) { name := strings.TrimSpace(entry.Name) if name != "" { return "name:" + name, name } base := strings.TrimSpace(entry.BaseURL) if base != "" { return "base:" + base, base } for _, model := range entry.Models { alias := strings.TrimSpace(model.Alias) if alias == "" { alias = strings.TrimSpace(model.Name) } if alias != "" { return "alias:" + alias, alias } } return fmt.Sprintf("index:%d", index), fmt.Sprintf("entry-%d", index+1) }