Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
477ee5f9
Commit
477ee5f9
authored
Apr 21, 2026
by
KnowSky404
Committed by
陈曦
Apr 27, 2026
Browse files
fix: reconcile openai admin test rate-limit state
parent
631b734e
Changes
3
Hide whitespace changes
Inline
Side-by-side
backend/internal/service/account_test_service.go
View file @
477ee5f9
...
...
@@ -538,6 +538,9 @@ func (s *AccountTestService) testOpenAIAccountConnection(c *gin.Context, account
if
resp
.
StatusCode
!=
http
.
StatusOK
{
body
,
_
:=
io
.
ReadAll
(
resp
.
Body
)
if
resp
.
StatusCode
==
http
.
StatusTooManyRequests
{
s
.
reconcileOpenAI429State
(
ctx
,
account
,
resp
.
Header
,
body
)
}
// 401 Unauthorized: 标记账号为永久错误
if
resp
.
StatusCode
==
http
.
StatusUnauthorized
&&
s
.
accountRepo
!=
nil
{
errMsg
:=
fmt
.
Sprintf
(
"Authentication failed (401): %s"
,
string
(
body
))
...
...
@@ -550,6 +553,39 @@ func (s *AccountTestService) testOpenAIAccountConnection(c *gin.Context, account
return
s
.
processOpenAIStream
(
c
,
resp
.
Body
)
}
func
(
s
*
AccountTestService
)
reconcileOpenAI429State
(
ctx
context
.
Context
,
account
*
Account
,
headers
http
.
Header
,
body
[]
byte
)
{
if
s
==
nil
||
s
.
accountRepo
==
nil
||
account
==
nil
{
return
}
var
resetAt
*
time
.
Time
if
calculated
:=
calculateOpenAI429ResetTime
(
headers
);
calculated
!=
nil
{
resetAt
=
calculated
}
else
if
unixTs
:=
parseOpenAIRateLimitResetTime
(
body
);
unixTs
!=
nil
{
t
:=
time
.
Unix
(
*
unixTs
,
0
)
resetAt
=
&
t
}
if
resetAt
==
nil
{
return
}
if
err
:=
s
.
accountRepo
.
SetRateLimited
(
ctx
,
account
.
ID
,
*
resetAt
);
err
!=
nil
{
return
}
now
:=
time
.
Now
()
account
.
RateLimitedAt
=
&
now
account
.
RateLimitResetAt
=
resetAt
if
account
.
Status
==
StatusError
{
if
err
:=
s
.
accountRepo
.
ClearError
(
ctx
,
account
.
ID
);
err
!=
nil
{
return
}
account
.
Status
=
StatusActive
account
.
ErrorMessage
=
""
}
}
// testGeminiAccountConnection tests a Gemini account's connection
func
(
s
*
AccountTestService
)
testGeminiAccountConnection
(
c
*
gin
.
Context
,
account
*
Account
,
modelID
string
,
prompt
string
)
error
{
ctx
:=
c
.
Request
.
Context
()
...
...
backend/internal/service/account_test_service_openai_test.go
View file @
477ee5f9
...
...
@@ -61,9 +61,10 @@ func newTestContext() (*gin.Context, *httptest.ResponseRecorder) {
type
openAIAccountTestRepo
struct
{
mockAccountRepoForGemini
updatedExtra
map
[
string
]
any
updatedExtra
map
[
string
]
any
rateLimitedID
int64
rateLimitedAt
*
time
.
Time
clearedErrorID
int64
}
func
(
r
*
openAIAccountTestRepo
)
UpdateExtra
(
_
context
.
Context
,
_
int64
,
updates
map
[
string
]
any
)
error
{
...
...
@@ -77,6 +78,11 @@ func (r *openAIAccountTestRepo) SetRateLimited(_ context.Context, id int64, rese
return
nil
}
func
(
r
*
openAIAccountTestRepo
)
ClearError
(
_
context
.
Context
,
id
int64
)
error
{
r
.
clearedErrorID
=
id
return
nil
}
func
TestAccountTestService_OpenAISuccessPersistsSnapshotFromHeaders
(
t
*
testing
.
T
)
{
gin
.
SetMode
(
gin
.
TestMode
)
ctx
,
recorder
:=
newTestContext
()
...
...
@@ -111,11 +117,11 @@ func TestAccountTestService_OpenAISuccessPersistsSnapshotFromHeaders(t *testing.
require
.
Contains
(
t
,
recorder
.
Body
.
String
(),
"test_complete"
)
}
func
TestAccountTestService_OpenAI429PersistsSnapshot
Without
RateLimit
(
t
*
testing
.
T
)
{
func
TestAccountTestService_OpenAI429PersistsSnapshot
And
RateLimit
State
(
t
*
testing
.
T
)
{
gin
.
SetMode
(
gin
.
TestMode
)
ctx
,
_
:=
newTestContext
()
resp
:=
newJSONResponse
(
http
.
StatusTooManyRequests
,
`{"error":{"type":"usage_limit_reached","message":"limit reached"}}`
)
resp
:=
newJSONResponse
(
http
.
StatusTooManyRequests
,
`{"error":{"type":"usage_limit_reached","message":"limit reached"
,"resets_at":1777283883
}}`
)
resp
.
Header
.
Set
(
"x-codex-primary-used-percent"
,
"100"
)
resp
.
Header
.
Set
(
"x-codex-primary-reset-after-seconds"
,
"604800"
)
resp
.
Header
.
Set
(
"x-codex-primary-window-minutes"
,
"10080"
)
...
...
@@ -130,6 +136,7 @@ func TestAccountTestService_OpenAI429PersistsSnapshotWithoutRateLimit(t *testing
ID
:
88
,
Platform
:
PlatformOpenAI
,
Type
:
AccountTypeOAuth
,
Status
:
StatusError
,
Concurrency
:
1
,
Credentials
:
map
[
string
]
any
{
"access_token"
:
"test-token"
},
}
...
...
@@ -138,7 +145,7 @@ func TestAccountTestService_OpenAI429PersistsSnapshotWithoutRateLimit(t *testing
require
.
Error
(
t
,
err
)
require
.
NotEmpty
(
t
,
repo
.
updatedExtra
)
require
.
Equal
(
t
,
100.0
,
repo
.
updatedExtra
[
"codex_5h_used_percent"
])
require
.
Zero
(
t
,
repo
.
rateLimitedID
)
require
.
Nil
(
t
,
repo
.
rateLimitedAt
)
require
.
Ni
l
(
t
,
account
.
RateLimitResetAt
)
require
.
Equal
(
t
,
account
.
ID
,
repo
.
rateLimitedID
)
require
.
Not
Nil
(
t
,
repo
.
rateLimitedAt
)
require
.
Equa
l
(
t
,
account
.
ID
,
repo
.
clearedErrorID
)
}
backend/internal/service/ratelimit_service.go
View file @
477ee5f9
...
...
@@ -931,7 +931,7 @@ func (s *RateLimitService) handle429(ctx context.Context, account *Account, head
// calculateOpenAI429ResetTime 从 OpenAI 429 响应头计算正确的重置时间
// 返回 nil 表示无法从响应头中确定重置时间
func
(
s
*
RateLimitService
)
calculateOpenAI429ResetTime
(
headers
http
.
Header
)
*
time
.
Time
{
func
calculateOpenAI429ResetTime
(
headers
http
.
Header
)
*
time
.
Time
{
snapshot
:=
ParseCodexRateLimitHeaders
(
headers
)
if
snapshot
==
nil
{
return
nil
...
...
@@ -977,6 +977,10 @@ func (s *RateLimitService) calculateOpenAI429ResetTime(headers http.Header) *tim
return
nil
}
func
(
s
*
RateLimitService
)
calculateOpenAI429ResetTime
(
headers
http
.
Header
)
*
time
.
Time
{
return
calculateOpenAI429ResetTime
(
headers
)
}
// anthropic429Result holds the parsed Anthropic 429 rate-limit information.
type
anthropic429Result
struct
{
resetAt
time
.
Time
// The correct reset time to use for SetRateLimited
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment