fix(openai): persist passthrough 429 rate limits
This commit is contained in:
@@ -2598,6 +2598,12 @@ func (s *OpenAIGatewayService) handleErrorResponsePassthrough(
|
|||||||
}
|
}
|
||||||
setOpsUpstreamError(c, resp.StatusCode, upstreamMsg, upstreamDetail)
|
setOpsUpstreamError(c, resp.StatusCode, upstreamMsg, upstreamDetail)
|
||||||
logOpenAIInstructionsRequiredDebug(ctx, c, account, resp.StatusCode, upstreamMsg, requestBody, body)
|
logOpenAIInstructionsRequiredDebug(ctx, c, account, resp.StatusCode, upstreamMsg, requestBody, body)
|
||||||
|
if s.rateLimitService != nil {
|
||||||
|
// Passthrough mode preserves the raw upstream error response, but runtime
|
||||||
|
// account state still needs to be updated so sticky routing can stop
|
||||||
|
// reusing a freshly rate-limited account.
|
||||||
|
_ = s.rateLimitService.HandleUpstreamError(ctx, account, resp.StatusCode, resp.Header, body)
|
||||||
|
}
|
||||||
appendOpsUpstreamError(c, OpsUpstreamErrorEvent{
|
appendOpsUpstreamError(c, OpsUpstreamErrorEvent{
|
||||||
Platform: account.Platform,
|
Platform: account.Platform,
|
||||||
AccountID: account.ID,
|
AccountID: account.ID,
|
||||||
|
|||||||
@@ -536,6 +536,55 @@ func TestOpenAIGatewayService_OAuthPassthrough_UpstreamErrorIncludesPassthroughF
|
|||||||
require.True(t, arr[len(arr)-1].Passthrough)
|
require.True(t, arr[len(arr)-1].Passthrough)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOpenAIGatewayService_OAuthPassthrough_429PersistsRateLimit(t *testing.T) {
|
||||||
|
gin.SetMode(gin.TestMode)
|
||||||
|
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
c, _ := gin.CreateTestContext(rec)
|
||||||
|
c.Request = httptest.NewRequest(http.MethodPost, "/v1/responses", bytes.NewReader(nil))
|
||||||
|
c.Request.Header.Set("User-Agent", "codex_cli_rs/0.1.0")
|
||||||
|
|
||||||
|
originalBody := []byte(`{"model":"gpt-5.2","stream":false,"instructions":"local-test-instructions","input":[{"type":"text","text":"hi"}]}`)
|
||||||
|
resetAt := time.Now().Add(7 * 24 * time.Hour).Unix()
|
||||||
|
resp := &http.Response{
|
||||||
|
StatusCode: http.StatusTooManyRequests,
|
||||||
|
Header: http.Header{
|
||||||
|
"Content-Type": []string{"application/json"},
|
||||||
|
"x-request-id": []string{"rid-rate-limit"},
|
||||||
|
},
|
||||||
|
Body: io.NopCloser(strings.NewReader(fmt.Sprintf(`{"error":{"message":"The usage limit has been reached","type":"usage_limit_reached","resets_at":%d}}`, resetAt))),
|
||||||
|
}
|
||||||
|
upstream := &httpUpstreamRecorder{resp: resp}
|
||||||
|
repo := &openAIWSRateLimitSignalRepo{}
|
||||||
|
rateSvc := &RateLimitService{accountRepo: repo}
|
||||||
|
|
||||||
|
svc := &OpenAIGatewayService{
|
||||||
|
cfg: &config.Config{Gateway: config.GatewayConfig{ForceCodexCLI: false}},
|
||||||
|
httpUpstream: upstream,
|
||||||
|
rateLimitService: rateSvc,
|
||||||
|
}
|
||||||
|
|
||||||
|
account := &Account{
|
||||||
|
ID: 123,
|
||||||
|
Name: "acc",
|
||||||
|
Platform: PlatformOpenAI,
|
||||||
|
Type: AccountTypeOAuth,
|
||||||
|
Concurrency: 1,
|
||||||
|
Credentials: map[string]any{"access_token": "oauth-token", "chatgpt_account_id": "chatgpt-acc"},
|
||||||
|
Extra: map[string]any{"openai_passthrough": true},
|
||||||
|
Status: StatusActive,
|
||||||
|
Schedulable: true,
|
||||||
|
RateMultiplier: f64p(1),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := svc.Forward(context.Background(), c, account, originalBody)
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Equal(t, http.StatusTooManyRequests, rec.Code)
|
||||||
|
require.Contains(t, rec.Body.String(), "usage_limit_reached")
|
||||||
|
require.Len(t, repo.rateLimitCalls, 1)
|
||||||
|
require.WithinDuration(t, time.Unix(resetAt, 0), repo.rateLimitCalls[0], 2*time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
func TestOpenAIGatewayService_OAuthPassthrough_NonCodexUAFallbackToCodexUA(t *testing.T) {
|
func TestOpenAIGatewayService_OAuthPassthrough_NonCodexUAFallbackToCodexUA(t *testing.T) {
|
||||||
gin.SetMode(gin.TestMode)
|
gin.SetMode(gin.TestMode)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user