mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-13 23:56:07 +00:00
fix: record pricing refresh health failures
This commit is contained in:
@@ -20,7 +20,7 @@ export type CachedModelPricing = {
|
||||
tieredPricing?: CachedPricingTier[];
|
||||
};
|
||||
|
||||
export type GatewayModelPricingHealthSource = "openrouter" | "litellm" | "bootstrap";
|
||||
export type GatewayModelPricingHealthSource = "openrouter" | "litellm" | "bootstrap" | "refresh";
|
||||
|
||||
export type GatewayModelPricingHealth = {
|
||||
state: "ok" | "degraded" | "disabled";
|
||||
|
||||
@@ -456,6 +456,84 @@ describe("model-pricing-cache", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("records and clears scheduled refresh rejections for health surfaces", async () => {
|
||||
vi.useFakeTimers();
|
||||
try {
|
||||
const manifestRegistry: PluginManifestRegistry = { diagnostics: [], plugins: [] };
|
||||
let failManifestRead = false;
|
||||
const pluginMetadataSnapshot = {
|
||||
index: { plugins: [] } as never,
|
||||
get manifestRegistry() {
|
||||
if (failManifestRead) {
|
||||
throw new Error("manifest metadata failed");
|
||||
}
|
||||
return manifestRegistry;
|
||||
},
|
||||
};
|
||||
const config = {
|
||||
agents: {
|
||||
defaults: {
|
||||
model: { primary: "custom/gpt-remote" },
|
||||
},
|
||||
},
|
||||
models: {
|
||||
providers: {
|
||||
custom: {
|
||||
baseUrl: "https://models.example/v1",
|
||||
api: "openai-completions",
|
||||
models: [{ id: "gpt-remote" }],
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as OpenClawConfig;
|
||||
const fetchImpl = withFetchPreconnect(async (input: RequestInfo | URL) => {
|
||||
const url =
|
||||
typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
||||
return new Response(JSON.stringify(url.includes("openrouter.ai") ? { data: [] } : {}), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
});
|
||||
|
||||
await refreshGatewayModelPricingCache({
|
||||
config,
|
||||
fetchImpl,
|
||||
pluginMetadataSnapshot,
|
||||
});
|
||||
expect(getGatewayModelPricingHealth()).toEqual({
|
||||
state: "ok",
|
||||
sources: [],
|
||||
});
|
||||
|
||||
failManifestRead = true;
|
||||
await vi.runOnlyPendingTimersAsync();
|
||||
|
||||
expect(getGatewayModelPricingHealth()).toMatchObject({
|
||||
state: "degraded",
|
||||
sources: [
|
||||
{
|
||||
source: "refresh",
|
||||
state: "degraded",
|
||||
detail: "pricing refresh failed: Error: manifest metadata failed",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
failManifestRead = false;
|
||||
await refreshGatewayModelPricingCache({
|
||||
config,
|
||||
fetchImpl,
|
||||
pluginMetadataSnapshot,
|
||||
});
|
||||
expect(getGatewayModelPricingHealth()).toEqual({
|
||||
state: "ok",
|
||||
sources: [],
|
||||
});
|
||||
} finally {
|
||||
vi.useRealTimers();
|
||||
}
|
||||
});
|
||||
|
||||
it("seeds pricing from explicit configured model cost without external catalog fetches", async () => {
|
||||
const config = {
|
||||
agents: {
|
||||
|
||||
@@ -1125,7 +1125,11 @@ function scheduleRefresh(
|
||||
return;
|
||||
}
|
||||
void refreshGatewayModelPricingCache(params).catch((error: unknown) => {
|
||||
log.warn(`pricing refresh failed: ${String(error)}`);
|
||||
const message = `pricing refresh failed: ${String(error)}`;
|
||||
log.warn(message);
|
||||
if (!params.signal?.aborted) {
|
||||
recordGatewayModelPricingSourceFailure("refresh", message);
|
||||
}
|
||||
});
|
||||
}, CACHE_TTL_MS);
|
||||
refreshTimer.unref?.();
|
||||
@@ -1340,6 +1344,7 @@ export async function refreshGatewayModelPricingCache(
|
||||
return;
|
||||
}
|
||||
clearGatewayModelPricingSourceFailure("bootstrap");
|
||||
clearGatewayModelPricingSourceFailure("refresh");
|
||||
replaceGatewayModelPricingCache(nextPricing);
|
||||
scheduleRefresh({ ...params, fetchImpl });
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user