14d46a0a5d
Move credits handling from executor-level retry to conductor-level orchestration. When all free-tier auths are exhausted (429/503), the conductor discovers auths with available Google One AI credits and retries with enabledCreditTypes injected via context flag. Key changes: - Add AntigravityCreditsHint system for tracking per-auth credits state - Conductor tries credits fallback after all auths fail (Execute/Stream/Count) - Executor injects enabledCreditTypes only when conductor sets context flag - Credits fallback respects provider scope (requires antigravity in providers) - Add context cancellation check in credits fallback to avoid wasted requests - Remove executor-level attemptCreditsFallback and preferCredits machinery - Restructure 429 decision logic (parse details first, keyword fallback) - Expand shouldAbort to cover INVALID_ARGUMENT/FAILED_PRECONDITION/500+UNKNOWN - Support human-readable retry delay parsing (e.g. "1h43m56s")
63 lines
1.6 KiB
Go
63 lines
1.6 KiB
Go
package auth
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestIsAuthBlockedForModel_ClaudeWithCreditsStillBlockedDuringCooldown(t *testing.T) {
|
|
auth := &Auth{
|
|
ID: "ag-1",
|
|
Provider: "antigravity",
|
|
ModelStates: map[string]*ModelState{
|
|
"claude-sonnet-4-6": {
|
|
Unavailable: true,
|
|
NextRetryAfter: time.Now().Add(10 * time.Minute),
|
|
Quota: QuotaState{
|
|
Exceeded: true,
|
|
NextRecoverAt: time.Now().Add(10 * time.Minute),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
SetAntigravityCreditsHint(auth.ID, AntigravityCreditsHint{
|
|
Known: true,
|
|
Available: true,
|
|
UpdatedAt: time.Now(),
|
|
})
|
|
|
|
blocked, reason, _ := isAuthBlockedForModel(auth, "claude-sonnet-4-6", time.Now())
|
|
if !blocked || reason != blockReasonCooldown {
|
|
t.Fatalf("expected auth to be blocked during cooldown even with credits, got blocked=%v reason=%v", blocked, reason)
|
|
}
|
|
}
|
|
|
|
func TestIsAuthBlockedForModel_KeepsGeminiBlockedWithoutCreditsBypass(t *testing.T) {
|
|
auth := &Auth{
|
|
ID: "ag-2",
|
|
Provider: "antigravity",
|
|
ModelStates: map[string]*ModelState{
|
|
"gemini-3-flash": {
|
|
Unavailable: true,
|
|
NextRetryAfter: time.Now().Add(10 * time.Minute),
|
|
Quota: QuotaState{
|
|
Exceeded: true,
|
|
NextRecoverAt: time.Now().Add(10 * time.Minute),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
SetAntigravityCreditsHint(auth.ID, AntigravityCreditsHint{
|
|
Known: true,
|
|
Available: true,
|
|
UpdatedAt: time.Now(),
|
|
})
|
|
|
|
blocked, reason, _ := isAuthBlockedForModel(auth, "gemini-3-flash", time.Now())
|
|
if !blocked || reason != blockReasonCooldown {
|
|
t.Fatalf("expected gemini auth to remain blocked, got blocked=%v reason=%v", blocked, reason)
|
|
}
|
|
}
|