mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-07 16:08:02 +08:00
完善 Grok Realtime 与默认映射测试
This commit is contained in:
@@ -57,9 +57,15 @@ func (h *OpenAIGatewayHandler) GrokRealtime(c *gin.Context) {
|
||||
var release func()
|
||||
var token string
|
||||
var upstream *service.GrokRealtimeUpstream
|
||||
var candidateSeen bool
|
||||
for attempts := 0; attempts < 4; attempts++ {
|
||||
// Realtime's voice model is not a text-model capability. Passing a
|
||||
// concrete text model here would reject accounts mapped only to an
|
||||
// older/default text model before the upstream handshake can decide.
|
||||
// An empty requested model keeps account selection capability-based;
|
||||
// the actual voice model remains in the upstream WS query below.
|
||||
candidate, _, selectErr := h.gatewayService.SelectAccountWithSchedulerForCapability(
|
||||
c.Request.Context(), apiKey.GroupID, "", "", "grok-4.6", failed,
|
||||
c.Request.Context(), apiKey.GroupID, "", "", "", failed,
|
||||
service.OpenAIUpstreamTransportHTTPSSE,
|
||||
service.OpenAIEndpointCapabilityChatCompletions,
|
||||
false, false, false, service.PlatformGrok,
|
||||
@@ -67,6 +73,7 @@ func (h *OpenAIGatewayHandler) GrokRealtime(c *gin.Context) {
|
||||
if selectErr != nil || candidate == nil || candidate.Account == nil {
|
||||
break
|
||||
}
|
||||
candidateSeen = true
|
||||
account := candidate.Account
|
||||
var streamStarted bool
|
||||
var slotStatus openAISlotAcquireResult
|
||||
@@ -86,7 +93,7 @@ func (h *OpenAIGatewayHandler) GrokRealtime(c *gin.Context) {
|
||||
failed[account.ID] = struct{}{}
|
||||
continue
|
||||
}
|
||||
probeCtx, cancelProbe := context.WithTimeout(c.Request.Context(), 15*time.Second)
|
||||
probeCtx, cancelProbe := context.WithTimeout(c.Request.Context(), service.DefaultGrokRealtimeDialTimeout)
|
||||
candidateUpstream, openErr := h.gatewayService.OpenGrokRealtime(probeCtx, account, token, model)
|
||||
cancelProbe()
|
||||
if openErr != nil {
|
||||
@@ -100,7 +107,11 @@ func (h *OpenAIGatewayHandler) GrokRealtime(c *gin.Context) {
|
||||
break
|
||||
}
|
||||
if selection == nil || selection.Account == nil || release == nil || upstream == nil {
|
||||
h.errorResponse(c, http.StatusBadGateway, "upstream_error", "Grok realtime upstream unavailable")
|
||||
if !candidateSeen {
|
||||
h.errorResponse(c, http.StatusServiceUnavailable, "api_error", "No available Grok accounts")
|
||||
} else {
|
||||
h.errorResponse(c, http.StatusBadGateway, "upstream_error", "Grok realtime upstream unavailable")
|
||||
}
|
||||
return
|
||||
}
|
||||
defer release()
|
||||
|
||||
@@ -672,7 +672,7 @@ func TestResolveOpenAIMessagesDispatchMappedModel(t *testing.T) {
|
||||
Platform: service.PlatformGrok,
|
||||
},
|
||||
}
|
||||
require.Equal(t, "grok-4.5", resolveOpenAIMessagesDispatchMappedModel(nil, apiKey, "claude-sonnet-4-5"))
|
||||
require.Equal(t, "grok-4.6", resolveOpenAIMessagesDispatchMappedModel(nil, apiKey, "claude-sonnet-4-5"))
|
||||
require.Empty(t, resolveOpenAIMessagesDispatchMappedModel(nil, apiKey, "grok"))
|
||||
})
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ const (
|
||||
AccountTestModeGrokRealtime = "realtime"
|
||||
|
||||
defaultGrokRealtimeTestModel = "grok-voice-latest"
|
||||
grokRealtimeProbeTimeout = 12 * time.Second
|
||||
grokRealtimeProbeTimeout = DefaultGrokRealtimeDialTimeout
|
||||
)
|
||||
|
||||
// isOpenAIImageModel checks if the model is an OpenAI image generation model (e.g. gpt-image-2).
|
||||
|
||||
@@ -16,6 +16,10 @@ import (
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
// DefaultGrokRealtimeDialTimeout bounds the pre-accept upstream handshake.
|
||||
// The timeout only covers dialing; an established session is not interrupted.
|
||||
const DefaultGrokRealtimeDialTimeout = 12 * time.Second
|
||||
|
||||
// supportedGrokVoiceHTTPEndpoints are xAI Voice HTTP paths we forward as-is.
|
||||
var supportedGrokVoiceHTTPEndpoints = map[string]struct{}{
|
||||
"tts": {},
|
||||
|
||||
@@ -251,7 +251,7 @@ func TestForwardGrokChatViaResponsesNonStreamingCachesAndReturnsChat(t *testing.
|
||||
require.NotNil(t, result)
|
||||
require.Equal(t, xai.DefaultCLIBaseURL+"/responses", upstream.lastReq.URL.String())
|
||||
require.Equal(t, grokChatResponsesEndpoint, result.UpstreamEndpoint)
|
||||
require.Equal(t, "grok-4.5", result.UpstreamModel)
|
||||
require.Equal(t, "grok-4.6", result.UpstreamModel)
|
||||
require.Equal(t, 9908, result.Usage.InputTokens)
|
||||
require.Equal(t, 12, result.Usage.OutputTokens)
|
||||
require.Equal(t, 9856, result.Usage.CacheReadInputTokens)
|
||||
@@ -352,7 +352,7 @@ func TestForwardGrokChatViaResponsesCodeBuddyUsesStableConversationHeader(t *tes
|
||||
c.Request.Header.Set("X-Request-ID", "generic-"+tt.requestID)
|
||||
c.Set("api_key", &APIKey{ID: 7111})
|
||||
|
||||
identity := resolveGrokCacheIdentity(c, tt.body, "", "grok-4.5")
|
||||
identity := resolveGrokCacheIdentity(c, tt.body, "", "grok-4.6")
|
||||
require.NotEmpty(t, identity)
|
||||
if index == 0 {
|
||||
stableIdentity = identity
|
||||
@@ -406,8 +406,8 @@ func TestForwardGrokChatViaResponsesTraeToolHistoryKeepsCacheRoute(t *testing.T)
|
||||
accountRepo: repo,
|
||||
}
|
||||
|
||||
firstTurnIdentity := resolveGrokCacheIdentity(c, firstTurnBody, "", "grok-4.5")
|
||||
extendedTurnIdentity := resolveGrokCacheIdentity(c, body, "", "grok-4.5")
|
||||
firstTurnIdentity := resolveGrokCacheIdentity(c, firstTurnBody, "", "grok-4.6")
|
||||
extendedTurnIdentity := resolveGrokCacheIdentity(c, body, "", "grok-4.6")
|
||||
require.NotEmpty(t, firstTurnIdentity)
|
||||
require.Equal(t, firstTurnIdentity, extendedTurnIdentity)
|
||||
|
||||
@@ -463,8 +463,8 @@ func TestForwardGrokChatViaResponsesTraeCompatibilityFieldsKeepCacheRoute(t *tes
|
||||
accountRepo: repo,
|
||||
}
|
||||
|
||||
firstTurnIdentity := resolveGrokCacheIdentity(c, firstTurnBody, "", "grok-4.5")
|
||||
extendedTurnIdentity := resolveGrokCacheIdentity(c, body, "", "grok-4.5")
|
||||
firstTurnIdentity := resolveGrokCacheIdentity(c, firstTurnBody, "", "grok-4.6")
|
||||
extendedTurnIdentity := resolveGrokCacheIdentity(c, body, "", "grok-4.6")
|
||||
require.NotEmpty(t, firstTurnIdentity)
|
||||
require.Equal(t, firstTurnIdentity, extendedTurnIdentity)
|
||||
|
||||
@@ -534,7 +534,7 @@ func TestForwardGrokChatRuntimeGateFallsBackToRaw(t *testing.T) {
|
||||
mappedModel string
|
||||
wantUpstream string
|
||||
}{
|
||||
{name: "missing cache identity", wantUpstream: "grok-4.5"},
|
||||
{name: "missing cache identity", wantUpstream: "grok-4.6"},
|
||||
{name: "non cache capable mapped model", setAPIKey: true, mappedModel: "grok-4.3", wantUpstream: "grok-4.3"},
|
||||
}
|
||||
|
||||
|
||||
@@ -1791,7 +1791,7 @@ func TestForwardAsChatCompletionsForGrokStopFallsBackToXAIChatCompletions(t *tes
|
||||
require.Equal(t, "Bearer access-token", upstream.lastReq.Header.Get("Authorization"))
|
||||
require.NotEmpty(t, upstream.lastReq.Header.Get(grokConversationIDHeader))
|
||||
require.NotEqual(t, "raw-client-cache-key", upstream.lastReq.Header.Get(grokConversationIDHeader))
|
||||
require.Equal(t, "grok-4.5", gjson.GetBytes(upstream.lastBody, "model").String())
|
||||
require.Equal(t, "grok-4.6", gjson.GetBytes(upstream.lastBody, "model").String())
|
||||
require.False(t, gjson.GetBytes(upstream.lastBody, "prompt_cache_key").Exists())
|
||||
require.Equal(t, "grok", result.Model)
|
||||
require.Equal(t, "grok-4.5", result.UpstreamModel)
|
||||
@@ -1848,7 +1848,7 @@ func TestForwardGrokResponsesStreamingDefaultsEmptyModelTo45AndSnapshots(t *test
|
||||
require.Equal(t, xai.DefaultCLIBaseURL+"/responses", upstream.lastReq.URL.String())
|
||||
require.Equal(t, "Bearer access-token", upstream.lastReq.Header.Get("Authorization"))
|
||||
require.Equal(t, "responses=experimental", upstream.lastReq.Header.Get("OpenAI-Beta"))
|
||||
require.Equal(t, "grok-4.5", gjson.GetBytes(upstream.lastBody, "model").String())
|
||||
require.Equal(t, "grok-4.6", gjson.GetBytes(upstream.lastBody, "model").String())
|
||||
require.NotEmpty(t, gjson.GetBytes(upstream.lastBody, "prompt_cache_key").String())
|
||||
require.Equal(t, gjson.GetBytes(upstream.lastBody, "prompt_cache_key").String(), upstream.lastReq.Header.Get(grokConversationIDHeader))
|
||||
require.Equal(t, "web_search", gjson.GetBytes(upstream.lastBody, "tools.0.type").String())
|
||||
@@ -1908,7 +1908,7 @@ func TestForwardGrokResponsesAPIKeyUsesXAIResponses(t *testing.T) {
|
||||
require.Equal(t, "Bearer xai-test-key", upstream.lastReq.Header.Get("Authorization"))
|
||||
require.Empty(t, upstream.lastReq.Header.Get("X-Grok-Client-Version"))
|
||||
require.NotEqual(t, grokUpstreamUserAgent, upstream.lastReq.Header.Get("User-Agent"))
|
||||
require.Equal(t, "grok-4.5", gjson.GetBytes(upstream.lastBody, "model").String())
|
||||
require.Equal(t, "grok-4.6", gjson.GetBytes(upstream.lastBody, "model").String())
|
||||
require.Equal(t, "resp_grok_api_key", result.ResponseID)
|
||||
require.Equal(t, 2, result.Usage.InputTokens)
|
||||
require.Equal(t, 1, result.Usage.OutputTokens)
|
||||
@@ -2410,7 +2410,7 @@ func TestForwardGrokResponsesNonStreamingUsesCacheIdentityAndCachedUsage(t *test
|
||||
require.Equal(t, 7, result.Usage.InputTokens)
|
||||
require.Equal(t, 2, result.Usage.OutputTokens)
|
||||
require.Equal(t, 4, result.Usage.CacheReadInputTokens)
|
||||
require.Equal(t, "grok-4.5", gjson.GetBytes(upstream.lastBody, "model").String())
|
||||
require.Equal(t, "grok-4.6", gjson.GetBytes(upstream.lastBody, "model").String())
|
||||
identity := gjson.GetBytes(upstream.lastBody, "prompt_cache_key").String()
|
||||
require.NotEmpty(t, identity)
|
||||
require.Equal(t, identity, upstream.lastReq.Header.Get(grokConversationIDHeader))
|
||||
|
||||
@@ -1187,7 +1187,7 @@ func TestProxyResponsesWebSocketFromClientForGrokUsesXAIHTTPBridgeAndPreservesMa
|
||||
require.Equal(t, "Bearer access-token", upstream.lastReq.Header.Get("Authorization"))
|
||||
require.Equal(t, xai.CLIUserAgent(xai.CLIClientVersion), upstream.lastReq.Header.Get("User-Agent"))
|
||||
require.Equal(t, grokCLIVersion, upstream.lastReq.Header.Get("X-Grok-Client-Version"))
|
||||
require.Equal(t, "grok-4.5", gjson.GetBytes(upstream.bodies[0], "model").String())
|
||||
require.Equal(t, "grok-4.6", gjson.GetBytes(upstream.bodies[0], "model").String())
|
||||
require.Equal(t, "grok-4.3", gjson.GetBytes(upstream.bodies[1], "model").String())
|
||||
require.Equal(t, "grok-4.3", gjson.GetBytes(upstream.bodies[2], "model").String())
|
||||
require.NotEmpty(t, gjson.GetBytes(upstream.lastBody, "prompt_cache_key").String())
|
||||
|
||||
Reference in New Issue
Block a user