fix: 未知 Grok 文本兜底排除 image/video/audio 等媒体族

带版本号的媒体 ID(grok-2-image-1212、grok-5-video)会被
grok-N* 白名单当成文本,错吃 4.5 token 价。vision 多模态
仍按 token 计;HasIdentifiedTokenPricing 也不放行这些 ID。
This commit is contained in:
IanShaw027
2026-08-13 09:22:38 +08:00
parent fd82dfd52d
commit e29b93a1fb
3 changed files with 30 additions and 0 deletions
@@ -862,6 +862,9 @@ func (s *BillingService) grokUnknownTextFamilyFallback(model string) *ModelPrici
func isGrokUnknownTextFamilyModel(model string) bool {
native := strings.ToLower(strings.TrimSpace(xai.StripGrokProviderPrefix(model)))
if isGrokMediaFamilyModel(native) {
return false
}
switch {
case native == "grok", native == "grok-latest":
return true
@@ -877,6 +880,19 @@ func isGrokUnknownTextFamilyModel(model string) bool {
}
}
// isGrokMediaFamilyModel matches ids that are billed per image/video/audio unit
// rather than per token, so version-numbered media ids (grok-2-image-1212,
// grok-5-video) cannot slip into the unknown-text fallback and pick up a token
// card. "vision" is deliberately absent: multimodal chat models are token billed.
func isGrokMediaFamilyModel(native string) bool {
for _, marker := range []string{"imagine", "image", "video", "audio", "speech", "tts", "transcribe", "realtime"} {
if strings.Contains(native, marker) {
return true
}
}
return false
}
// HasIdentifiedTokenPricing 判断模型能否在价格表中被"确定性识别"出 token 价格。
//
// 与 GetModelPricing 的关键区别:本函数拒绝按子串猜系列的兜底。GetModelPricing 会
@@ -1221,6 +1221,14 @@ func TestGetModelPricing_UnknownGrokTextFallsBackToGrok45(t *testing.T) {
require.InDelta(t, baseline.CacheReadPricePerToken, pricing.CacheReadPricePerToken, 1e-12, model)
}
// Per-unit media ids must not inherit the text card just because they carry
// a version number; they are billed by the image/video/audio paths instead.
for _, model := range []string{"grok-2-image-1212", "grok-2-audio", "grok-5-video", "x-ai/grok-6-image"} {
require.False(t, isGrokUnknownTextFamilyModel(model), "model %s", model)
}
// Multimodal chat models stay token billed.
require.True(t, isGrokUnknownTextFamilyModel("grok-2-vision-1212"))
for _, model := range []string{
"grok-imagine-image-3.0",
"grok-imagine-video-2",
@@ -372,6 +372,12 @@ func TestBillingServiceHasIdentifiedTokenPricing_RejectsFamilyGuesses(t *testing
}
require.False(t, billing.HasIdentifiedTokenPricing(""))
require.False(t, billing.HasIdentifiedTokenPricing("zz-unpriced-response-model"))
// Versioned media ids may inherit a text card via GetModelPricing; the
// identified-token gate must still reject them so response-model billing
// cannot adopt grok-4.5 rates for image/audio/video ids.
require.False(t, billing.HasIdentifiedTokenPricing("grok-2-image-1212"))
require.False(t, billing.HasIdentifiedTokenPricing("grok-2-audio"))
require.False(t, billing.HasIdentifiedTokenPricing("grok-5-video"))
}
func TestGatewayServiceRecordUsage_ResponseModelRejectsUnidentifiedFamilyName(t *testing.T) {