






















@@ -456,6 +456,84 @@ describe("model-pricing-cache", () => {
456456});
457457});
458458459+it("records and clears scheduled refresh rejections for health surfaces", async () => {
460+vi.useFakeTimers();
461+try {
462+const manifestRegistry: PluginManifestRegistry = { diagnostics: [], plugins: [] };
463+let failManifestRead = false;
464+const pluginMetadataSnapshot = {
465+index: { plugins: [] } as never,
466+get manifestRegistry() {
467+if (failManifestRead) {
468+throw new Error("manifest metadata failed");
469+}
470+return manifestRegistry;
471+},
472+};
473+const config = {
474+agents: {
475+defaults: {
476+model: { primary: "custom/gpt-remote" },
477+},
478+},
479+models: {
480+providers: {
481+custom: {
482+baseUrl: "https://models.example/v1",
483+api: "openai-completions",
484+models: [{ id: "gpt-remote" }],
485+},
486+},
487+},
488+} as unknown as OpenClawConfig;
489+const fetchImpl = withFetchPreconnect(async (input: RequestInfo | URL) => {
490+const url =
491+typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
492+return new Response(JSON.stringify(url.includes("openrouter.ai") ? { data: [] } : {}), {
493+status: 200,
494+headers: { "Content-Type": "application/json" },
495+});
496+});
497+498+await refreshGatewayModelPricingCache({
499+ config,
500+ fetchImpl,
501+ pluginMetadataSnapshot,
502+});
503+expect(getGatewayModelPricingHealth()).toEqual({
504+state: "ok",
505+sources: [],
506+});
507+508+failManifestRead = true;
509+await vi.runOnlyPendingTimersAsync();
510+511+expect(getGatewayModelPricingHealth()).toMatchObject({
512+state: "degraded",
513+sources: [
514+{
515+source: "refresh",
516+state: "degraded",
517+detail: "pricing refresh failed: Error: manifest metadata failed",
518+},
519+],
520+});
521+522+failManifestRead = false;
523+await refreshGatewayModelPricingCache({
524+ config,
525+ fetchImpl,
526+ pluginMetadataSnapshot,
527+});
528+expect(getGatewayModelPricingHealth()).toEqual({
529+state: "ok",
530+sources: [],
531+});
532+} finally {
533+vi.useRealTimers();
534+}
535+});
536+459537it("seeds pricing from explicit configured model cost without external catalog fetches", async () => {
460538const config = {
461539agents: {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。