





















@@ -275,6 +275,12 @@ function toCachedModelPricing(
275275};
276276}
277277278+async function cancelUnreadResponseBody(response: Response | undefined): Promise<void> {
279+if (response?.bodyUsed !== true) {
280+await response?.body?.cancel().catch(() => undefined);
281+}
282+}
283+278284async function readPricingJsonObject(
279285response: Response,
280286source: string,
@@ -299,6 +305,28 @@ async function readPricingJsonObject(
299305return payload as Record<string, unknown>;
300306}
301307308+async function fetchPricingJsonObject(params: {
309+fetchImpl: typeof fetch;
310+url: string;
311+source: string;
312+failureLabel: string;
313+signal?: AbortSignal;
314+}): Promise<Record<string, unknown>> {
315+let response: Response | undefined;
316+try {
317+response = await params.fetchImpl(params.url, {
318+headers: { Accept: "application/json" },
319+signal: createPricingFetchSignal(params.signal),
320+});
321+if (!response.ok) {
322+throw new Error(`${params.failureLabel}: HTTP ${response.status}`);
323+}
324+return await readPricingJsonObject(response, params.source);
325+} finally {
326+await cancelUnreadResponseBody(response);
327+}
328+}
329+302330// ---------------------------------------------------------------------------
303331// LiteLLM tiered-pricing parsing
304332// ---------------------------------------------------------------------------
@@ -383,14 +411,13 @@ async function fetchLiteLLMPricingCatalog(
383411fetchImpl: typeof fetch,
384412signal?: AbortSignal,
385413): Promise<LiteLLMPricingCatalog> {
386-const response = await fetchImpl(LITELLM_PRICING_URL, {
387-headers: { Accept: "application/json" },
388-signal: createPricingFetchSignal(signal),
414+const payload = await fetchPricingJsonObject({
415+ fetchImpl,
416+url: LITELLM_PRICING_URL,
417+source: "LiteLLM",
418+failureLabel: "LiteLLM pricing fetch failed",
419+ signal,
389420});
390-if (!response.ok) {
391-throw new Error(`LiteLLM pricing fetch failed: HTTP ${response.status}`);
392-}
393-const payload = await readPricingJsonObject(response, "LiteLLM");
394421const catalog: LiteLLMPricingCatalog = new Map();
395422for (const [key, value] of Object.entries(payload)) {
396423if (!value || typeof value !== "object") {
@@ -1059,14 +1086,13 @@ async function fetchOpenRouterPricingCatalog(
10591086fetchImpl: typeof fetch,
10601087signal?: AbortSignal,
10611088): Promise<Map<string, OpenRouterPricingEntry>> {
1062-const response = await fetchImpl(OPENROUTER_MODELS_URL, {
1063-headers: { Accept: "application/json" },
1064-signal: createPricingFetchSignal(signal),
1089+const payload = await fetchPricingJsonObject({
1090+ fetchImpl,
1091+url: OPENROUTER_MODELS_URL,
1092+source: "OpenRouter",
1093+failureLabel: "OpenRouter /models failed",
1094+ signal,
10651095});
1066-if (!response.ok) {
1067-throw new Error(`OpenRouter /models failed: HTTP ${response.status}`);
1068-}
1069-const payload = await readPricingJsonObject(response, "OpenRouter");
10701096const entries = Array.isArray(payload.data) ? payload.data : [];
10711097const catalog = new Map<string, OpenRouterPricingEntry>();
10721098for (const entry of entries) {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。