openai_model_alias.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. package service
  2. import (
  3. "strings"
  4. "github.com/Wei-Shaw/sub2api/internal/pkg/openai"
  5. )
  6. func lastOpenAIModelSegment(model string) string {
  7. model = strings.TrimSpace(model)
  8. if model == "" {
  9. return ""
  10. }
  11. if strings.Contains(model, "/") {
  12. parts := strings.Split(model, "/")
  13. model = parts[len(parts)-1]
  14. }
  15. return strings.TrimSpace(model)
  16. }
  17. func canonicalizeOpenAIModelAliasSpelling(model string) string {
  18. return openai.CanonicalizeOpenAIModelAliasSpelling(model)
  19. }
  20. func normalizeKnownOpenAICodexModel(model string) string {
  21. normalized := canonicalizeOpenAIModelAliasSpelling(model)
  22. if normalized == "" {
  23. return ""
  24. }
  25. if mapped := getNormalizedCodexModel(normalized); mapped != "" {
  26. return mapped
  27. }
  28. if strings.HasSuffix(normalized, "-openai-compact") {
  29. if mapped := getNormalizedCodexModel(strings.TrimSuffix(normalized, "-openai-compact")); mapped != "" {
  30. return mapped
  31. }
  32. }
  33. switch {
  34. case normalized == "gpt-6" || normalized == "gpt-6-astra":
  35. return "gpt-6-astra"
  36. case strings.Contains(normalized, "gpt-5.6-sol"):
  37. return "gpt-5.6-sol"
  38. case strings.Contains(normalized, "gpt-5.6-terra"):
  39. return "gpt-5.6-terra"
  40. case strings.Contains(normalized, "gpt-5.6-luna"):
  41. return "gpt-5.6-luna"
  42. case normalized == "gpt-5.6":
  43. return "gpt-5.6-sol"
  44. case strings.HasPrefix(normalized, "gpt-5.6-"):
  45. suffix := strings.TrimPrefix(normalized, "gpt-5.6-")
  46. if suffix == "max" || isKnownCodexModelSuffix(suffix) {
  47. return "gpt-5.6-sol"
  48. }
  49. return ""
  50. case strings.Contains(normalized, "gpt-5.5-pro"):
  51. return "gpt-5.5-pro"
  52. case strings.Contains(normalized, "gpt-5.5"):
  53. return "gpt-5.5"
  54. case strings.Contains(normalized, "gpt-5.4-mini"):
  55. return "gpt-5.4-mini"
  56. case strings.Contains(normalized, "gpt-5.4-nano"):
  57. return "gpt-5.4-nano"
  58. case strings.Contains(normalized, "gpt-5.4"):
  59. return "gpt-5.4"
  60. case strings.Contains(normalized, "gpt-5.2"):
  61. return "gpt-5.2"
  62. case strings.Contains(normalized, "gpt-5.3-codex-spark"):
  63. return "gpt-5.3-codex-spark"
  64. case strings.Contains(normalized, "gpt-5.3-codex"):
  65. return "gpt-5.3-codex"
  66. case strings.Contains(normalized, "gpt-5.3"):
  67. return "gpt-5.3-codex"
  68. case strings.Contains(normalized, "codex"):
  69. return "gpt-5.3-codex"
  70. case strings.Contains(normalized, "gpt-5"):
  71. return "gpt-5.4"
  72. default:
  73. return ""
  74. }
  75. }
  76. // isOpenAIGPT56Model 判断是否 GPT-5.6 系列模型;入参可为原始模型名
  77. // (含大小写/路径/后缀变体)或已归一化的基名,两者均能正确识别。
  78. func isOpenAIGPT56Model(model string) bool {
  79. normalized := canonicalizeOpenAIModelAliasSpelling(model)
  80. if normalized == "gpt-5.6" {
  81. return true
  82. }
  83. if suffix, ok := strings.CutPrefix(normalized, "gpt-5.6-"); ok && (suffix == "max" || isKnownCodexModelSuffix(suffix)) {
  84. return true
  85. }
  86. for _, prefix := range []string{"gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna"} {
  87. if normalized == prefix || strings.HasPrefix(normalized, prefix+"-") {
  88. return true
  89. }
  90. }
  91. return false
  92. }
  93. // isOpenAIGPT6AstraModel reports GPT-6 Astra and dated/provider-prefixed variants.
  94. // The public "gpt-6" alias routes to Astra; unrelated GPT-6 families stay excluded.
  95. func isOpenAIGPT6AstraModel(model string) bool {
  96. normalized := canonicalizeOpenAIModelAliasSpelling(model)
  97. return normalized == "gpt-6" || normalized == "gpt-6-astra" || strings.HasPrefix(normalized, "gpt-6-astra-")
  98. }
  99. func appendUsageBillingModelCandidate(candidates []string, seen map[string]struct{}, model string) []string {
  100. trimmed := strings.TrimSpace(model)
  101. if trimmed == "" {
  102. return candidates
  103. }
  104. add := func(candidate string) {
  105. candidate = strings.TrimSpace(candidate)
  106. if candidate == "" {
  107. return
  108. }
  109. key := strings.ToLower(candidate)
  110. if _, ok := seen[key]; ok {
  111. return
  112. }
  113. seen[key] = struct{}{}
  114. candidates = append(candidates, candidate)
  115. }
  116. add(trimmed)
  117. if canonical := canonicalizeOpenAIModelAliasSpelling(trimmed); canonical != "" {
  118. add(canonical)
  119. }
  120. if normalized := normalizeKnownOpenAICodexModel(trimmed); normalized != "" {
  121. add(normalized)
  122. }
  123. return candidates
  124. }
  125. func usageBillingModelCandidates(primary string, alternates ...string) []string {
  126. seen := make(map[string]struct{}, 1+len(alternates))
  127. candidates := appendUsageBillingModelCandidate(nil, seen, primary)
  128. for _, alternate := range alternates {
  129. candidates = appendUsageBillingModelCandidate(candidates, seen, alternate)
  130. }
  131. return candidates
  132. }
  133. func firstUsageBillingModel(candidates []string) string {
  134. for _, candidate := range candidates {
  135. if trimmed := strings.TrimSpace(candidate); trimmed != "" {
  136. return trimmed
  137. }
  138. }
  139. return ""
  140. }