






















@@ -446,7 +446,11 @@ async function expectFallsBackToHaiku(params: {
446446447447expect(result.result).toBe("ok");
448448expect(run).toHaveBeenCalledTimes(2);
449-expect(requireMockCall(run, 1, "fallback run")).toEqual(["anthropic", "claude-haiku-3-5"]);
449+expect(requireMockCall(run, 1, "fallback run")).toEqual([
450+"anthropic",
451+"claude-haiku-3-5",
452+{ isFinalFallbackAttempt: true },
453+]);
450454}
451455452456function createOverrideFailureRun(params: {
@@ -540,7 +544,7 @@ async function expectSkippedUnavailableProvider(params: {
540544});
541545542546expect(result.result).toBe("ok");
543-expect(run.mock.calls).toEqual([["fallback", "ok-model"]]);
547+expect(run.mock.calls).toEqual([["fallback", "ok-model", { isFinalFallbackAttempt: true }]]);
544548expect(result.attempts[0]?.reason).toBe(params.expectedReason);
545549expect(result.attempts[0]?.authMode).toBe(params.expectedAuthMode);
546550}
@@ -637,7 +641,9 @@ describe("runWithModelFallback", () => {
637641"/tmp/openclaw-no-auth-profiles",
638642);
639643expect(authRuntimeMock.runtime.ensureAuthProfileStore).not.toHaveBeenCalled();
640-expect(run).toHaveBeenCalledWith("openai", "gpt-4.1-mini");
644+expect(run).toHaveBeenCalledWith("openai", "gpt-4.1-mini", {
645+isFinalFallbackAttempt: false,
646+});
641647});
642648643649it("resolves primary model aliases before running", () => {
@@ -759,7 +765,11 @@ describe("runWithModelFallback", () => {
759765});
760766expect(result.result).toBe("ok");
761767expect(run).toHaveBeenCalledTimes(2);
762-expect(requireMockCall(run, 1, "fallback run")).toEqual(["anthropic", "claude-haiku-3-5"]);
768+expect(requireMockCall(run, 1, "fallback run")).toEqual([
769+"anthropic",
770+"claude-haiku-3-5",
771+{ isFinalFallbackAttempt: false },
772+]);
763773expect(result.attempts).toHaveLength(1);
764774expect(result.attempts[0].reason).toBe("overloaded");
765775});
@@ -1070,7 +1080,11 @@ describe("runWithModelFallback", () => {
1070108010711081expect(result.result).toBe("external cli ok");
10721082expect(run).toHaveBeenCalledTimes(1);
1073-expect(run.mock.calls[0]).toEqual(["anthropic", "claude-sonnet-4-6"]);
1083+expect(run.mock.calls[0]).toEqual([
1084+"anthropic",
1085+"claude-sonnet-4-6",
1086+{ isFinalFallbackAttempt: false },
1087+]);
10741088expect(result.attempts).toStrictEqual([]);
10751089});
10761090@@ -1114,7 +1128,11 @@ describe("runWithModelFallback", () => {
1114112811151129expect(result.result).toBe("cli ok");
11161130expect(run).toHaveBeenCalledTimes(1);
1117-expect(run.mock.calls[0]).toEqual(["anthropic", "claude-sonnet-4-6"]);
1131+expect(run.mock.calls[0]).toEqual([
1132+"anthropic",
1133+"claude-sonnet-4-6",
1134+{ isFinalFallbackAttempt: true },
1135+]);
11181136expect(result.attempts).toStrictEqual([]);
11191137});
11201138@@ -1162,7 +1180,7 @@ describe("runWithModelFallback", () => {
1162118011631181expect(result.result).toBe("direct cli ok");
11641182expect(run).toHaveBeenCalledTimes(1);
1165-expect(run.mock.calls[0]).toEqual(["claude-cli", "opus"]);
1183+expect(run.mock.calls[0]).toEqual(["claude-cli", "opus", { isFinalFallbackAttempt: true }]);
11661184expect(result.attempts).toStrictEqual([]);
11671185});
11681186@@ -1467,7 +1485,11 @@ describe("runWithModelFallback", () => {
1467148514681486expect(result.result).toEqual({ payloads: [{ text: "fallback ok" }] });
14691487expect(run).toHaveBeenCalledTimes(2);
1470-expect(requireMockCall(run, 1, "fallback run")).toEqual(["anthropic", "claude-haiku-3-5"]);
1488+expect(requireMockCall(run, 1, "fallback run")).toEqual([
1489+"anthropic",
1490+"claude-haiku-3-5",
1491+{ isFinalFallbackAttempt: true },
1492+]);
14711493expect(result.attempts[0]?.provider).toBe("openai");
14721494expect(result.attempts[0]?.model).toBe("gpt-5.4");
14731495expect(result.attempts[0]?.reason).toBe("format");
@@ -1513,7 +1535,11 @@ describe("runWithModelFallback", () => {
1513153515141536expect(result.result.payloads).toEqual([{ text: "fallback ok" }]);
15151537expect(run).toHaveBeenCalledTimes(2);
1516-expect(requireMockCall(run, 1, "fallback run")).toEqual(["openai", "gpt-5.5"]);
1538+expect(requireMockCall(run, 1, "fallback run")).toEqual([
1539+"openai",
1540+"gpt-5.5",
1541+{ isFinalFallbackAttempt: true },
1542+]);
15171543expect(result.attempts[0]).toMatchObject({
15181544provider: "zai",
15191545model: "glm-5.1",
@@ -1835,8 +1861,8 @@ describe("runWithModelFallback", () => {
18351861expect(result.attempts).toStrictEqual([]);
18361862expect(onError).not.toHaveBeenCalled();
18371863expect(run.mock.calls).toEqual([
1838-["openai", "gpt-4.1-mini"],
1839-["anthropic", "claude-sonnet-4-6"],
1864+["openai", "gpt-4.1-mini", { isFinalFallbackAttempt: false }],
1865+["anthropic", "claude-sonnet-4-6", { isFinalFallbackAttempt: false }],
18401866]);
18411867});
18421868@@ -1860,8 +1886,8 @@ describe("runWithModelFallback", () => {
18601886expect(result.model).toBe("claude-haiku-3-5");
18611887expect(result.attempts[0]?.reason).toBe("unknown");
18621888expect(run.mock.calls).toEqual([
1863-["openai", "gpt-4.1-mini"],
1864-["anthropic", "claude-haiku-3-5"],
1889+["openai", "gpt-4.1-mini", { isFinalFallbackAttempt: false }],
1890+["anthropic", "claude-haiku-3-5", { isFinalFallbackAttempt: true }],
18651891]);
18661892});
18671893@@ -2083,9 +2109,9 @@ describe("runWithModelFallback", () => {
2083210920842110expect(result.result).toBe("ok");
20852111expect(run.mock.calls).toEqual([
2086-["anthropic", "claude-opus-4"],
2087-["anthropic", "claude-haiku-3-5"],
2088-["openai", "gpt-4.1-mini"],
2112+["anthropic", "claude-opus-4", { isFinalFallbackAttempt: false }],
2113+["anthropic", "claude-haiku-3-5", { isFinalFallbackAttempt: false }],
2114+["openai", "gpt-4.1-mini", { isFinalFallbackAttempt: true }],
20892115]);
20902116});
20912117@@ -2162,8 +2188,8 @@ describe("runWithModelFallback", () => {
2162218821632189expect(result.result).toBe("ok");
21642190expect(run.mock.calls).toEqual([
2165-["openrouter", "xiaomi/mimo-v2-pro"],
2166-["openai", "gpt-4.1-mini"],
2191+["openrouter", "xiaomi/mimo-v2-pro", { isFinalFallbackAttempt: false }],
2192+["openai", "gpt-4.1-mini", { isFinalFallbackAttempt: true }],
21672193]);
21682194expect(result.attempts).toHaveLength(1);
21692195expect(result.attempts[0]?.reason).toBe("billing");
@@ -2177,6 +2203,7 @@ describe("runWithModelFallback", () => {
21772203error: Error;
21782204expectedFallback: [string, string];
21792205expectedReason?: string;
2206+isFinalFallbackAttempt?: boolean;
21802207}> = [
21812208{
21822209name: "unknown anthropic override",
@@ -2199,6 +2226,7 @@ describe("runWithModelFallback", () => {
21992226error: new Error("stream_read_error"),
22002227expectedFallback: ["anthropic", "claude-haiku-3-5"],
22012228expectedReason: "timeout",
2229+isFinalFallbackAttempt: true,
22022230},
22032231];
22042232@@ -2216,7 +2244,10 @@ describe("runWithModelFallback", () => {
2216224422172245expect(result.result).toBe("ok");
22182246expect(run).toHaveBeenCalledTimes(2);
2219-expect(requireMockCall(run, 1, "fallback run")).toEqual(testCase.expectedFallback);
2247+expect(requireMockCall(run, 1, "fallback run")).toEqual([
2248+ ...testCase.expectedFallback,
2249+{ isFinalFallbackAttempt: testCase.isFinalFallbackAttempt ?? false },
2250+]);
22202251if (testCase.expectedReason) {
22212252expect(result.attempts).toHaveLength(1);
22222253expect(result.attempts[0]?.reason).toBe(testCase.expectedReason);
@@ -2376,7 +2407,7 @@ describe("runWithModelFallback", () => {
23762407});
2377240823782409expect(result.result).toBe("ok");
2379-expect(run.mock.calls).toEqual([[provider, "m1"]]);
2410+expect(run.mock.calls).toEqual([[provider, "m1", { isFinalFallbackAttempt: false }]]);
23802411expect(result.attempts).toStrictEqual([]);
23812412});
23822413@@ -2987,6 +3018,7 @@ describe("runWithModelFallback", () => {
29873018expect(run).toHaveBeenCalledTimes(1);
29883019expect(run).toHaveBeenNthCalledWith(1, "anthropic", "claude-sonnet-4-5", {
29893020allowTransientCooldownProbe: true,
3021+isFinalFallbackAttempt: false,
29903022});
29913023});
29923024@@ -3020,6 +3052,7 @@ describe("runWithModelFallback", () => {
30203052expect(run).toHaveBeenCalledTimes(1);
30213053expect(run).toHaveBeenNthCalledWith(1, "anthropic", "claude-sonnet-4-6", {
30223054allowTransientCooldownProbe: true,
3055+isFinalFallbackAttempt: false,
30233056});
30243057});
30253058@@ -3048,7 +3081,9 @@ describe("runWithModelFallback", () => {
3048308130493082expect(result.result).toBe("groq success");
30503083expect(run).toHaveBeenCalledTimes(1);
3051-expect(run).toHaveBeenNthCalledWith(1, "groq", "llama-3.3-70b-versatile");
3084+expect(run).toHaveBeenNthCalledWith(1, "groq", "llama-3.3-70b-versatile", {
3085+isFinalFallbackAttempt: true,
3086+});
30523087});
3053308830543089it("tries cross-provider fallbacks when same provider has rate limit", async () => {
@@ -3096,8 +3131,11 @@ describe("runWithModelFallback", () => {
30963131expect(run).toHaveBeenCalledTimes(2);
30973132expect(run).toHaveBeenNthCalledWith(1, "anthropic", "claude-opus-4-6", {
30983133allowTransientCooldownProbe: true,
3134+isFinalFallbackAttempt: false,
3135+});
3136+expect(run).toHaveBeenNthCalledWith(2, "groq", "llama-3.3-70b-versatile", {
3137+isFinalFallbackAttempt: true,
30993138});
3100-expect(run).toHaveBeenNthCalledWith(2, "groq", "llama-3.3-70b-versatile");
31013139});
3102314031033141it("limits cooldown probes to one per provider before moving to cross-provider fallback", async () => {
@@ -3134,8 +3172,11 @@ describe("runWithModelFallback", () => {
31343172expect(run).toHaveBeenCalledTimes(2);
31353173expect(run).toHaveBeenNthCalledWith(1, "anthropic", "claude-opus-4-6", {
31363174allowTransientCooldownProbe: true,
3175+isFinalFallbackAttempt: false,
3176+});
3177+expect(run).toHaveBeenNthCalledWith(2, "groq", "llama-3.3-70b-versatile", {
3178+isFinalFallbackAttempt: true,
31373179});
3138-expect(run).toHaveBeenNthCalledWith(2, "groq", "llama-3.3-70b-versatile");
31393180});
3140318131413182it("does not consume transient probe slot when first same-provider probe fails with model_not_found", async () => {
@@ -3172,9 +3213,11 @@ describe("runWithModelFallback", () => {
31723213expect(run).toHaveBeenCalledTimes(2);
31733214expect(run).toHaveBeenNthCalledWith(1, "anthropic", "claude-opus-4-6", {
31743215allowTransientCooldownProbe: true,
3216+isFinalFallbackAttempt: false,
31753217});
31763218expect(run).toHaveBeenNthCalledWith(2, "anthropic", "claude-sonnet-4-5", {
31773219allowTransientCooldownProbe: true,
3220+isFinalFallbackAttempt: false,
31783221});
31793222});
31803223});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。