mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-15 23:22:59 +08:00
fix(antigravity): keep legacy default passthroughs and bill Claude 5.5 tier IDs at base rates
- Restore runtime default passthroughs and Gemini 3.1 Pro aliases for
explicit Antigravity model_mapping. Mappings saved before the Gemini
3.x Flash / 3.1 Pro releases still depend on them; dropping them would
silently remove those models from existing accounts on upgrade.
- Capability sync now skips injected-only passthrough targets, so models
the upstream catalog omits no longer mark the sync as partial.
- Price Antigravity claude-{opus,sonnet}-5-5-{high,medium,low} at the base
Opus/Sonnet 5.5 card instead of substring-matching claude-opus-5 /
claude-sonnet-4-5 (1.25x-3x overbilling).
This commit is contained in:
@@ -1408,14 +1408,14 @@ func modelIDsForTest(models []gatewayModelItemForTest) []string {
|
||||
return ids
|
||||
}
|
||||
|
||||
func TestGatewayModels_AntigravityExplicitMappingDefinesBothCatalogs(t *testing.T) {
|
||||
func TestGatewayModels_AntigravityExplicitMappingAppearsInBothCatalogs(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
const groupID int64 = 93
|
||||
mapping := map[string]any{
|
||||
"gemini-3.8-flash": "gemini-3.8-flash-high",
|
||||
"gemini-3.8-flash-high": "gemini-3.8-flash-high",
|
||||
"claude-opus-5-5-high": "claude-opus-5-5-high",
|
||||
"gemini-pro-agent": "gemini-pro-agent",
|
||||
"gemini-3.8-flash": "gemini-3.8-flash-high",
|
||||
"gemini-3.8-flash-high": "gemini-3.8-flash-high",
|
||||
"claude-opus-4-6-thinking": "claude-opus-4-6-thinking",
|
||||
"gemini-pro-agent": "gemini-pro-agent",
|
||||
}
|
||||
for _, platform := range []string{service.PlatformAntigravity, service.PlatformComposite, service.PlatformGemini} {
|
||||
t.Run(platform, func(t *testing.T) {
|
||||
@@ -1426,7 +1426,7 @@ func TestGatewayModels_AntigravityExplicitMappingDefinesBothCatalogs(t *testing.
|
||||
Credentials: map[string]any{"model_mapping": mapping},
|
||||
}}},
|
||||
})
|
||||
want := []string{"gemini-3.8-flash", "gemini-3.8-flash-high", "gemini-pro-agent", "claude-opus-5-5-high"}
|
||||
want := []string{"gemini-3.8-flash", "gemini-3.8-flash-high", "gemini-pro-agent", "claude-opus-4-6-thinking"}
|
||||
if platform == service.PlatformGemini {
|
||||
want = want[:3]
|
||||
}
|
||||
@@ -1452,7 +1452,11 @@ func TestGatewayModels_AntigravityExplicitMappingDefinesBothCatalogs(t *testing.
|
||||
ids = modelIDsForTest(got.Data)
|
||||
}
|
||||
require.Equal(t, http.StatusOK, rec.Code)
|
||||
require.ElementsMatch(t, want, ids, "codex=%t", codex)
|
||||
// 显式条目必须同时出现在两种目录;默认透传补齐的条目不锁定。
|
||||
require.Subset(t, ids, want, "codex=%t", codex)
|
||||
if platform == service.PlatformGemini {
|
||||
require.NotContains(t, ids, "claude-opus-4-6-thinking", "codex=%t", codex)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1517,7 +1521,12 @@ func TestGatewayModels_GeminiGroupIncludesAntigravityGeminiMappings(t *testing.T
|
||||
var got gatewayModelsResponseForTest
|
||||
require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
|
||||
ids := modelIDsForTest(got.Data)
|
||||
require.ElementsMatch(t, []string{"gemini-3.8-flash-high", "gemini-synced-custom", "gemini-3.8-flash-low", "gemini-native-only"}, ids)
|
||||
// 账号显式配置的 gemini-* 映射必须出现(resolveModelMapping 还会补一组 antigravity
|
||||
// 默认透传别名,这里只断言显式条目与排除项,不锁定补齐后的完整集合)。
|
||||
require.Contains(t, ids, "gemini-3.8-flash-high")
|
||||
require.Contains(t, ids, "gemini-synced-custom")
|
||||
require.Contains(t, ids, "gemini-3.8-flash-low")
|
||||
require.Contains(t, ids, "gemini-native-only")
|
||||
require.NotContains(t, ids, "gemini-disabled-only")
|
||||
require.NotContains(t, ids, "gemini-unset-only")
|
||||
// antigravity 账号的 claude-* 映射与 anthropic 账号的映射都不应出现在 gemini 分组。
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"log/slog"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -690,8 +691,7 @@ func (a *Account) resolveModelMapping(rawMapping map[string]any) map[string]stri
|
||||
}
|
||||
if len(result) > 0 {
|
||||
if a.Platform == domain.PlatformAntigravity {
|
||||
// Explicit mappings define this account's whitelist. Injecting static
|
||||
// defaults re-enables retired models after an upstream model sync.
|
||||
ensureAntigravityDefaultPassthroughs(result, antigravityDefaultPassthroughModels)
|
||||
applyAntigravityGemini31ProAliases(result)
|
||||
}
|
||||
return result
|
||||
@@ -741,6 +741,72 @@ func modelMappingSignature(rawMapping map[string]any) uint64 {
|
||||
return h.Sum64()
|
||||
}
|
||||
|
||||
// antigravityDefaultPassthroughModels 是 Antigravity 显式 model_mapping 运行时补齐的默认透传条目:
|
||||
// 让新模型发布前保存的映射也能调用这些模型。
|
||||
var antigravityDefaultPassthroughModels = []string{
|
||||
"gemini-3-flash",
|
||||
"gemini-3.1-pro-high",
|
||||
"gemini-3.1-pro-low",
|
||||
"gemini-3.6-flash",
|
||||
"gemini-3.6-flash-high",
|
||||
"gemini-3.6-flash-low",
|
||||
"gemini-3.6-flash-medium",
|
||||
"gemini-3.6-flash-tiered",
|
||||
"gemini-3.7-flash",
|
||||
"gemini-3.7-flash-high",
|
||||
"gemini-3.7-flash-low",
|
||||
"gemini-3.7-flash-medium",
|
||||
"gemini-3.7-flash-tiered",
|
||||
"gemini-3.8-flash",
|
||||
"gemini-3.8-flash-high",
|
||||
"gemini-3.8-flash-low",
|
||||
"gemini-3.8-flash-medium",
|
||||
"gemini-3.8-flash-tiered",
|
||||
}
|
||||
|
||||
// isAntigravityInjectedDefaultPassthrough 判断 GetModelMapping 中的 key→target 是否仅由
|
||||
// resolveModelMapping 运行时补齐(管理员未写入 credentials.model_mapping)。
|
||||
func (a *Account) isAntigravityInjectedDefaultPassthrough(key, target string) bool {
|
||||
if a == nil || a.Platform != domain.PlatformAntigravity || key != target || !slices.Contains(antigravityDefaultPassthroughModels, key) {
|
||||
return false
|
||||
}
|
||||
rawMapping, _ := a.Credentials["model_mapping"].(map[string]any)
|
||||
hasExplicitEntry := false
|
||||
for _, v := range rawMapping {
|
||||
if _, ok := v.(string); ok {
|
||||
hasExplicitEntry = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasExplicitEntry {
|
||||
// 空映射走默认目录,不做运行时补齐。
|
||||
return false
|
||||
}
|
||||
_, configured := rawMapping[key].(string)
|
||||
return !configured
|
||||
}
|
||||
|
||||
func ensureAntigravityDefaultPassthrough(mapping map[string]string, model string) {
|
||||
if mapping == nil || model == "" {
|
||||
return
|
||||
}
|
||||
if _, exists := mapping[model]; exists {
|
||||
return
|
||||
}
|
||||
for pattern := range mapping {
|
||||
if matchWildcard(pattern, model) {
|
||||
return
|
||||
}
|
||||
}
|
||||
mapping[model] = model
|
||||
}
|
||||
|
||||
func ensureAntigravityDefaultPassthroughs(mapping map[string]string, models []string) {
|
||||
for _, model := range models {
|
||||
ensureAntigravityDefaultPassthrough(mapping, model)
|
||||
}
|
||||
}
|
||||
|
||||
func applyAntigravityGemini31ProAliases(mapping map[string]string) {
|
||||
target := strings.TrimSpace(mapping[domain.AntigravityGemini31ProAgentModel])
|
||||
if target == "" {
|
||||
@@ -774,13 +840,26 @@ func applyAntigravityGemini31ProAliases(mapping map[string]string) {
|
||||
|
||||
for _, alias := range aliases {
|
||||
current, exists := mapping[alias.model]
|
||||
if !exists {
|
||||
if exists {
|
||||
if _, legacy := alias.legacyTargets[current]; legacy {
|
||||
mapping[alias.model] = target
|
||||
}
|
||||
continue
|
||||
}
|
||||
if _, legacy := alias.legacyTargets[current]; legacy {
|
||||
mapping[alias.model] = target
|
||||
if mappingHasWildcardForModel(mapping, alias.model) {
|
||||
continue
|
||||
}
|
||||
mapping[alias.model] = target
|
||||
}
|
||||
}
|
||||
|
||||
func mappingHasWildcardForModel(mapping map[string]string, model string) bool {
|
||||
for pattern := range mapping {
|
||||
if matchWildcard(pattern, model) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func normalizeRequestedModelForLookup(platform, requestedModel string) string {
|
||||
|
||||
@@ -437,8 +437,8 @@ func TestAccountGetModelMapping_AntigravityNormalizesGemini31ProAliases(t *testi
|
||||
|
||||
mapping := account.GetModelMapping()
|
||||
|
||||
if account.IsModelSupported("gemini-3.1-pro") {
|
||||
t.Fatal("legacy target normalization must not add an unconfigured alias")
|
||||
if got := mapping["gemini-3.1-pro"]; got != domain.AntigravityGemini31ProAgentModel {
|
||||
t.Fatalf("expected gemini-3.1-pro to map to %q, got %q", domain.AntigravityGemini31ProAgentModel, got)
|
||||
}
|
||||
if got := mapping["gemini-3.1-pro-high"]; got != domain.AntigravityGemini31ProAgentModel {
|
||||
t.Fatalf("expected gemini-3.1-pro-high to map to %q, got %q", domain.AntigravityGemini31ProAgentModel, got)
|
||||
@@ -470,8 +470,8 @@ func TestAccountGetModelMapping_AntigravityPreservesGemini31ProOverrides(t *test
|
||||
if got := mapping["gemini-3.1-pro-preview"]; got != "custom-preview" {
|
||||
t.Fatalf("expected gemini-3.1-pro-preview override to be preserved, got %q", got)
|
||||
}
|
||||
if account.IsModelSupported("gemini-3.1-pro") {
|
||||
t.Fatal("explicit overrides must not add an unconfigured alias")
|
||||
if got := mapping["gemini-3.1-pro"]; got != domain.AntigravityGemini31ProAgentModel {
|
||||
t.Fatalf("expected gemini-3.1-pro alias to default to %q, got %q", domain.AntigravityGemini31ProAgentModel, got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -591,7 +591,7 @@ func TestAccountResolveMappedModel(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccountGetModelMapping_AntigravityExplicitWhitelistDoesNotReenableDefaults(t *testing.T) {
|
||||
func TestAccountGetModelMapping_AntigravityEnsuresGeminiDefaultPassthroughs(t *testing.T) {
|
||||
account := &Account{
|
||||
Platform: PlatformAntigravity,
|
||||
Credentials: map[string]any{
|
||||
@@ -602,13 +602,14 @@ func TestAccountGetModelMapping_AntigravityExplicitWhitelistDoesNotReenableDefau
|
||||
}
|
||||
|
||||
mapping := account.GetModelMapping()
|
||||
if len(mapping) != 1 || account.GetMappedModel("gemini-3-pro-high") != "gemini-3.1-pro-high" {
|
||||
t.Fatalf("expected the explicit alias to be retained, got %v", mapping)
|
||||
if mapping["gemini-3-flash"] != "gemini-3-flash" {
|
||||
t.Fatalf("expected gemini-3-flash passthrough to be auto-filled, got: %q", mapping["gemini-3-flash"])
|
||||
}
|
||||
for _, model := range []string{"gemini-3-flash", "gemini-3.1-pro-high", "gemini-3.1-pro-low", "gemini-3.7-flash-high", "claude-opus-5-5-high"} {
|
||||
if account.IsModelSupported(model) {
|
||||
t.Fatalf("explicit whitelist must not re-enable %q", model)
|
||||
}
|
||||
if mapping["gemini-3.1-pro-high"] != "gemini-3.1-pro-high" {
|
||||
t.Fatalf("expected gemini-3.1-pro-high passthrough to be auto-filled, got: %q", mapping["gemini-3.1-pro-high"])
|
||||
}
|
||||
if mapping["gemini-3.1-pro-low"] != "gemini-3.1-pro-low" {
|
||||
t.Fatalf("expected gemini-3.1-pro-low passthrough to be auto-filled, got: %q", mapping["gemini-3.1-pro-low"])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,8 @@ func TestResolveGeminiThinkingVariant(t *testing.T) {
|
||||
{"only -high exists: low request degrades to high", map[string]string{
|
||||
"gemini-3.5-flash-high": "gemini-3.5-flash-high",
|
||||
}, "gemini-3.5-flash", budget("1000"), "gemini-3.5-flash-high", true},
|
||||
{"unconfigured default variants stay unavailable", catalog, "gemini-3.6-flash", budget("1000"), "gemini-3.6-flash-high", true},
|
||||
// gemini-3.6/3.7/3.8-flash 的四个变体会被 resolveModelMapping 自动补齐(存量显式映射账号的兼容透传)。
|
||||
{"injected default variants are usable", catalog, "gemini-3.6-flash", budget("1000"), "gemini-3.6-flash-low", true},
|
||||
{"already suffixed: untouched", catalog, "gemini-3.8-flash-low", budget("-1"), "", false},
|
||||
{"bare name explicitly mapped: untouched", catalog, "gemini-2.5-flash", budget("-1"), "", false},
|
||||
{"no variants in mapping: untouched", catalog, "gemini-9.9-flash", budget("-1"), "", false},
|
||||
@@ -89,10 +90,15 @@ func TestResolveGeminiThinkingVariant(t *testing.T) {
|
||||
{"variant value is itself a mapping target", map[string]string{
|
||||
"gemini-3.8-flash-high": "gemini-3.8-flash-tiered",
|
||||
}, "gemini-3.8-flash", budget("-1"), "gemini-3.8-flash-tiered", true},
|
||||
// 前端持久化的默认目录含裸名自映射;-low 由默认透传补齐,按预算命中。
|
||||
{"persisted default bare identity passthrough still resolves when variants exist", map[string]string{
|
||||
"gemini-3.8-flash": "gemini-3.8-flash",
|
||||
"gemini-3.8-flash-high": "gemini-3.8-flash-high",
|
||||
}, "gemini-3.8-flash", budget("1000"), "gemini-3.8-flash-high", true},
|
||||
}, "gemini-3.8-flash", budget("1000"), "gemini-3.8-flash-low", true},
|
||||
{"bare identity passthrough resolves a configured variant outside the default passthroughs", map[string]string{
|
||||
"gemini-3.5-flash": "gemini-3.5-flash",
|
||||
"gemini-3.5-flash-high": "gemini-3.5-flash-high",
|
||||
}, "gemini-3.5-flash", budget("1000"), "gemini-3.5-flash-high", true},
|
||||
{"wildcard mapping does not fabricate variants for non-tiered bare model", map[string]string{
|
||||
"gemini-2.5*": "gemini-2.5-flash",
|
||||
}, "gemini-2.5-flash", budget("-1"), "", false},
|
||||
|
||||
@@ -1035,7 +1035,7 @@ func (s *BillingService) initFallbackPricing() {
|
||||
|
||||
// getFallbackPricing 根据模型系列获取回退价格
|
||||
func (s *BillingService) getFallbackPricing(model string) *ModelPricing {
|
||||
modelLower := strings.ToLower(model)
|
||||
modelLower := normalizeAntigravityClaudeTierAlias(strings.ToLower(model))
|
||||
if modelLower == "jev-latest" {
|
||||
return s.fallbackPrices["jev-latest"]
|
||||
}
|
||||
|
||||
@@ -1309,7 +1309,7 @@ func (s *PricingService) buildModelLookupCandidates(modelLower string) []string
|
||||
// one later. Antigravity's Gemini thinking tiers share the base rate,
|
||||
// so the normalized base remains the fallback after the exact aliases.
|
||||
candidates := rawCandidates
|
||||
if normalizeGeminiThinkingTierAlias(lastSegment(modelLower)) != lastSegment(modelLower) {
|
||||
if isAntigravityThinkingTierAlias(lastSegment(modelLower)) {
|
||||
candidates = append(candidates, normalized)
|
||||
} else {
|
||||
// Prefer canonical model names for all other aliases (including models/xxx).
|
||||
@@ -1365,7 +1365,29 @@ func normalizeModelNameForPricing(model string) string {
|
||||
}
|
||||
return canonical
|
||||
}
|
||||
return normalizeGeminiThinkingTierAlias(model)
|
||||
return normalizeAntigravityClaudeTierAlias(normalizeGeminiThinkingTierAlias(model))
|
||||
}
|
||||
|
||||
// normalizeAntigravityClaudeTierAlias maps Antigravity's Claude 5.5 thinking-tier
|
||||
// model IDs (claude-opus-5-5-high, claude-sonnet-5-5-low, ...) to the public base
|
||||
// model. Like the Gemini tiers, the suffix selects reasoning depth rather than a
|
||||
// separate token rate; without this the IDs fall into substring family matching
|
||||
// and are billed as claude-opus-5 / claude-sonnet-4-5.
|
||||
func normalizeAntigravityClaudeTierAlias(model string) string {
|
||||
for _, baseModel := range []string{"claude-opus-5-5", "claude-sonnet-5-5"} {
|
||||
for _, tier := range []string{"-high", "-low", "-medium"} {
|
||||
if model == baseModel+tier {
|
||||
return baseModel
|
||||
}
|
||||
}
|
||||
}
|
||||
return model
|
||||
}
|
||||
|
||||
// isAntigravityThinkingTierAlias reports whether model is an Antigravity
|
||||
// Gemini or Claude thinking-tier alias of a priced base model.
|
||||
func isAntigravityThinkingTierAlias(model string) bool {
|
||||
return normalizeGeminiThinkingTierAlias(model) != model || normalizeAntigravityClaudeTierAlias(model) != model
|
||||
}
|
||||
|
||||
// normalizeGeminiThinkingTierAlias maps Antigravity's Gemini thinking-tier
|
||||
@@ -1411,6 +1433,7 @@ func (s *PricingService) extractBaseName(model string) string {
|
||||
|
||||
// matchByModelFamily 基于模型系列匹配
|
||||
func (s *PricingService) matchByModelFamily(model string) *LiteLLMModelPricing {
|
||||
model = normalizeAntigravityClaudeTierAlias(model)
|
||||
if claude.IsOpus55(model) {
|
||||
if pricing, ok := s.pricingData["claude-opus-5-5"]; ok {
|
||||
return pricing
|
||||
|
||||
@@ -1278,3 +1278,42 @@ func TestCalculateCost_ClaudeSonnetCatalogLadderIsDataDriven(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.False(t, cost.LongContextBillingApplied, "恰好 200000 不进高档(严格大于)")
|
||||
}
|
||||
|
||||
// Antigravity 的 Claude 5.5 思考档位 ID 只决定推理深度,必须按基础型号计价,
|
||||
// 不能落入子串家族匹配(opus-5-5-high → claude-opus-5、sonnet-5-5-high → sonnet-4-5)。
|
||||
func TestPricingService_AntigravityClaude55TiersUseBaseModelPricing(t *testing.T) {
|
||||
opus := &LiteLLMModelPricing{InputCostPerToken: 4e-6, OutputCostPerToken: 20e-6}
|
||||
sonnet := &LiteLLMModelPricing{InputCostPerToken: 2e-6, OutputCostPerToken: 10e-6}
|
||||
svc := &PricingService{pricingData: map[string]*LiteLLMModelPricing{
|
||||
"claude-opus-5-5": opus,
|
||||
"claude-sonnet-5-5": sonnet,
|
||||
"claude-opus-5": {InputCostPerToken: 5e-6, OutputCostPerToken: 25e-6},
|
||||
"claude-sonnet-4-5": {InputCostPerToken: 3e-6, OutputCostPerToken: 15e-6},
|
||||
}}
|
||||
for _, tier := range []string{"high", "medium", "low"} {
|
||||
require.Same(t, opus, svc.GetModelPricing("claude-opus-5-5-"+tier))
|
||||
require.Same(t, sonnet, svc.GetModelPricing("claude-sonnet-5-5-"+tier))
|
||||
}
|
||||
|
||||
// 价格目录缺基础型号时走家族兜底,同样不能落到 claude-opus-5 / sonnet-4-5。
|
||||
sparse := &PricingService{pricingData: map[string]*LiteLLMModelPricing{
|
||||
"claude-opus-5": {InputCostPerToken: 5e-6, OutputCostPerToken: 25e-6},
|
||||
"claude-sonnet-4-5": {InputCostPerToken: 3e-6, OutputCostPerToken: 15e-6},
|
||||
}}
|
||||
require.Same(t, claudeOpus55FallbackPricing, sparse.GetModelPricing("claude-opus-5-5-high"))
|
||||
require.Same(t, claudeSonnet55FallbackPricing, sparse.GetModelPricing("claude-sonnet-5-5-low"))
|
||||
}
|
||||
|
||||
func TestBillingService_AntigravityClaude55TierFallbacksUseBaseRates(t *testing.T) {
|
||||
svc := NewBillingService(&config.Config{}, nil)
|
||||
tokens := UsageTokens{InputTokens: 1_000_000, OutputTokens: 1_000_000}
|
||||
for _, tier := range []string{"high", "medium", "low"} {
|
||||
for base, want := range map[string]string{"claude-opus-5-5": "claude-opus-5-5", "claude-sonnet-5-5": "claude-sonnet-5-5"} {
|
||||
got, err := svc.CalculateCost(base+"-"+tier, tokens, 1)
|
||||
require.NoError(t, err)
|
||||
ref, err := svc.CalculateCost(want, tokens, 1)
|
||||
require.NoError(t, err)
|
||||
require.InDelta(t, ref.TotalCost, got.TotalCost, 1e-12, base+"-"+tier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,7 +340,11 @@ func configuredUpstreamModelsForCapabilitySync(account *Account) []string {
|
||||
return nil
|
||||
}
|
||||
models := make([]string, 0)
|
||||
for _, mappedModel := range account.GetModelMapping() {
|
||||
for requestedModel, mappedModel := range account.GetModelMapping() {
|
||||
// Antigravity 运行时补齐的默认透传不是管理员配置的目标,上游目录未列出时不应拉低能力同步完整度。
|
||||
if account.isAntigravityInjectedDefaultPassthrough(requestedModel, mappedModel) {
|
||||
continue
|
||||
}
|
||||
mappedModel = strings.TrimSpace(mappedModel)
|
||||
if mappedModel == "" || strings.Contains(mappedModel, "*") {
|
||||
continue
|
||||
|
||||
@@ -223,3 +223,23 @@ func TestSyncUpstreamModelCatalogAntigravityBudgetReasoningLevels(t *testing.T)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfiguredUpstreamModelsForCapabilitySyncSkipsInjectedAntigravityPassthroughs(t *testing.T) {
|
||||
account := &Account{
|
||||
Platform: PlatformAntigravity,
|
||||
Credentials: map[string]any{"model_mapping": map[string]any{
|
||||
"gemini-3.8-flash-high": "gemini-3.8-flash-high",
|
||||
"gemini-3.8-flash": "gemini-3.8-flash-tiered",
|
||||
"custom-alias": "gemini-3.6-flash-low",
|
||||
}},
|
||||
}
|
||||
// 运行时补齐的默认透传仍可调用(存量账号兼容)。
|
||||
require.True(t, account.IsModelSupported("gemini-3.7-flash-medium"))
|
||||
|
||||
got := configuredUpstreamModelsForCapabilitySync(account)
|
||||
require.ElementsMatch(t, []string{"gemini-3.8-flash-high", "gemini-3.8-flash-tiered", "gemini-3.6-flash-low"}, got)
|
||||
|
||||
// 空映射走默认目录,其中的同名条目不是运行时补齐。
|
||||
empty := &Account{Platform: PlatformAntigravity, Credentials: map[string]any{}}
|
||||
require.Contains(t, configuredUpstreamModelsForCapabilitySync(empty), "gemini-3.8-flash-low")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user