fix: remove cross-platform pricing/mapping leakage for antigravity groups
Antigravity groups were incorrectly matching pricing and model mapping entries from anthropic/gemini platform tabs. Each platform should be strictly isolated — antigravity groups only use antigravity-tagged pricing.
This commit is contained in:
@@ -197,10 +197,8 @@ func newEmptyChannelCache() *channelCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// expandPricingToCache 将渠道的模型定价展开到缓存(按分组+平台维度)。
|
// expandPricingToCache 将渠道的模型定价展开到缓存(按分组+平台维度)。
|
||||||
// antigravity 平台同时服务 Claude 和 Gemini 模型,需匹配 anthropic/gemini 的定价条目。
|
// 各平台严格独立:antigravity 分组只匹配 antigravity 定价,不会匹配 anthropic/gemini 的定价。
|
||||||
// 缓存 key 使用定价条目的原始平台(pricing.Platform),而非分组平台,
|
// 查找时通过 lookupPricingAcrossPlatforms() 在本平台内查找。
|
||||||
// 避免跨平台同名模型(如 anthropic 和 gemini 都有 "model-x")互相覆盖。
|
|
||||||
// 查找时通过 lookupPricingAcrossPlatforms() 依次尝试所有匹配平台。
|
|
||||||
func expandPricingToCache(cache *channelCache, ch *Channel, gid int64, platform string) {
|
func expandPricingToCache(cache *channelCache, ch *Channel, gid int64, platform string) {
|
||||||
for j := range ch.ModelPricing {
|
for j := range ch.ModelPricing {
|
||||||
pricing := &ch.ModelPricing[j]
|
pricing := &ch.ModelPricing[j]
|
||||||
@@ -226,8 +224,7 @@ func expandPricingToCache(cache *channelCache, ch *Channel, gid int64, platform
|
|||||||
}
|
}
|
||||||
|
|
||||||
// expandMappingToCache 将渠道的模型映射展开到缓存(按分组+平台维度)。
|
// expandMappingToCache 将渠道的模型映射展开到缓存(按分组+平台维度)。
|
||||||
// antigravity 平台同时服务 Claude 和 Gemini 模型。
|
// 各平台严格独立:antigravity 分组只匹配 antigravity 映射。
|
||||||
// 缓存 key 使用映射条目的原始平台(mappingPlatform),避免跨平台同名映射覆盖。
|
|
||||||
func expandMappingToCache(cache *channelCache, ch *Channel, gid int64, platform string) {
|
func expandMappingToCache(cache *channelCache, ch *Channel, gid int64, platform string) {
|
||||||
for _, mappingPlatform := range matchingPlatforms(platform) {
|
for _, mappingPlatform := range matchingPlatforms(platform) {
|
||||||
platformMapping, ok := ch.ModelMapping[mappingPlatform]
|
platformMapping, ok := ch.ModelMapping[mappingPlatform]
|
||||||
@@ -311,23 +308,14 @@ func (s *ChannelService) buildCache(ctx context.Context) (*channelCache, error)
|
|||||||
// invalidateCache 使缓存失效,让下次读取时自然重建
|
// invalidateCache 使缓存失效,让下次读取时自然重建
|
||||||
|
|
||||||
// isPlatformPricingMatch 判断定价条目的平台是否匹配分组平台。
|
// isPlatformPricingMatch 判断定价条目的平台是否匹配分组平台。
|
||||||
// antigravity 平台同时服务 Claude(anthropic)和 Gemini(gemini)模型,
|
// 各平台(antigravity / anthropic / gemini / openai)严格独立,不跨平台匹配。
|
||||||
// 因此 antigravity 分组应匹配 anthropic 和 gemini 的定价条目。
|
|
||||||
func isPlatformPricingMatch(groupPlatform, pricingPlatform string) bool {
|
func isPlatformPricingMatch(groupPlatform, pricingPlatform string) bool {
|
||||||
if groupPlatform == pricingPlatform {
|
return groupPlatform == pricingPlatform
|
||||||
return true
|
|
||||||
}
|
|
||||||
if groupPlatform == PlatformAntigravity {
|
|
||||||
return pricingPlatform == PlatformAnthropic || pricingPlatform == PlatformGemini
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// matchingPlatforms 返回分组平台对应的所有可匹配平台列表。
|
// matchingPlatforms 返回分组平台对应的可匹配平台列表。
|
||||||
|
// 各平台严格独立,只返回自身。
|
||||||
func matchingPlatforms(groupPlatform string) []string {
|
func matchingPlatforms(groupPlatform string) []string {
|
||||||
if groupPlatform == PlatformAntigravity {
|
|
||||||
return []string{PlatformAntigravity, PlatformAnthropic, PlatformGemini}
|
|
||||||
}
|
|
||||||
return []string{groupPlatform}
|
return []string{groupPlatform}
|
||||||
}
|
}
|
||||||
func (s *ChannelService) invalidateCache() {
|
func (s *ChannelService) invalidateCache() {
|
||||||
@@ -364,10 +352,8 @@ func (c *channelCache) matchWildcardMapping(groupID int64, platform, modelLower
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// lookupPricingAcrossPlatforms 在所有匹配平台中查找模型定价。
|
// lookupPricingAcrossPlatforms 在分组平台内查找模型定价。
|
||||||
// antigravity 分组的缓存 key 使用定价条目的原始平台,因此查找时需依次尝试
|
// 各平台严格独立,只在本平台内查找(先精确匹配,再通配符)。
|
||||||
// matchingPlatforms() 返回的所有平台(antigravity → anthropic → gemini),
|
|
||||||
// 返回第一个命中的结果。非 antigravity 平台只尝试自身。
|
|
||||||
func lookupPricingAcrossPlatforms(cache *channelCache, groupID int64, groupPlatform, modelLower string) *ChannelModelPricing {
|
func lookupPricingAcrossPlatforms(cache *channelCache, groupID int64, groupPlatform, modelLower string) *ChannelModelPricing {
|
||||||
for _, p := range matchingPlatforms(groupPlatform) {
|
for _, p := range matchingPlatforms(groupPlatform) {
|
||||||
key := channelModelKey{groupID: groupID, platform: p, model: modelLower}
|
key := channelModelKey{groupID: groupID, platform: p, model: modelLower}
|
||||||
@@ -384,7 +370,7 @@ func lookupPricingAcrossPlatforms(cache *channelCache, groupID int64, groupPlatf
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// lookupMappingAcrossPlatforms 在所有匹配平台中查找模型映射。
|
// lookupMappingAcrossPlatforms 在分组平台内查找模型映射。
|
||||||
// 逻辑与 lookupPricingAcrossPlatforms 相同:先精确查找,再通配符。
|
// 逻辑与 lookupPricingAcrossPlatforms 相同:先精确查找,再通配符。
|
||||||
func lookupMappingAcrossPlatforms(cache *channelCache, groupID int64, groupPlatform, modelLower string) string {
|
func lookupMappingAcrossPlatforms(cache *channelCache, groupID int64, groupPlatform, modelLower string) string {
|
||||||
for _, p := range matchingPlatforms(groupPlatform) {
|
for _, p := range matchingPlatforms(groupPlatform) {
|
||||||
@@ -442,8 +428,7 @@ func (s *ChannelService) lookupGroupChannel(ctx context.Context, groupID int64)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetChannelModelPricing 获取指定分组+模型的渠道定价(热路径 O(1))。
|
// GetChannelModelPricing 获取指定分组+模型的渠道定价(热路径 O(1))。
|
||||||
// antigravity 分组依次尝试所有匹配平台(antigravity → anthropic → gemini),
|
// 各平台严格独立,只在本平台内查找定价。
|
||||||
// 确保跨平台同名模型各自独立匹配。
|
|
||||||
func (s *ChannelService) GetChannelModelPricing(ctx context.Context, groupID int64, model string) *ChannelModelPricing {
|
func (s *ChannelService) GetChannelModelPricing(ctx context.Context, groupID int64, model string) *ChannelModelPricing {
|
||||||
lk, err := s.lookupGroupChannel(ctx, groupID)
|
lk, err := s.lookupGroupChannel(ctx, groupID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -524,7 +509,7 @@ func resolveMapping(lk *channelLookup, groupID int64, model string) ChannelMappi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// checkRestricted 基于已查找的渠道信息检查模型是否被限制。
|
// checkRestricted 基于已查找的渠道信息检查模型是否被限制。
|
||||||
// antigravity 分组依次尝试所有匹配平台的定价列表。
|
// 只在本平台的定价列表中查找。
|
||||||
func checkRestricted(lk *channelLookup, groupID int64, model string) bool {
|
func checkRestricted(lk *channelLookup, groupID int64, model string) bool {
|
||||||
if !lk.channel.RestrictModels {
|
if !lk.channel.RestrictModels {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -1932,8 +1932,8 @@ func TestIsPlatformPricingMatch(t *testing.T) {
|
|||||||
pricingPlatform string
|
pricingPlatform string
|
||||||
want bool
|
want bool
|
||||||
}{
|
}{
|
||||||
{"antigravity matches anthropic", PlatformAntigravity, PlatformAnthropic, true},
|
{"antigravity does NOT match anthropic", PlatformAntigravity, PlatformAnthropic, false},
|
||||||
{"antigravity matches gemini", PlatformAntigravity, PlatformGemini, true},
|
{"antigravity does NOT match gemini", PlatformAntigravity, PlatformGemini, false},
|
||||||
{"antigravity matches antigravity", PlatformAntigravity, PlatformAntigravity, true},
|
{"antigravity matches antigravity", PlatformAntigravity, PlatformAntigravity, true},
|
||||||
{"antigravity does NOT match openai", PlatformAntigravity, PlatformOpenAI, false},
|
{"antigravity does NOT match openai", PlatformAntigravity, PlatformOpenAI, false},
|
||||||
{"anthropic matches anthropic", PlatformAnthropic, PlatformAnthropic, true},
|
{"anthropic matches anthropic", PlatformAnthropic, PlatformAnthropic, true},
|
||||||
@@ -1963,7 +1963,7 @@ func TestMatchingPlatforms(t *testing.T) {
|
|||||||
groupPlatform string
|
groupPlatform string
|
||||||
want []string
|
want []string
|
||||||
}{
|
}{
|
||||||
{"antigravity returns all three", PlatformAntigravity, []string{PlatformAntigravity, PlatformAnthropic, PlatformGemini}},
|
{"antigravity returns itself only", PlatformAntigravity, []string{PlatformAntigravity}},
|
||||||
{"anthropic returns itself", PlatformAnthropic, []string{PlatformAnthropic}},
|
{"anthropic returns itself", PlatformAnthropic, []string{PlatformAnthropic}},
|
||||||
{"gemini returns itself", PlatformGemini, []string{PlatformGemini}},
|
{"gemini returns itself", PlatformGemini, []string{PlatformGemini}},
|
||||||
{"openai returns itself", PlatformOpenAI, []string{PlatformOpenAI}},
|
{"openai returns itself", PlatformOpenAI, []string{PlatformOpenAI}},
|
||||||
@@ -1978,12 +1978,12 @@ func TestMatchingPlatforms(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
// 9. Antigravity cross-platform channel pricing
|
// 9. Antigravity platform isolation — no cross-platform pricing leakage
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
|
||||||
func TestGetChannelModelPricing_AntigravityCrossPlatform(t *testing.T) {
|
func TestGetChannelModelPricing_AntigravityDoesNotSeeCrossPlatformPricing(t *testing.T) {
|
||||||
// Channel has anthropic pricing for claude-opus-4-6.
|
// Channel has anthropic pricing for claude-opus-4-6.
|
||||||
// Group 10 is antigravity — should see the anthropic pricing.
|
// Group 10 is antigravity — should NOT see the anthropic pricing.
|
||||||
ch := Channel{
|
ch := Channel{
|
||||||
ID: 1,
|
ID: 1,
|
||||||
Status: StatusActive,
|
Status: StatusActive,
|
||||||
@@ -1996,9 +1996,7 @@ func TestGetChannelModelPricing_AntigravityCrossPlatform(t *testing.T) {
|
|||||||
svc := newTestChannelService(repo)
|
svc := newTestChannelService(repo)
|
||||||
|
|
||||||
result := svc.GetChannelModelPricing(context.Background(), 10, "claude-opus-4-6")
|
result := svc.GetChannelModelPricing(context.Background(), 10, "claude-opus-4-6")
|
||||||
require.NotNil(t, result, "antigravity group should see anthropic pricing")
|
require.Nil(t, result, "antigravity group should NOT see anthropic-platform pricing")
|
||||||
require.Equal(t, int64(100), result.ID)
|
|
||||||
require.InDelta(t, 15e-6, *result.InputPrice, 1e-12)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetChannelModelPricing_AnthropicCannotSeeAntigravityPricing(t *testing.T) {
|
func TestGetChannelModelPricing_AnthropicCannotSeeAntigravityPricing(t *testing.T) {
|
||||||
@@ -2020,12 +2018,12 @@ func TestGetChannelModelPricing_AnthropicCannotSeeAntigravityPricing(t *testing.
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
// 10. Antigravity cross-platform model mapping
|
// 10. Antigravity platform isolation — no cross-platform model mapping
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
|
||||||
func TestResolveChannelMapping_AntigravityCrossPlatform(t *testing.T) {
|
func TestResolveChannelMapping_AntigravityDoesNotSeeCrossPlatformMapping(t *testing.T) {
|
||||||
// Channel has anthropic model mapping: claude-opus-4-5 → claude-opus-4-6.
|
// Channel has anthropic model mapping: claude-opus-4-5 → claude-opus-4-6.
|
||||||
// Group 10 is antigravity — should apply the anthropic mapping.
|
// Group 10 is antigravity — should NOT apply the anthropic mapping.
|
||||||
ch := Channel{
|
ch := Channel{
|
||||||
ID: 1,
|
ID: 1,
|
||||||
Status: StatusActive,
|
Status: StatusActive,
|
||||||
@@ -2040,18 +2038,17 @@ func TestResolveChannelMapping_AntigravityCrossPlatform(t *testing.T) {
|
|||||||
svc := newTestChannelService(repo)
|
svc := newTestChannelService(repo)
|
||||||
|
|
||||||
result := svc.ResolveChannelMapping(context.Background(), 10, "claude-opus-4-5")
|
result := svc.ResolveChannelMapping(context.Background(), 10, "claude-opus-4-5")
|
||||||
require.True(t, result.Mapped, "antigravity group should apply anthropic mapping")
|
require.False(t, result.Mapped, "antigravity group should NOT apply anthropic mapping")
|
||||||
require.Equal(t, "claude-opus-4-6", result.MappedModel)
|
require.Equal(t, "claude-opus-4-5", result.MappedModel)
|
||||||
require.Equal(t, int64(1), result.ChannelID)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
// 11. Antigravity cross-platform same-name model — no overwrite
|
// 11. Antigravity platform isolation — same-name model across platforms
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
|
||||||
func TestGetChannelModelPricing_AntigravitySameModelDifferentPlatforms(t *testing.T) {
|
func TestGetChannelModelPricing_AntigravityDoesNotSeeSameModelFromOtherPlatforms(t *testing.T) {
|
||||||
// anthropic 和 gemini 都定义了同名模型 "shared-model",价格不同。
|
// anthropic 和 gemini 都定义了同名模型 "shared-model",价格不同。
|
||||||
// antigravity 分组应能分别查到各自的定价,而不是后者覆盖前者。
|
// antigravity 分组不应看到任何一个(各平台严格独立)。
|
||||||
ch := Channel{
|
ch := Channel{
|
||||||
ID: 1,
|
ID: 1,
|
||||||
Status: StatusActive,
|
Status: StatusActive,
|
||||||
@@ -2064,17 +2061,13 @@ func TestGetChannelModelPricing_AntigravitySameModelDifferentPlatforms(t *testin
|
|||||||
repo := makeStandardRepo(ch, map[int64]string{10: PlatformAntigravity})
|
repo := makeStandardRepo(ch, map[int64]string{10: PlatformAntigravity})
|
||||||
svc := newTestChannelService(repo)
|
svc := newTestChannelService(repo)
|
||||||
|
|
||||||
// antigravity 分组查找 "shared-model":应命中第一个匹配(按 matchingPlatforms 顺序 antigravity→anthropic→gemini)
|
|
||||||
result := svc.GetChannelModelPricing(context.Background(), 10, "shared-model")
|
result := svc.GetChannelModelPricing(context.Background(), 10, "shared-model")
|
||||||
require.NotNil(t, result, "antigravity group should find pricing for shared-model")
|
require.Nil(t, result, "antigravity group should NOT see anthropic/gemini-platform pricing")
|
||||||
// 第一个匹配应该是 anthropic(matchingPlatforms 返回 [antigravity, anthropic, gemini])
|
|
||||||
require.Equal(t, int64(200), result.ID)
|
|
||||||
require.InDelta(t, 10e-6, *result.InputPrice, 1e-12)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetChannelModelPricing_AntigravityOnlyGeminiPricing(t *testing.T) {
|
func TestGetChannelModelPricing_AntigravityDoesNotSeeGeminiOnlyPricing(t *testing.T) {
|
||||||
// 只有 gemini 平台定义了模型 "gemini-model"。
|
// 只有 gemini 平台定义了模型 "gemini-model"。
|
||||||
// antigravity 分组应能查到 gemini 的定价。
|
// antigravity 分组不应看到 gemini 的定价。
|
||||||
ch := Channel{
|
ch := Channel{
|
||||||
ID: 1,
|
ID: 1,
|
||||||
Status: StatusActive,
|
Status: StatusActive,
|
||||||
@@ -2087,14 +2080,12 @@ func TestGetChannelModelPricing_AntigravityOnlyGeminiPricing(t *testing.T) {
|
|||||||
svc := newTestChannelService(repo)
|
svc := newTestChannelService(repo)
|
||||||
|
|
||||||
result := svc.GetChannelModelPricing(context.Background(), 10, "gemini-model")
|
result := svc.GetChannelModelPricing(context.Background(), 10, "gemini-model")
|
||||||
require.NotNil(t, result, "antigravity group should find gemini pricing")
|
require.Nil(t, result, "antigravity group should NOT see gemini-platform pricing")
|
||||||
require.Equal(t, int64(300), result.ID)
|
|
||||||
require.InDelta(t, 2e-6, *result.InputPrice, 1e-12)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetChannelModelPricing_AntigravityWildcardCrossPlatformNoOverwrite(t *testing.T) {
|
func TestGetChannelModelPricing_AntigravityDoesNotSeeWildcardFromOtherPlatforms(t *testing.T) {
|
||||||
// anthropic 和 gemini 都有 "shared-*" 通配符定价,价格不同。
|
// anthropic 和 gemini 都有 "shared-*" 通配符定价。
|
||||||
// antigravity 分组查找 "shared-model" 应命中第一个匹配而非被覆盖。
|
// antigravity 分组不应命中任何一个。
|
||||||
ch := Channel{
|
ch := Channel{
|
||||||
ID: 1,
|
ID: 1,
|
||||||
Status: StatusActive,
|
Status: StatusActive,
|
||||||
@@ -2108,15 +2099,12 @@ func TestGetChannelModelPricing_AntigravityWildcardCrossPlatformNoOverwrite(t *t
|
|||||||
svc := newTestChannelService(repo)
|
svc := newTestChannelService(repo)
|
||||||
|
|
||||||
result := svc.GetChannelModelPricing(context.Background(), 10, "shared-model")
|
result := svc.GetChannelModelPricing(context.Background(), 10, "shared-model")
|
||||||
require.NotNil(t, result, "antigravity group should find wildcard pricing for shared-model")
|
require.Nil(t, result, "antigravity group should NOT see wildcard pricing from other platforms")
|
||||||
// 两个通配符都存在,应命中 anthropic 的(matchingPlatforms 顺序)
|
|
||||||
require.Equal(t, int64(400), result.ID)
|
|
||||||
require.InDelta(t, 10e-6, *result.InputPrice, 1e-12)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResolveChannelMapping_AntigravitySameModelDifferentPlatforms(t *testing.T) {
|
func TestResolveChannelMapping_AntigravityDoesNotSeeMappingFromOtherPlatforms(t *testing.T) {
|
||||||
// anthropic 和 gemini 都定义了同名模型映射 "alias" → 不同目标。
|
// anthropic 和 gemini 都定义了同名模型映射 "alias" → 不同目标。
|
||||||
// antigravity 分组应命中 anthropic 的映射(按 matchingPlatforms 顺序)。
|
// antigravity 分组不应命中任何一个。
|
||||||
ch := Channel{
|
ch := Channel{
|
||||||
ID: 1,
|
ID: 1,
|
||||||
Status: StatusActive,
|
Status: StatusActive,
|
||||||
@@ -2130,13 +2118,13 @@ func TestResolveChannelMapping_AntigravitySameModelDifferentPlatforms(t *testing
|
|||||||
svc := newTestChannelService(repo)
|
svc := newTestChannelService(repo)
|
||||||
|
|
||||||
result := svc.ResolveChannelMapping(context.Background(), 10, "alias")
|
result := svc.ResolveChannelMapping(context.Background(), 10, "alias")
|
||||||
require.True(t, result.Mapped)
|
require.False(t, result.Mapped, "antigravity group should NOT see mapping from other platforms")
|
||||||
require.Equal(t, "anthropic-target", result.MappedModel)
|
require.Equal(t, "alias", result.MappedModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCheckRestricted_AntigravitySameModelDifferentPlatforms(t *testing.T) {
|
func TestCheckRestricted_AntigravityDoesNotSeeModelsFromOtherPlatforms(t *testing.T) {
|
||||||
// anthropic 和 gemini 都定义了同名模型 "shared-model"。
|
// anthropic 和 gemini 都定义了同名模型 "shared-model"。
|
||||||
// antigravity 分组启用了 RestrictModels,"shared-model" 应不被限制。
|
// antigravity 分组启用了 RestrictModels,"shared-model" 应被限制(各平台独立)。
|
||||||
ch := Channel{
|
ch := Channel{
|
||||||
ID: 1,
|
ID: 1,
|
||||||
Status: StatusActive,
|
Status: StatusActive,
|
||||||
@@ -2151,13 +2139,39 @@ func TestCheckRestricted_AntigravitySameModelDifferentPlatforms(t *testing.T) {
|
|||||||
svc := newTestChannelService(repo)
|
svc := newTestChannelService(repo)
|
||||||
|
|
||||||
restricted := svc.IsModelRestricted(context.Background(), 10, "shared-model")
|
restricted := svc.IsModelRestricted(context.Background(), 10, "shared-model")
|
||||||
require.False(t, restricted, "shared-model should not be restricted for antigravity")
|
require.True(t, restricted, "shared-model from other platforms should be restricted for antigravity")
|
||||||
|
|
||||||
// 未定义的模型应被限制
|
|
||||||
restricted = svc.IsModelRestricted(context.Background(), 10, "unknown-model")
|
restricted = svc.IsModelRestricted(context.Background(), 10, "unknown-model")
|
||||||
require.True(t, restricted, "unknown-model should be restricted for antigravity")
|
require.True(t, restricted, "unknown-model should be restricted for antigravity")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetChannelModelPricing_AntigravityOwnPricingWorks(t *testing.T) {
|
||||||
|
// antigravity 平台自己配置的定价应正常生效(覆盖 Claude 和 Gemini 模型)。
|
||||||
|
ch := Channel{
|
||||||
|
ID: 1,
|
||||||
|
Status: StatusActive,
|
||||||
|
GroupIDs: []int64{10},
|
||||||
|
ModelPricing: []ChannelModelPricing{
|
||||||
|
{ID: 600, Platform: PlatformAntigravity, Models: []string{"claude-*"}, InputPrice: testPtrFloat64(15e-6)},
|
||||||
|
{ID: 601, Platform: PlatformAntigravity, Models: []string{"gemini-*"}, InputPrice: testPtrFloat64(2e-6)},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
repo := makeStandardRepo(ch, map[int64]string{10: PlatformAntigravity})
|
||||||
|
svc := newTestChannelService(repo)
|
||||||
|
|
||||||
|
// Claude 模型匹配 antigravity 定价
|
||||||
|
result := svc.GetChannelModelPricing(context.Background(), 10, "claude-sonnet-4")
|
||||||
|
require.NotNil(t, result)
|
||||||
|
require.Equal(t, int64(600), result.ID)
|
||||||
|
require.InDelta(t, 15e-6, *result.InputPrice, 1e-12)
|
||||||
|
|
||||||
|
// Gemini 模型匹配 antigravity 定价
|
||||||
|
result = svc.GetChannelModelPricing(context.Background(), 10, "gemini-2.5-flash")
|
||||||
|
require.NotNil(t, result)
|
||||||
|
require.Equal(t, int64(601), result.ID)
|
||||||
|
require.InDelta(t, 2e-6, *result.InputPrice, 1e-12)
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetChannelModelPricing_NonAntigravityUnaffected(t *testing.T) {
|
func TestGetChannelModelPricing_NonAntigravityUnaffected(t *testing.T) {
|
||||||
// 确保非 antigravity 平台的行为不受影响。
|
// 确保非 antigravity 平台的行为不受影响。
|
||||||
// anthropic 分组只能看到 anthropic 的定价,看不到 gemini 的。
|
// anthropic 分组只能看到 anthropic 的定价,看不到 gemini 的。
|
||||||
|
|||||||
Reference in New Issue
Block a user