Merge pull request #2346 from pjpjq/codex/fix-codex-capacity-retry
fix(codex): Treat Codex capacity errors as retryable
This commit is contained in:
@@ -685,13 +685,39 @@ func applyCodexHeaders(r *http.Request, auth *cliproxyauth.Auth, token string, s
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newCodexStatusErr(statusCode int, body []byte) statusErr {
|
func newCodexStatusErr(statusCode int, body []byte) statusErr {
|
||||||
err := statusErr{code: statusCode, msg: string(body)}
|
errCode := statusCode
|
||||||
if retryAfter := parseCodexRetryAfter(statusCode, body, time.Now()); retryAfter != nil {
|
if isCodexModelCapacityError(body) {
|
||||||
|
errCode = http.StatusTooManyRequests
|
||||||
|
}
|
||||||
|
err := statusErr{code: errCode, msg: string(body)}
|
||||||
|
if retryAfter := parseCodexRetryAfter(errCode, body, time.Now()); retryAfter != nil {
|
||||||
err.retryAfter = retryAfter
|
err.retryAfter = retryAfter
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isCodexModelCapacityError(errorBody []byte) bool {
|
||||||
|
if len(errorBody) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
candidates := []string{
|
||||||
|
gjson.GetBytes(errorBody, "error.message").String(),
|
||||||
|
gjson.GetBytes(errorBody, "message").String(),
|
||||||
|
string(errorBody),
|
||||||
|
}
|
||||||
|
for _, candidate := range candidates {
|
||||||
|
lower := strings.ToLower(strings.TrimSpace(candidate))
|
||||||
|
if lower == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.Contains(lower, "selected model is at capacity") ||
|
||||||
|
strings.Contains(lower, "model is at capacity. please try a different model") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func parseCodexRetryAfter(statusCode int, errorBody []byte, now time.Time) *time.Duration {
|
func parseCodexRetryAfter(statusCode int, errorBody []byte, now time.Time) *time.Duration {
|
||||||
if statusCode != http.StatusTooManyRequests || len(errorBody) == 0 {
|
if statusCode != http.StatusTooManyRequests || len(errorBody) == 0 {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -60,6 +60,19 @@ func TestParseCodexRetryAfter(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewCodexStatusErrTreatsCapacityAsRetryableRateLimit(t *testing.T) {
|
||||||
|
body := []byte(`{"error":{"message":"Selected model is at capacity. Please try a different model."}}`)
|
||||||
|
|
||||||
|
err := newCodexStatusErr(http.StatusBadRequest, body)
|
||||||
|
|
||||||
|
if got := err.StatusCode(); got != http.StatusTooManyRequests {
|
||||||
|
t.Fatalf("status code = %d, want %d", got, http.StatusTooManyRequests)
|
||||||
|
}
|
||||||
|
if err.RetryAfter() != nil {
|
||||||
|
t.Fatalf("expected nil explicit retryAfter for capacity fallback, got %v", *err.RetryAfter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func itoa(v int64) string {
|
func itoa(v int64) string {
|
||||||
return strconv.FormatInt(v, 10)
|
return strconv.FormatInt(v, 10)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user