Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions internal/executor/classify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"testing"
"time"

"github.com/caigee-cmd/cli2api/internal/accounts"
"github.com/caigee-cmd/cli2api/internal/providers"
)

Expand Down Expand Up @@ -271,3 +272,39 @@ func TestObserveStreamFailureIgnoresCancellation(test *testing.T) {
})
}
}

func TestClassifyErrorUpstreamEmptyNoCooldown(t *testing.T) {
// A heartbeat-only empty upstream stream says nothing about the account's
// health: fail over, but never cool the account down.
err := ClassifyError(&providers.Error{
Kind: accounts.KindUnavailable,
Status: http.StatusBadGateway,
Code: "upstream_empty",
Message: "upstream returned empty content (possible context overflow / silent reject)",
})
if err.Code != "upstream_empty" {
t.Errorf("expected Code='upstream_empty', got '%s'", err.Code)
}
if !err.Failover {
t.Error("expected Failover=true for upstream_empty")
}
if err.Cooldown != 0 {
t.Errorf("expected Cooldown=0 for upstream_empty, got %v", err.Cooldown)
}
if err.RetryAfter != 0 {
t.Errorf("expected RetryAfter=0 for upstream_empty, got %v", err.RetryAfter)
}
if err.Kind != accounts.KindUnavailable {
t.Errorf("expected Kind='unavailable', got '%s'", err.Kind)
}
// A plain unavailable error keeps its default cooldown so only the
// upstream_empty special case is exempt.
generic := ClassifyError(&providers.Error{
Kind: accounts.KindUnavailable,
Status: http.StatusBadGateway,
Message: "connection reset by peer",
})
if generic.Cooldown <= 0 {
t.Errorf("expected generic unavailable to keep cooldown, got %v", generic.Cooldown)
}
}
8 changes: 8 additions & 0 deletions internal/executor/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,14 @@ func ClassifyError(err error) Classified {
if providerErr.Message != "" {
classified.Message = providerErr.Message
}
if providerErr.Code == "upstream_empty" {
// A heartbeat-only upstream stream says nothing about the account's
// health: the credential, quota, and rate-limit state are all fine, and
// the same account may succeed on the very next request. Fail over so
// another account serves the turn, but never cool this one down.
classified.Failover = true
classified.Cooldown = 0
}
if classified.Kind == accounts.KindRateLimit {
switch {
case providerErr.Code == "4011" && providerErr.RetryAfter <= 0:
Expand Down
Loading