



















11import { beforeEach, describe, expect, it } from "vitest";
22import {
33loadRunCronIsolatedAgentTurn,
4+logWarnMock,
45makeCronSession,
6+mockRunCronFallbackPassthrough,
57preflightCronModelProviderMock,
68resolveConfiguredModelRefMock,
79resolveCronSessionMock,
810resetRunCronIsolatedAgentTurnHarness,
911runEmbeddedAgentMock,
12+runWithModelFallbackMock,
1013} from "./isolated-agent/run.test-harness.js";
11141215const runCronIsolatedAgentTurn = await loadRunCronIsolatedAgentTurn();
@@ -43,6 +46,14 @@ describe("runCronIsolatedAgentTurn model provider preflight", () => {
43464447const result = await runCronIsolatedAgentTurn({
4548cfg: {
49+agents: {
50+defaults: {
51+model: {
52+primary: "ollama/qwen3:32b",
53+fallbacks: [],
54+},
55+},
56+},
4657models: {
4758providers: {
4859ollama: {
@@ -79,4 +90,135 @@ describe("runCronIsolatedAgentTurn model provider preflight", () => {
7990expect(result.error).toContain("local provider endpoint is not reachable");
8091expect(runEmbeddedAgentMock).not.toHaveBeenCalled();
8192});
93+94+it("continues with configured fallback when the local primary preflight is unavailable", async () => {
95+mockRunCronFallbackPassthrough();
96+preflightCronModelProviderMock.mockResolvedValueOnce({
97+status: "unavailable",
98+reason:
99+"Agent cron job uses ollama/qwen3:32b but the local provider endpoint is not reachable at http://127.0.0.1:11434.",
100+provider: "ollama",
101+model: "qwen3:32b",
102+baseUrl: "http://127.0.0.1:11434",
103+retryAfterMs: 300000,
104+});
105+106+const result = await runCronIsolatedAgentTurn({
107+cfg: {
108+agents: {
109+defaults: {
110+model: {
111+primary: "ollama/qwen3:32b",
112+fallbacks: ["openrouter/nvidia/nemotron-3-super-120b-a12b:free", "openai/gpt-5.4"],
113+},
114+},
115+},
116+models: {
117+providers: {
118+ollama: {
119+api: "ollama",
120+baseUrl: "http://127.0.0.1:11434",
121+models: [],
122+},
123+openrouter: {
124+api: "openai-completions",
125+baseUrl: "https://openrouter.ai/api/v1",
126+models: [],
127+},
128+},
129+},
130+},
131+deps: {} as never,
132+job: {
133+id: "fallback-from-dead-ollama",
134+name: "Fallback From Dead Ollama",
135+enabled: true,
136+createdAtMs: 0,
137+updatedAtMs: 0,
138+schedule: { kind: "cron", expr: "*/5 * * * *", tz: "UTC" },
139+sessionTarget: "isolated",
140+state: {},
141+wakeMode: "next-heartbeat",
142+payload: { kind: "agentTurn", message: "summarize" },
143+delivery: { mode: "none" },
144+},
145+message: "summarize",
146+sessionKey: "cron:fallback-from-dead-ollama",
147+lane: "cron",
148+});
149+150+expect(result.status).toBe("ok");
151+expect(result.provider).toBe("openrouter");
152+expect(result.model).toBe("nvidia/nemotron-3-super-120b-a12b:free");
153+expect(preflightCronModelProviderMock.mock.calls.map((call) => call[0])).toMatchObject([
154+{ provider: "ollama", model: "qwen3:32b" },
155+{ provider: "openrouter", model: "nvidia/nemotron-3-super-120b-a12b:free" },
156+]);
157+expect(runEmbeddedAgentMock.mock.calls[0]?.[0]).toMatchObject({
158+provider: "openrouter",
159+model: "nvidia/nemotron-3-super-120b-a12b:free",
160+});
161+expect(runWithModelFallbackMock.mock.calls[0]?.[0]).toMatchObject({
162+fallbacksOverride: ["openai/gpt-5.4"],
163+});
164+expect(String(logWarnMock.mock.calls[0]?.[0] ?? "")).toContain(
165+"continuing with fallback openrouter/nvidia/nemotron-3-super-120b-a12b:free",
166+);
167+expect(String(logWarnMock.mock.calls[0]?.[0] ?? "")).not.toContain("Skipping this cron run");
168+});
169+170+it("keeps explicit empty payload fallbacks strict when local primary preflight fails", async () => {
171+preflightCronModelProviderMock.mockResolvedValueOnce({
172+status: "unavailable",
173+reason:
174+"Agent cron job uses ollama/qwen3:32b but the local provider endpoint is not reachable at http://127.0.0.1:11434.",
175+provider: "ollama",
176+model: "qwen3:32b",
177+baseUrl: "http://127.0.0.1:11434",
178+retryAfterMs: 300000,
179+});
180+181+const result = await runCronIsolatedAgentTurn({
182+cfg: {
183+agents: {
184+defaults: {
185+model: {
186+primary: "ollama/qwen3:32b",
187+fallbacks: ["openrouter/nvidia/nemotron-3-super-120b-a12b:free"],
188+},
189+},
190+},
191+models: {
192+providers: {
193+ollama: {
194+api: "ollama",
195+baseUrl: "http://127.0.0.1:11434",
196+models: [],
197+},
198+},
199+},
200+},
201+deps: {} as never,
202+job: {
203+id: "strict-dead-ollama",
204+name: "Strict Dead Ollama",
205+enabled: true,
206+createdAtMs: 0,
207+updatedAtMs: 0,
208+schedule: { kind: "cron", expr: "*/5 * * * *", tz: "UTC" },
209+sessionTarget: "isolated",
210+state: {},
211+wakeMode: "next-heartbeat",
212+payload: { kind: "agentTurn", message: "summarize", fallbacks: [] },
213+delivery: { mode: "none" },
214+},
215+message: "summarize",
216+sessionKey: "cron:strict-dead-ollama",
217+lane: "cron",
218+});
219+220+expect(result.status).toBe("skipped");
221+expect(preflightCronModelProviderMock).toHaveBeenCalledOnce();
222+expect(runEmbeddedAgentMock).not.toHaveBeenCalled();
223+});
82224});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。