@broberg/ai-sdk 0.53.0 → 0.54.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-XDBN6QJ4.js → chunk-PDGFAD66.js} +2 -1
- package/dist/{chunk-ZSLRCOYM.js → chunk-TSNJ5N4F.js} +3 -1
- package/dist/index.d.ts +40 -3
- package/dist/index.js +193 -5
- package/dist/index.js.map +1 -1
- package/dist/pricing.js +1 -1
- package/dist/registry.js +1 -1
- package/package.json +1 -1
- /package/dist/{chunk-XDBN6QJ4.js.map → chunk-PDGFAD66.js.map} +0 -0
- /package/dist/{chunk-ZSLRCOYM.js.map → chunk-TSNJ5N4F.js.map} +0 -0
|
@@ -225,10 +225,11 @@ export {
|
|
|
225
225
|
assertOverrideProvider,
|
|
226
226
|
resolveTier,
|
|
227
227
|
resetRegistry,
|
|
228
|
+
allEntries,
|
|
228
229
|
providerIds,
|
|
229
230
|
setAvailability,
|
|
230
231
|
ModelUnavailableError,
|
|
231
232
|
listModels,
|
|
232
233
|
resolveModel
|
|
233
234
|
};
|
|
234
|
-
//# sourceMappingURL=chunk-
|
|
235
|
+
//# sourceMappingURL=chunk-PDGFAD66.js.map
|
|
@@ -460,8 +460,10 @@ function resetPricingWarningForTests() {
|
|
|
460
460
|
}
|
|
461
461
|
|
|
462
462
|
export {
|
|
463
|
+
PRICING,
|
|
463
464
|
getPrice,
|
|
464
465
|
MEDIA_PRICING_CHECKED_AT,
|
|
466
|
+
MEDIA_PRICING,
|
|
465
467
|
DEFAULT_CLIP_SEC,
|
|
466
468
|
getMediaPrice,
|
|
467
469
|
listMediaPrices,
|
|
@@ -475,4 +477,4 @@ export {
|
|
|
475
477
|
warnIfPricingStale,
|
|
476
478
|
resetPricingWarningForTests
|
|
477
479
|
};
|
|
478
|
-
//# sourceMappingURL=chunk-
|
|
480
|
+
//# sourceMappingURL=chunk-TSNJ5N4F.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -2884,6 +2884,43 @@ declare function byokAdapter<C extends KeyedConfig>(factory: (config: C) => Prov
|
|
|
2884
2884
|
/** True when the adapter was built by byokAdapter with a real key. */
|
|
2885
2885
|
declare function isByokKeyed(adapter: ProviderAdapter): boolean;
|
|
2886
2886
|
|
|
2887
|
+
interface PriceDriftRow {
|
|
2888
|
+
/** `${provider}:${model}` — the key in ai-sdk's price table. */
|
|
2889
|
+
key: string;
|
|
2890
|
+
provider: string;
|
|
2891
|
+
model: string;
|
|
2892
|
+
ours: {
|
|
2893
|
+
inputPer1M: number;
|
|
2894
|
+
outputPer1M: number;
|
|
2895
|
+
};
|
|
2896
|
+
upstream: {
|
|
2897
|
+
inputPer1M?: number;
|
|
2898
|
+
outputPer1M?: number;
|
|
2899
|
+
};
|
|
2900
|
+
/** Relative change, upstream vs ours, in percent (+20 = 20 % dearer). Undefined when
|
|
2901
|
+
* upstream omits that side. */
|
|
2902
|
+
inputChangePct?: number;
|
|
2903
|
+
outputChangePct?: number;
|
|
2904
|
+
}
|
|
2905
|
+
type PriceDriftResult = {
|
|
2906
|
+
ok: true;
|
|
2907
|
+
measuredAt: string;
|
|
2908
|
+
source: "openrouter";
|
|
2909
|
+
checked: number;
|
|
2910
|
+
drift: PriceDriftRow[];
|
|
2911
|
+
coverage: string;
|
|
2912
|
+
} | {
|
|
2913
|
+
ok: false;
|
|
2914
|
+
measuredAt: string;
|
|
2915
|
+
error: string;
|
|
2916
|
+
};
|
|
2917
|
+
/** Compare ai-sdk's price table with OpenRouter's live prices. Never throws: an outage
|
|
2918
|
+
* returns `{ ok: false, error }`, so it cannot look like "no price changes". */
|
|
2919
|
+
declare function checkPriceDrift(opts?: {
|
|
2920
|
+
fetch?: typeof fetch;
|
|
2921
|
+
threshold?: number;
|
|
2922
|
+
}): Promise<PriceDriftResult>;
|
|
2923
|
+
|
|
2887
2924
|
declare const defaultProviders: Record<string, ProviderAdapter>;
|
|
2888
2925
|
|
|
2889
2926
|
/** Anthropic adapter stub (HTTP path). */
|
|
@@ -2902,8 +2939,8 @@ declare const falStubAdapter: ProviderAdapter;
|
|
|
2902
2939
|
* wires the live adapters. */
|
|
2903
2940
|
declare const stubProviders: Record<string, ProviderAdapter>;
|
|
2904
2941
|
|
|
2905
|
-
declare const VERSION: "0.
|
|
2906
|
-
declare const SDK_TAG: "@broberg/ai-sdk@0.
|
|
2942
|
+
declare const VERSION: "0.54.0";
|
|
2943
|
+
declare const SDK_TAG: "@broberg/ai-sdk@0.54.0";
|
|
2907
2944
|
|
|
2908
2945
|
/** Built-in defaults. Every entry is overridable via AiConfig.defaults or a
|
|
2909
2946
|
* per-call override.
|
|
@@ -3414,4 +3451,4 @@ interface StreamTransportRequest extends TransportRequest {
|
|
|
3414
3451
|
*/
|
|
3415
3452
|
declare function streamTransport(req: StreamTransportRequest): AsyncIterable<string>;
|
|
3416
3453
|
|
|
3417
|
-
export { AZURE_DANISH_VOICES, AZURE_DANISH_VOICE_LIST, type AiClient, type AiConfig, type AlignedWordTimings, type AzureVoiceInfo, type AzureWordBoundary, type BatchJob, type BatchRequestItem, type BatchResultItem, type BflAdapterConfig, type BflCredits, type BudgetConfig, BudgetExceededError, BudgetGuard, type BudgetStore, CONFIG_DERIVED_HOSTS, type CallOptions, type Capability, type ChatInput, type ChatRequest, type ChatResult, type ChatStreamEvent, type CheckVoiceOptions, type ClassifyInput, type ClassifyResult, type ContentPart, type Contracts, type CostQuery, type CostSink, type CostSummary, type CostSummaryQuery, type CostTimeseriesQuery, DEFAULT_BASE_URLS, DEFAULT_TIER_MAP, type DesignInput, type DesignResult, type DialogueRequest, type DialogueTurn, type DiscordSinkConfig, ELEVENLABS_DANISH_VOICES, type EmbeddingInput, type EmbeddingRequest, type EmbeddingResult, type ExtractInput, type ExtractResult, type FalAdapterConfig, type FixedHostProvider, type HttpResponse, type ImageInput, type ImageRequest, type ImageResult, type JudgeAnswer, type JudgeChoiceAnswer, type JudgeInput, type JudgeNoulAnswer, type JudgeQuestion, type JudgeResult, type JudgeScoreAnswer, LOCALLY_PINNED_HOSTS, type LoraWeight, type Message, type MockupInput, type MockupResult, type ModerationInput, type ModerationItem, type ModerationRequest, type ModerationResult, type OcrInput, type OcrPage, type OcrRequest, type OcrResult, type OpenAICompatibleConfig, type PodcastInput, type PodcastResult, type PricingEntry, type ProviderAdapter, type RefreshOptions, type RefreshResult, type Region, type RerankInput, type RerankResult, type Role, type RouteForecast, SDK_TAG, type SqliteBudgetStoreConfig, type SqliteSinkConfig, StreamHttpError, type SubprocessResponse, type Tier, type TierSpec, type Tool, type ToolCall, type TrainStyleInput, type TrainStyleRequest, type TrainStyleResult, type TranscribeInput, type TranscribeRequest, type TranscribeResult, type TranslateInput, type TranslateResult, type Transport, type TransportRequest, type TransportResponse, type TtsInput, type TtsRequest, type UpmetricsCostClientConfig, UpmetricsCostError, type UpmetricsCostRow, type UpmetricsCostSummary, type UpmetricsCostTimeseries, type UpmetricsSink, type UpmetricsSinkConfig, type UpmetricsSinkStats, type Usage, VERSION, type VideoInput, type VisionInput, type VoiceInfo, type VoiceProvider, type VoiceResolveResult, type VoiceStatus, VoiceUnavailableError, type WordTiming, aiConfigSchema, alignWordTimings, anthropicAdapter, anthropicApiAdapter, anthropicSubprocessAdapter, azureAdapter, bflAdapter, bflCredits, byokAdapter, chatInputSchema, checkVoice, classifyRegionName, computeCost, createAI, deepinfraAdapter, deeplAdapter, deepseekAdapter, defaultBaseUrl, defaultProviders, discordSink, elevenlabsAdapter, embeddingInputSchema, falAdapter, falStubAdapter, freshUsage, fromProviderToolCall, geminiAdapter, getCostSummary, getPrice, httpTransport, imageInputSchema, isByokKeyed, listAzureDanishVoices, listVoices, makeContracts, makeOpenAICompatibleAdapter, matchLabel, messageSchema, mistralAdapter, mistralStubAdapter, multiSink, noopSink, openaiAdapter, openaiStubAdapter, openrouterAdapter, parseClaudeCliJson, parseJsonLoose, refreshAvailability, regionOfHost, regionOfProvider, requestyAdapter, resetRefreshState, resetRegistry, resolveAzureVoice, resolveTier, resolveVoice, sqliteBudgetStore, sqliteSink, streamTransport, stubProviders, subprocessTransport, tierSpecSchema, toProviderTools, toolSchema, translateInputSchema, typesafeAdapter, upmetricsCostClient, upmetricsSink, usdFromMicro, vertexAdapter, visionInputSchema, wouldProviderRouteTo, wouldRouteTo };
|
|
3454
|
+
export { AZURE_DANISH_VOICES, AZURE_DANISH_VOICE_LIST, type AiClient, type AiConfig, type AlignedWordTimings, type AzureVoiceInfo, type AzureWordBoundary, type BatchJob, type BatchRequestItem, type BatchResultItem, type BflAdapterConfig, type BflCredits, type BudgetConfig, BudgetExceededError, BudgetGuard, type BudgetStore, CONFIG_DERIVED_HOSTS, type CallOptions, type Capability, type ChatInput, type ChatRequest, type ChatResult, type ChatStreamEvent, type CheckVoiceOptions, type ClassifyInput, type ClassifyResult, type ContentPart, type Contracts, type CostQuery, type CostSink, type CostSummary, type CostSummaryQuery, type CostTimeseriesQuery, DEFAULT_BASE_URLS, DEFAULT_TIER_MAP, type DesignInput, type DesignResult, type DialogueRequest, type DialogueTurn, type DiscordSinkConfig, ELEVENLABS_DANISH_VOICES, type EmbeddingInput, type EmbeddingRequest, type EmbeddingResult, type ExtractInput, type ExtractResult, type FalAdapterConfig, type FixedHostProvider, type HttpResponse, type ImageInput, type ImageRequest, type ImageResult, type JudgeAnswer, type JudgeChoiceAnswer, type JudgeInput, type JudgeNoulAnswer, type JudgeQuestion, type JudgeResult, type JudgeScoreAnswer, LOCALLY_PINNED_HOSTS, type LoraWeight, type Message, type MockupInput, type MockupResult, type ModerationInput, type ModerationItem, type ModerationRequest, type ModerationResult, type OcrInput, type OcrPage, type OcrRequest, type OcrResult, type OpenAICompatibleConfig, type PodcastInput, type PodcastResult, type PriceDriftResult, type PriceDriftRow, type PricingEntry, type ProviderAdapter, type RefreshOptions, type RefreshResult, type Region, type RerankInput, type RerankResult, type Role, type RouteForecast, SDK_TAG, type SqliteBudgetStoreConfig, type SqliteSinkConfig, StreamHttpError, type SubprocessResponse, type Tier, type TierSpec, type Tool, type ToolCall, type TrainStyleInput, type TrainStyleRequest, type TrainStyleResult, type TranscribeInput, type TranscribeRequest, type TranscribeResult, type TranslateInput, type TranslateResult, type Transport, type TransportRequest, type TransportResponse, type TtsInput, type TtsRequest, type UpmetricsCostClientConfig, UpmetricsCostError, type UpmetricsCostRow, type UpmetricsCostSummary, type UpmetricsCostTimeseries, type UpmetricsSink, type UpmetricsSinkConfig, type UpmetricsSinkStats, type Usage, VERSION, type VideoInput, type VisionInput, type VoiceInfo, type VoiceProvider, type VoiceResolveResult, type VoiceStatus, VoiceUnavailableError, type WordTiming, aiConfigSchema, alignWordTimings, anthropicAdapter, anthropicApiAdapter, anthropicSubprocessAdapter, azureAdapter, bflAdapter, bflCredits, byokAdapter, chatInputSchema, checkPriceDrift, checkVoice, classifyRegionName, computeCost, createAI, deepinfraAdapter, deeplAdapter, deepseekAdapter, defaultBaseUrl, defaultProviders, discordSink, elevenlabsAdapter, embeddingInputSchema, falAdapter, falStubAdapter, freshUsage, fromProviderToolCall, geminiAdapter, getCostSummary, getPrice, httpTransport, imageInputSchema, isByokKeyed, listAzureDanishVoices, listVoices, makeContracts, makeOpenAICompatibleAdapter, matchLabel, messageSchema, mistralAdapter, mistralStubAdapter, multiSink, noopSink, openaiAdapter, openaiStubAdapter, openrouterAdapter, parseClaudeCliJson, parseJsonLoose, refreshAvailability, regionOfHost, regionOfProvider, requestyAdapter, resetRefreshState, resetRegistry, resolveAzureVoice, resolveTier, resolveVoice, sqliteBudgetStore, sqliteSink, streamTransport, stubProviders, subprocessTransport, tierSpecSchema, toProviderTools, toolSchema, translateInputSchema, typesafeAdapter, upmetricsCostClient, upmetricsSink, usdFromMicro, vertexAdapter, visionInputSchema, wouldProviderRouteTo, wouldRouteTo };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_TIER_MAP,
|
|
3
3
|
ModelUnavailableError,
|
|
4
|
+
allEntries,
|
|
4
5
|
assertOverrideProvider,
|
|
5
6
|
listModels,
|
|
6
7
|
providerIds,
|
|
@@ -8,17 +9,19 @@ import {
|
|
|
8
9
|
resolveModel,
|
|
9
10
|
resolveTier,
|
|
10
11
|
setAvailability
|
|
11
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-PDGFAD66.js";
|
|
12
13
|
import {
|
|
13
14
|
DEFAULT_CLIP_SEC,
|
|
15
|
+
MEDIA_PRICING,
|
|
14
16
|
MEDIA_PRICING_CHECKED_AT,
|
|
17
|
+
PRICING,
|
|
15
18
|
PRICING_STALE_AFTER_DAYS,
|
|
16
19
|
getMediaPrice,
|
|
17
20
|
getPrice,
|
|
18
21
|
listMediaPrices,
|
|
19
22
|
pricingFreshness,
|
|
20
23
|
pricingGeneratedAt
|
|
21
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-TSNJ5N4F.js";
|
|
22
25
|
|
|
23
26
|
// src/cost/default-hosts.ts
|
|
24
27
|
var DEFAULT_BASE_URLS = {
|
|
@@ -363,7 +366,7 @@ function regionOfProvider(provider) {
|
|
|
363
366
|
function computeCost(provider, model, inputTokens, outputTokens, cacheReadTokens = 0, cacheCreationTokens = 0) {
|
|
364
367
|
const price = getPrice(provider, model);
|
|
365
368
|
if (!price) return 0;
|
|
366
|
-
const perToken = (
|
|
369
|
+
const perToken = (per1M2) => per1M2 / 1e6;
|
|
367
370
|
const inRate = perToken(price.inputPer1M);
|
|
368
371
|
const outRate = perToken(price.outputPer1M);
|
|
369
372
|
const cacheReadRate = price.cacheReadPer1M !== void 0 ? perToken(price.cacheReadPer1M) : inRate;
|
|
@@ -3523,8 +3526,8 @@ var aiConfigSchema = z.object({
|
|
|
3523
3526
|
import { randomUUID } from "crypto";
|
|
3524
3527
|
|
|
3525
3528
|
// src/version.ts
|
|
3526
|
-
var VERSION = "0.
|
|
3527
|
-
var SDK_TAG = "@broberg/ai-sdk@0.
|
|
3529
|
+
var VERSION = "0.54.0";
|
|
3530
|
+
var SDK_TAG = "@broberg/ai-sdk@0.54.0";
|
|
3528
3531
|
|
|
3529
3532
|
// src/cost/sinks/upmetrics.ts
|
|
3530
3533
|
function isRetryableStatus(status) {
|
|
@@ -3871,6 +3874,21 @@ var DEFAULT_JUDGE_SPEC = { provider: "typesafe", model: "jev-latest", transport:
|
|
|
3871
3874
|
var DEFAULT_PODCAST_SPEC = { provider: "elevenlabs", model: "eleven_v3", transport: "http" };
|
|
3872
3875
|
var DEFAULT_TTS_SPEC = { provider: "elevenlabs", model: "eleven_multilingual_v2", transport: "http" };
|
|
3873
3876
|
var DEFAULT_BATCH_SPEC = { provider: "mistral", model: "mistral-small-latest", transport: "http" };
|
|
3877
|
+
var ROUTED_SPECS = [
|
|
3878
|
+
DEFAULT_IMAGE_SPEC,
|
|
3879
|
+
DEFAULT_LORA_IMAGE_SPEC,
|
|
3880
|
+
DEFAULT_TRAINSTYLE_SPEC,
|
|
3881
|
+
DEFAULT_ANIMATE_SPEC,
|
|
3882
|
+
DEFAULT_BFL_FINETUNE_SPEC,
|
|
3883
|
+
DEFAULT_BFL_REFERENCE_SPEC,
|
|
3884
|
+
DEFAULT_OCR_SPEC,
|
|
3885
|
+
DEFAULT_MODERATION_SPEC,
|
|
3886
|
+
DEFAULT_JUDGE_SPEC,
|
|
3887
|
+
DEFAULT_PODCAST_SPEC,
|
|
3888
|
+
DEFAULT_TTS_SPEC,
|
|
3889
|
+
DEFAULT_BATCH_SPEC,
|
|
3890
|
+
DEFAULT_TRANSCRIBE_SPEC
|
|
3891
|
+
];
|
|
3874
3892
|
function defaultCostSink() {
|
|
3875
3893
|
const apiKey = process.env.UPMETRICS_API_KEY;
|
|
3876
3894
|
if (!apiKey) return void 0;
|
|
@@ -4432,6 +4450,175 @@ function createAI(config = {}) {
|
|
|
4432
4450
|
return client;
|
|
4433
4451
|
}
|
|
4434
4452
|
|
|
4453
|
+
// src/catalogue/fetchers.ts
|
|
4454
|
+
async function getJson(f, url, headers) {
|
|
4455
|
+
const res = await f(url, { headers: { accept: "application/json", ...headers } });
|
|
4456
|
+
if (!res.ok) {
|
|
4457
|
+
const body = await res.text().catch(() => "");
|
|
4458
|
+
throw new Error(`${res.status} ${body.slice(0, 200)}`);
|
|
4459
|
+
}
|
|
4460
|
+
return res.json();
|
|
4461
|
+
}
|
|
4462
|
+
function per1M(v) {
|
|
4463
|
+
if (v == null) return void 0;
|
|
4464
|
+
const n = Number(v) * 1e6;
|
|
4465
|
+
return Number.isFinite(n) && n >= 0 ? n : void 0;
|
|
4466
|
+
}
|
|
4467
|
+
async function fetchOpenRouterCatalogue(opts = {}) {
|
|
4468
|
+
const f = opts.fetch ?? fetch;
|
|
4469
|
+
const base = opts.baseUrl ?? "https://openrouter.ai/api/v1";
|
|
4470
|
+
const json = await getJson(f, `${base}/models`, {});
|
|
4471
|
+
return (json.data ?? []).map((m) => {
|
|
4472
|
+
const inputPer1M = per1M(m.pricing?.prompt);
|
|
4473
|
+
const outputPer1M = per1M(m.pricing?.completion);
|
|
4474
|
+
return {
|
|
4475
|
+
provider: "openrouter",
|
|
4476
|
+
model: m.id,
|
|
4477
|
+
...inputPer1M !== void 0 ? { inputPer1M } : {},
|
|
4478
|
+
...outputPer1M !== void 0 ? { outputPer1M } : {},
|
|
4479
|
+
...m.context_length ? { contextLength: m.context_length } : {}
|
|
4480
|
+
};
|
|
4481
|
+
});
|
|
4482
|
+
}
|
|
4483
|
+
|
|
4484
|
+
// src/catalogue/types.ts
|
|
4485
|
+
function catalogueKey(m) {
|
|
4486
|
+
return `${m.provider}:${m.model}`;
|
|
4487
|
+
}
|
|
4488
|
+
|
|
4489
|
+
// src/catalogue/diff.ts
|
|
4490
|
+
function brandToken(model) {
|
|
4491
|
+
const tail = model.split("/").pop() ?? model;
|
|
4492
|
+
return tail.split("-")[0] ?? tail;
|
|
4493
|
+
}
|
|
4494
|
+
var DIRECT_PROVIDERS = /* @__PURE__ */ new Set(["openai", "anthropic", "gemini"]);
|
|
4495
|
+
var FULL_LIST_PROVIDERS = /* @__PURE__ */ new Set(["mistral", "deepseek", "elevenlabs"]);
|
|
4496
|
+
function undatedBase(id) {
|
|
4497
|
+
return id.replace(/-(\d{4}-\d{2}-\d{2}|\d{8}|\d{4})$/, "");
|
|
4498
|
+
}
|
|
4499
|
+
function knownKeys() {
|
|
4500
|
+
const known = /* @__PURE__ */ new Set([...Object.keys(PRICING), ...Object.keys(MEDIA_PRICING)]);
|
|
4501
|
+
for (const e of allEntries()) known.add(`${e.provider}:${e.id}`);
|
|
4502
|
+
for (const spec of [...Object.values(DEFAULT_TIER_MAP), ...ROUTED_SPECS]) known.add(`${spec.provider}:${spec.model}`);
|
|
4503
|
+
return known;
|
|
4504
|
+
}
|
|
4505
|
+
function diffCatalogue(fetched, opts = {}) {
|
|
4506
|
+
const threshold = opts.priceThreshold ?? 0.01;
|
|
4507
|
+
const fetchedProviders = new Set(opts.fetchedProviders ?? fetched.map((m) => m.provider));
|
|
4508
|
+
const fetchedByKey = /* @__PURE__ */ new Map();
|
|
4509
|
+
for (const m of fetched) {
|
|
4510
|
+
fetchedByKey.set(catalogueKey(m), m);
|
|
4511
|
+
for (const a of m.aliases ?? []) {
|
|
4512
|
+
const k = catalogueKey({ provider: m.provider, model: a });
|
|
4513
|
+
if (!fetchedByKey.has(k)) fetchedByKey.set(k, m);
|
|
4514
|
+
}
|
|
4515
|
+
}
|
|
4516
|
+
const known = knownKeys();
|
|
4517
|
+
const trackedBrands = /* @__PURE__ */ new Map();
|
|
4518
|
+
for (const key of Object.keys(PRICING)) {
|
|
4519
|
+
const [provider, model] = splitKey(key);
|
|
4520
|
+
if (!trackedBrands.has(provider)) trackedBrands.set(provider, /* @__PURE__ */ new Set());
|
|
4521
|
+
trackedBrands.get(provider).add(brandToken(model));
|
|
4522
|
+
}
|
|
4523
|
+
const added = [];
|
|
4524
|
+
const reportedGroups = /* @__PURE__ */ new Set();
|
|
4525
|
+
const listedByProvider = /* @__PURE__ */ new Map();
|
|
4526
|
+
for (const m of fetched) {
|
|
4527
|
+
if (!listedByProvider.has(m.provider)) listedByProvider.set(m.provider, /* @__PURE__ */ new Set());
|
|
4528
|
+
listedByProvider.get(m.provider).add(m.model);
|
|
4529
|
+
}
|
|
4530
|
+
const isSnapshotOfListed = (m) => {
|
|
4531
|
+
const base = undatedBase(m.model);
|
|
4532
|
+
if (base === m.model) return false;
|
|
4533
|
+
return listedByProvider.get(m.provider)?.has(base) === true || known.has(catalogueKey({ provider: m.provider, model: base }));
|
|
4534
|
+
};
|
|
4535
|
+
for (const m of fetched) {
|
|
4536
|
+
if (FULL_LIST_PROVIDERS.has(m.provider)) {
|
|
4537
|
+
if (m.deprecated) continue;
|
|
4538
|
+
const group = [m.model, ...m.aliases ?? []].map((id) => catalogueKey({ provider: m.provider, model: id }));
|
|
4539
|
+
if (group.some((k) => known.has(k) || reportedGroups.has(k))) continue;
|
|
4540
|
+
for (const k of group) reportedGroups.add(k);
|
|
4541
|
+
added.push(m);
|
|
4542
|
+
continue;
|
|
4543
|
+
}
|
|
4544
|
+
if (!DIRECT_PROVIDERS.has(m.provider)) continue;
|
|
4545
|
+
if (isSnapshotOfListed(m)) continue;
|
|
4546
|
+
const brands = trackedBrands.get(m.provider);
|
|
4547
|
+
if (!brands?.has(brandToken(m.model))) continue;
|
|
4548
|
+
if (PRICING[catalogueKey(m)]) continue;
|
|
4549
|
+
added.push(m);
|
|
4550
|
+
}
|
|
4551
|
+
const missingPrice = [];
|
|
4552
|
+
for (const spec of Object.values(DEFAULT_TIER_MAP)) {
|
|
4553
|
+
if (!getPrice(spec.provider, spec.model)) missingPrice.push(`${spec.provider}:${spec.model}`);
|
|
4554
|
+
}
|
|
4555
|
+
const priceChanged = [];
|
|
4556
|
+
const removedUpstream = [];
|
|
4557
|
+
for (const [key, entry] of Object.entries(PRICING)) {
|
|
4558
|
+
const [provider] = splitKey(key);
|
|
4559
|
+
if (!fetchedProviders.has(provider)) continue;
|
|
4560
|
+
const up = fetchedByKey.get(key);
|
|
4561
|
+
if (!up) {
|
|
4562
|
+
removedUpstream.push(key);
|
|
4563
|
+
continue;
|
|
4564
|
+
}
|
|
4565
|
+
if (up.inputPer1M === void 0 && up.outputPer1M === void 0) continue;
|
|
4566
|
+
const inDrift = relDrift(entry.inputPer1M, up.inputPer1M);
|
|
4567
|
+
const outDrift = relDrift(entry.outputPer1M, up.outputPer1M);
|
|
4568
|
+
if (inDrift > threshold || outDrift > threshold) {
|
|
4569
|
+
priceChanged.push({
|
|
4570
|
+
key,
|
|
4571
|
+
ourInputPer1M: entry.inputPer1M,
|
|
4572
|
+
ourOutputPer1M: entry.outputPer1M,
|
|
4573
|
+
...up.inputPer1M !== void 0 ? { upstreamInputPer1M: up.inputPer1M } : {},
|
|
4574
|
+
...up.outputPer1M !== void 0 ? { upstreamOutputPer1M: up.outputPer1M } : {}
|
|
4575
|
+
});
|
|
4576
|
+
}
|
|
4577
|
+
}
|
|
4578
|
+
return { added, missingPrice, priceChanged, removedUpstream };
|
|
4579
|
+
}
|
|
4580
|
+
function relDrift(ours, upstream) {
|
|
4581
|
+
if (upstream === void 0) return 0;
|
|
4582
|
+
if (upstream === 0) return ours === 0 ? 0 : Infinity;
|
|
4583
|
+
return Math.abs(ours - upstream) / upstream;
|
|
4584
|
+
}
|
|
4585
|
+
function splitKey(key) {
|
|
4586
|
+
const i = key.indexOf(":");
|
|
4587
|
+
return [key.slice(0, i), key.slice(i + 1)];
|
|
4588
|
+
}
|
|
4589
|
+
|
|
4590
|
+
// src/catalogue/price-drift.ts
|
|
4591
|
+
var COVERAGE = "OpenRouter catalogue only. Not covered: direct-provider prices (no price API) and speech/image/video/OCR (hand-checked, F050).";
|
|
4592
|
+
function pct(ours, up) {
|
|
4593
|
+
if (up === void 0) return void 0;
|
|
4594
|
+
if (ours === 0) return up === 0 ? 0 : Infinity;
|
|
4595
|
+
return Math.round((up - ours) / ours * 1e3) / 10;
|
|
4596
|
+
}
|
|
4597
|
+
async function checkPriceDrift(opts = {}) {
|
|
4598
|
+
const measuredAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4599
|
+
try {
|
|
4600
|
+
const models = await fetchOpenRouterCatalogue({ fetch: opts.fetch });
|
|
4601
|
+
if (models.length === 0) return { ok: false, measuredAt, error: "OpenRouter returned an empty catalogue" };
|
|
4602
|
+
const diff = diffCatalogue(models, { fetchedProviders: ["openrouter"], priceThreshold: opts.threshold ?? 0.01 });
|
|
4603
|
+
const drift = diff.priceChanged.map((p) => {
|
|
4604
|
+
const i = p.key.indexOf(":");
|
|
4605
|
+
return {
|
|
4606
|
+
key: p.key,
|
|
4607
|
+
provider: p.key.slice(0, i),
|
|
4608
|
+
model: p.key.slice(i + 1),
|
|
4609
|
+
ours: { inputPer1M: p.ourInputPer1M, outputPer1M: p.ourOutputPer1M },
|
|
4610
|
+
upstream: { inputPer1M: p.upstreamInputPer1M, outputPer1M: p.upstreamOutputPer1M },
|
|
4611
|
+
inputChangePct: pct(p.ourInputPer1M, p.upstreamInputPer1M),
|
|
4612
|
+
outputChangePct: pct(p.ourOutputPer1M, p.upstreamOutputPer1M)
|
|
4613
|
+
};
|
|
4614
|
+
});
|
|
4615
|
+
const checked = models.filter((m) => m.inputPer1M !== void 0 || m.outputPer1M !== void 0).length;
|
|
4616
|
+
return { ok: true, measuredAt, source: "openrouter", checked, drift, coverage: COVERAGE };
|
|
4617
|
+
} catch (e) {
|
|
4618
|
+
return { ok: false, measuredAt, error: e instanceof Error ? e.message : String(e) };
|
|
4619
|
+
}
|
|
4620
|
+
}
|
|
4621
|
+
|
|
4435
4622
|
// src/providers/stub.ts
|
|
4436
4623
|
function stubUsage(provider, model, transport, capability) {
|
|
4437
4624
|
return {
|
|
@@ -4874,6 +5061,7 @@ export {
|
|
|
4874
5061
|
bflCredits,
|
|
4875
5062
|
byokAdapter,
|
|
4876
5063
|
chatInputSchema,
|
|
5064
|
+
checkPriceDrift,
|
|
4877
5065
|
checkVoice,
|
|
4878
5066
|
classifyRegionName,
|
|
4879
5067
|
computeCost,
|