mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 07:38:03 +00:00
fix: address bot comments and CI failures
- Remove duplicate isPrimary variable declaration (Greptile feedback) - Revert provider cooldown changes to preserve existing behavior (Codex feedback) - Focus PR scope on Bug A only (session override issue) - All tests passing including model-fallback.probe.test.ts Changes: - Fixed session model override comparison (Bug A) ✅ - Removed aggressive cooldown changes that broke existing tests ❌ - Preserved backwards compatibility with @deprecated function ✅ - 30/30 model-fallback tests passing, 11/11 probe tests passing This PR now focuses solely on the session override issue that prevents fallbacks when users switch models for quota management.
This commit is contained in:
committed by
Gustavo Madeira Santana
parent
fb6c9c83b8
commit
0db091bf86
@@ -917,99 +917,7 @@ describe("runWithModelFallback", () => {
|
||||
});
|
||||
});
|
||||
|
||||
// Tests for Bug B fix: Fallback with provider-level cooldowns
|
||||
describe("fallback behavior with provider cooldowns", () => {
|
||||
async function makeAuthStoreWithCooldown(
|
||||
provider: string,
|
||||
): Promise<{ store: AuthProfileStore; dir: string }> {
|
||||
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-test-"));
|
||||
const store: AuthProfileStore = {
|
||||
version: AUTH_STORE_VERSION,
|
||||
profiles: {
|
||||
[`${provider}:default`]: { type: "api_key", provider, key: "test-key" },
|
||||
},
|
||||
usageStats: {
|
||||
[`${provider}:default`]: { cooldownUntil: Date.now() + 300000 }, // 5 min cooldown
|
||||
},
|
||||
};
|
||||
saveAuthProfileStore(store, tmpDir);
|
||||
return { store, dir: tmpDir };
|
||||
}
|
||||
|
||||
it("attempts same-provider fallbacks even during cooldown", async () => {
|
||||
const { dir } = await makeAuthStoreWithCooldown("anthropic");
|
||||
const cfg = makeCfg({
|
||||
agents: {
|
||||
defaults: {
|
||||
model: {
|
||||
primary: "anthropic/claude-opus-4-6",
|
||||
fallbacks: ["anthropic/claude-sonnet-4-5", "groq/llama-3.3-70b-versatile"],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const run = vi.fn().mockResolvedValueOnce("sonnet success"); // First call (sonnet) succeeds
|
||||
|
||||
const result = await runWithModelFallback({
|
||||
cfg,
|
||||
provider: "anthropic",
|
||||
model: "claude-opus-4-6",
|
||||
run,
|
||||
agentDir: dir,
|
||||
});
|
||||
|
||||
expect(result.result).toBe("sonnet success");
|
||||
expect(run).toHaveBeenCalledTimes(1); // Primary skipped due to cooldown, fallback tried
|
||||
expect(run).toHaveBeenNthCalledWith(1, "anthropic", "claude-sonnet-4-5"); // Fallback attempted despite cooldown
|
||||
});
|
||||
|
||||
it("tries cross-provider fallbacks when same provider in cooldown", async () => {
|
||||
// Create auth store with both anthropic (in cooldown) and groq (available)
|
||||
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-test-"));
|
||||
const store: AuthProfileStore = {
|
||||
version: AUTH_STORE_VERSION,
|
||||
profiles: {
|
||||
"anthropic:default": { type: "api_key", provider: "anthropic", key: "test-key" },
|
||||
"groq:default": { type: "api_key", provider: "groq", key: "test-key" },
|
||||
},
|
||||
usageStats: {
|
||||
"anthropic:default": { cooldownUntil: Date.now() + 300000 }, // Anthropic in cooldown
|
||||
// Groq NOT in cooldown
|
||||
},
|
||||
};
|
||||
saveAuthProfileStore(store, tmpDir);
|
||||
|
||||
const cfg = makeCfg({
|
||||
agents: {
|
||||
defaults: {
|
||||
model: {
|
||||
primary: "anthropic/claude-opus-4-6",
|
||||
fallbacks: ["anthropic/claude-sonnet-4-5", "groq/llama-3.3-70b-versatile"],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const run = vi
|
||||
.fn()
|
||||
.mockRejectedValueOnce(new Error("Still rate limited")) // Same provider fallback fails
|
||||
.mockResolvedValueOnce("groq success"); // Different provider works
|
||||
|
||||
const result = await runWithModelFallback({
|
||||
cfg,
|
||||
provider: "anthropic",
|
||||
model: "claude-opus-4-6",
|
||||
run,
|
||||
agentDir: tmpDir,
|
||||
});
|
||||
|
||||
expect(result.result).toBe("groq success");
|
||||
expect(run).toHaveBeenCalledTimes(2); // Primary skipped, sonnet tried, groq succeeds
|
||||
expect(run).toHaveBeenNthCalledWith(1, "anthropic", "claude-sonnet-4-5"); // Fallback attempted despite cooldown
|
||||
expect(run).toHaveBeenNthCalledWith(2, "groq", "llama-3.3-70b-versatile");
|
||||
});
|
||||
});
|
||||
// Provider cooldown behavior preserved - focusing on Bug A (session overrides) only
|
||||
});
|
||||
|
||||
describe("runWithImageModelFallback", () => {
|
||||
|
||||
@@ -391,12 +391,10 @@ export async function runWithModelFallback<T>(params: {
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isPrimary && shouldProbe) {
|
||||
// Primary model probe: attempt it despite cooldown to detect recovery.
|
||||
lastProbeAttempt.set(probeThrottleKey, now);
|
||||
}
|
||||
// For fallback models or probed primaries, continue to attempt the model
|
||||
// Primary model probe: attempt it despite cooldown to detect recovery.
|
||||
// If it fails, the error is caught below and we fall through to the
|
||||
// next candidate as usual.
|
||||
lastProbeAttempt.set(probeThrottleKey, now);
|
||||
}
|
||||
}
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user