@jeffusion/bungee-core 5.9.0 → 5.11.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/main.js +31348 -17337
- package/dist/plugins/ai-transformer/index.js.map +2 -2
- package/dist/plugins/anthropic-request-sanitizer/index.js.map +2 -2
- package/dist/plugins/anthropic-tool-name-transformer/index.js.map +2 -2
- package/dist/plugins/chatgpt-oauth/control.js +513 -1
- package/dist/plugins/chatgpt-oauth/control.js.map +12 -4
- package/dist/plugins/chatgpt-oauth/index.js +511 -4
- package/dist/plugins/chatgpt-oauth/index.js.map +13 -5
- package/dist/plugins/chatgpt-oauth/manifest.json +19 -0
- package/dist/plugins/deepseek-reasoning-fix/index.js.map +2 -2
- package/dist/plugins/key-access/control.js.map +1 -1
- package/dist/plugins/key-access/index.js.map +3 -3
- package/dist/plugins/key-access/ingress.js.map +1 -1
- package/dist/plugins/key-rate-limit/index.js.map +2 -2
- package/dist/plugins/local-accounts/control.js +68 -18
- package/dist/plugins/local-accounts/control.js.map +4 -4
- package/dist/plugins/local-accounts/index.js.map +2 -2
- package/dist/plugins/local-accounts/manifest.json +48 -2
- package/dist/plugins/local-accounts/ui/Settings.svelte +73 -1
- package/dist/plugins/model-mapping/control.js +72 -17741
- package/dist/plugins/model-mapping/control.js.map +6 -58
- package/dist/plugins/model-mapping/index.js +32 -17433
- package/dist/plugins/model-mapping/index.js.map +6 -58
- package/dist/plugins/model-mapping/manifest.json +30 -14
- package/dist/plugins/models-dev/control.js +1042 -0
- package/dist/plugins/models-dev/control.js.map +16 -0
- package/dist/plugins/models-dev/index.js +15418 -0
- package/dist/plugins/models-dev/index.js.map +134 -0
- package/dist/plugins/models-dev/manifest.json +199 -0
- package/dist/plugins/models-dev/ui/ModelsDevSettings.svelte +120 -0
- package/dist/plugins/openai-messages-to-chat/index.js.map +2 -2
- package/dist/plugins/signature-repair/index.js.map +2 -2
- package/dist/plugins/token-budget/control.js +524 -9
- package/dist/plugins/token-budget/control.js.map +12 -4
- package/dist/plugins/token-budget/index.js +519 -12
- package/dist/plugins/token-budget/index.js.map +13 -6
- package/dist/plugins/token-budget/manifest.json +16 -2
- package/dist/plugins/token-metering/index.js +93 -2
- package/dist/plugins/token-metering/index.js.map +12 -6
- package/dist/plugins/token-stats/control.js +174 -312
- package/dist/plugins/token-stats/control.js.map +7 -7
- package/dist/plugins/token-stats/index.js +283 -486
- package/dist/plugins/token-stats/index.js.map +16 -12
- package/dist/plugins/token-stats/manifest.json +99 -12
- package/dist/plugins/token-stats/ui/ClientModelPicker.svelte +76 -0
- package/dist/plugins/token-stats/ui/PriceModelPicker.svelte +87 -0
- package/dist/plugins/token-stats/ui/TokenStatsSettings.svelte +136 -80
- package/dist/plugins/token-stats/ui/client-model-search.test.ts +58 -0
- package/dist/plugins/token-stats/ui/client-model-search.ts +16 -0
- package/dist/plugins/token-stats/ui/model-search.ts +45 -0
- package/dist/plugins/token-stats/ui/pricing-catalog.test.ts +114 -0
- package/dist/plugins/token-stats/ui/pricing-catalog.ts +17 -0
- package/package.json +2 -2
|
@@ -14823,7 +14823,98 @@ function definePlugin(plugin) {
|
|
|
14823
14823
|
return plugin;
|
|
14824
14824
|
}
|
|
14825
14825
|
|
|
14826
|
+
// packages/core/src/plugin-services/host-rpc.ts
|
|
14827
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
14828
|
+
|
|
14829
|
+
// packages/core/src/plugin-services/wire-contract.ts
|
|
14830
|
+
var RPC_JSON_MAX_BYTES = 64 * 1024;
|
|
14831
|
+
var RPC_CONTRACT_MAX_BYTES = 256 * 1024;
|
|
14832
|
+
var RPC_CALL_PURPOSES = new Set(["bootstrap", "background", "management", "request", "attempt"]);
|
|
14833
|
+
var RPC_COMMAND_DEDUPLICATION = new Set(["local-transaction", "external-contract", "none"]);
|
|
14834
|
+
var RPC_COMMAND_POLICY_KEYS = new Set(["deduplication", "resultRetentionMs", "quotaBytes", "maxResultBytes"]);
|
|
14835
|
+
|
|
14836
|
+
// packages/core/src/plugin-services/rpc-runtime.ts
|
|
14837
|
+
var PROCESSES = new Set(["control", "worker", "ingress"]);
|
|
14838
|
+
var SCOPES = new Set(["global", "binding"]);
|
|
14839
|
+
var PURPOSES = new Set(["bootstrap", "background", "management", "request", "attempt"]);
|
|
14840
|
+
var ENDPOINTS = new WeakMap;
|
|
14841
|
+
var REVOKED = new WeakMap;
|
|
14842
|
+
|
|
14843
|
+
// packages/core/src/plugin-services/host-rpc.ts
|
|
14844
|
+
var SERVICE_ERROR_MESSAGES = {
|
|
14845
|
+
unauthorized: "RPC call is not authorized",
|
|
14846
|
+
not_ready: "RPC endpoint is not ready",
|
|
14847
|
+
retired: "RPC endpoint is retiring",
|
|
14848
|
+
revoked: "RPC endpoint generation was revoked",
|
|
14849
|
+
unsupported_method: "RPC method is not published",
|
|
14850
|
+
wrong_purpose: "RPC method is not published for this purpose",
|
|
14851
|
+
invalid_input: "RPC input is not valid",
|
|
14852
|
+
invalid_output: "RPC output is not valid",
|
|
14853
|
+
invalid_operation_id: "RPC command operation id is invalid",
|
|
14854
|
+
overloaded: "RPC runtime is at capacity",
|
|
14855
|
+
cancelled: "RPC call was cancelled",
|
|
14856
|
+
timeout: "RPC call deadline exceeded",
|
|
14857
|
+
unknown: "RPC command outcome is unknown",
|
|
14858
|
+
capability_unavailable: "RPC command execution is unavailable",
|
|
14859
|
+
closed: "RPC runtime is closed",
|
|
14860
|
+
failed: "RPC call failed",
|
|
14861
|
+
conflict: "RPC command identity conflicts with an existing record",
|
|
14862
|
+
expired: "RPC command result has expired",
|
|
14863
|
+
pending: "RPC command is still pending",
|
|
14864
|
+
rejected: "RPC command was rejected",
|
|
14865
|
+
missing: "RPC command has no stored result",
|
|
14866
|
+
storage_failure: "RPC command storage failed",
|
|
14867
|
+
deadlock: "RPC call chain would deadlock",
|
|
14868
|
+
call_depth_exceeded: "RPC call depth exceeded",
|
|
14869
|
+
undeclared: "RPC service is not declared by the manifest",
|
|
14870
|
+
unavailable: "RPC service is unavailable",
|
|
14871
|
+
ambiguous_placement: "RPC service placement is ambiguous",
|
|
14872
|
+
invalid_registration: "RPC registration is invalid"
|
|
14873
|
+
};
|
|
14874
|
+
var SERVICE_ERROR_CODES = new Set(Object.keys(SERVICE_ERROR_MESSAGES));
|
|
14875
|
+
var EMPTY_OPTIONS = Object.freeze({});
|
|
14876
|
+
var FRAME_STORE = new AsyncLocalStorage;
|
|
14877
|
+
var CALLEE_INFO = new WeakMap;
|
|
14878
|
+
// packages/core/src/plugin-services/peer-channel-protocol.ts
|
|
14879
|
+
var CHANNEL_REQUEST_MAX_BYTES = 4 * 1024;
|
|
14880
|
+
var CHANNEL_RESULT_MAX_BYTES = 60 * 1024;
|
|
14881
|
+
var CHANNEL_MAX_CHUNK_BYTES = 60 * 1024;
|
|
14882
|
+
var PLUGIN_CHANNEL_LANES = Object.freeze(["event", "snapshot", "stream"]);
|
|
14883
|
+
var ERROR_CODES = new Set([
|
|
14884
|
+
"unavailable",
|
|
14885
|
+
"ambiguous",
|
|
14886
|
+
"unauthorized",
|
|
14887
|
+
"invalid",
|
|
14888
|
+
"overloaded",
|
|
14889
|
+
"expired",
|
|
14890
|
+
"conflict",
|
|
14891
|
+
"gap",
|
|
14892
|
+
"truncated",
|
|
14893
|
+
"rejected",
|
|
14894
|
+
"cancelled",
|
|
14895
|
+
"closed",
|
|
14896
|
+
"unknown",
|
|
14897
|
+
"failed"
|
|
14898
|
+
]);
|
|
14899
|
+
var RPC_CALL_PURPOSES2 = new Set(["bootstrap", "background", "management", "request", "attempt"]);
|
|
14900
|
+
|
|
14901
|
+
// packages/core/src/plugin-services/peer-channel-hub.ts
|
|
14902
|
+
var CHANNEL_MAX_TRANSFER_BUFFER_BYTES = 4 * CHANNEL_MAX_CHUNK_BYTES;
|
|
14903
|
+
var CHANNEL_MAX_TOTAL_BUFFER_BYTES = 16 * CHANNEL_MAX_CHUNK_BYTES;
|
|
14904
|
+
var CHANNEL_STREAM_MAX_TOTAL_BYTES = 64 * 1024 * 1024;
|
|
14905
|
+
var CHANNEL_STREAM_MAX_CREDIT_BYTES = 8 * CHANNEL_MAX_CHUNK_BYTES;
|
|
14906
|
+
var CHANNEL_STREAM_DEFAULT_CREDIT_BYTES = 4 * CHANNEL_MAX_CHUNK_BYTES;
|
|
14907
|
+
var CHANNEL_SNAPSHOT_MAX_BYTES = 64 * 1024 * 1024;
|
|
14908
|
+
var CHANNEL_EVENT_MAX_PAYLOAD_BYTES = 60 * 1024;
|
|
14909
|
+
var EMPTY = new Uint8Array(0);
|
|
14910
|
+
var HANDLE_REGISTRATIONS = new WeakMap;
|
|
14911
|
+
|
|
14912
|
+
// packages/core/src/plugin-services/snapshot-store.ts
|
|
14913
|
+
var DEFAULT_CHUNK_BYTES = 60 * 1024;
|
|
14914
|
+
var SNAPSHOT_FAMILY_REFS = new Map;
|
|
14915
|
+
|
|
14826
14916
|
// packages/core/src/plugin-services.ts
|
|
14917
|
+
var CHANNEL_KINDS = new Set(["events", "snapshot", "stream"]);
|
|
14827
14918
|
var TOKEN_METERING_SERVICE_ID = "token-metering.v1";
|
|
14828
14919
|
var TOKEN_METERING_CONTRACT_VERSION = 1;
|
|
14829
14920
|
var TOKEN_PRICING_SERVICE_ID = "token-stats.pricing.v1";
|
|
@@ -15089,6 +15180,38 @@ function withReportingWriteTimeout(db, operation) {
|
|
|
15089
15180
|
}
|
|
15090
15181
|
}
|
|
15091
15182
|
|
|
15183
|
+
// plugins/token-stats/server/model-mappings.ts
|
|
15184
|
+
var PRICE_MODEL_MAPPINGS_KEY = "pricing:model-mappings:v1";
|
|
15185
|
+
var MAX_PRICE_MODEL_MAPPINGS = 100;
|
|
15186
|
+
async function readPriceModelMappings(storage) {
|
|
15187
|
+
if (!storage.observation)
|
|
15188
|
+
return parsePriceModelMappings(await storage.get(PRICE_MODEL_MAPPINGS_KEY) ?? []);
|
|
15189
|
+
return storage.observation.withDatabase((db) => {
|
|
15190
|
+
const row = db.query("SELECT value, ttl FROM plugin_storage WHERE plugin_name = ? AND key = ?").get("token-stats", PRICE_MODEL_MAPPINGS_KEY);
|
|
15191
|
+
if (!row || row.ttl !== null && row.ttl < Math.floor(Date.now() / 1000))
|
|
15192
|
+
return [];
|
|
15193
|
+
return parsePriceModelMappings(JSON.parse(row.value));
|
|
15194
|
+
});
|
|
15195
|
+
}
|
|
15196
|
+
function parsePriceModelMappings(value) {
|
|
15197
|
+
if (!Array.isArray(value) || value.length > MAX_PRICE_MODEL_MAPPINGS)
|
|
15198
|
+
throw new Error("invalid_input");
|
|
15199
|
+
const sources = new Set;
|
|
15200
|
+
return value.map((item) => {
|
|
15201
|
+
if (!item || typeof item !== "object" || Array.isArray(item) || Object.keys(item).some((key) => !["source", "provider", "model"].includes(key)))
|
|
15202
|
+
throw new Error("invalid_input");
|
|
15203
|
+
const { source, provider, model } = item;
|
|
15204
|
+
for (const [text, limit] of [[source, 256], [provider, 128], [model, 256]]) {
|
|
15205
|
+
if (typeof text !== "string" || !text.trim() || text !== text.trim() || text.length > limit || /[\u0000-\u001f\u007f]/.test(text))
|
|
15206
|
+
throw new Error("invalid_input");
|
|
15207
|
+
}
|
|
15208
|
+
if (sources.has(source))
|
|
15209
|
+
throw new Error("invalid_input");
|
|
15210
|
+
sources.add(source);
|
|
15211
|
+
return { source, provider, model };
|
|
15212
|
+
});
|
|
15213
|
+
}
|
|
15214
|
+
|
|
15092
15215
|
// plugins/token-stats/server/repository.ts
|
|
15093
15216
|
var TOKEN_ACCOUNTING_AUTHORITIES = ["official", "local", "heuristic", "partial", "none"];
|
|
15094
15217
|
|
|
@@ -15098,6 +15221,9 @@ class TokenStatsRepositoryError extends Error {
|
|
|
15098
15221
|
this.name = "TokenStatsRepositoryError";
|
|
15099
15222
|
}
|
|
15100
15223
|
}
|
|
15224
|
+
var MAX_CLIENT_MODEL_PAGE_SIZE = 100;
|
|
15225
|
+
var MAX_CLIENT_MODEL_PAGE = 400;
|
|
15226
|
+
var CLIENT_MODEL_KEYWORD_MAX = 256;
|
|
15101
15227
|
var REPORTING_INCOMPLETE_KEY = "reporting:incomplete";
|
|
15102
15228
|
var MAX_PENDING_ATTEMPTS = 256;
|
|
15103
15229
|
var LOG_INTERVAL_MS = 60000;
|
|
@@ -15237,504 +15363,130 @@ class TokenStatsRepository {
|
|
|
15237
15363
|
})).sort(groupBy === "model" ? (a, b) => b.inputTokens + b.outputTokens - a.inputTokens - a.outputTokens || b.upstreamAttempts - a.upstreamAttempts || a.dimension.localeCompare(b.dimension) : (a, b) => a.bucketStartMs - b.bucketStartMs || a.dimension.localeCompare(b.dimension))
|
|
15238
15364
|
};
|
|
15239
15365
|
}
|
|
15240
|
-
}
|
|
15241
|
-
|
|
15242
|
-
|
|
15243
|
-
|
|
15244
|
-
|
|
15245
|
-
|
|
15246
|
-
|
|
15247
|
-
|
|
15248
|
-
|
|
15249
|
-
|
|
15250
|
-
|
|
15251
|
-
|
|
15252
|
-
|
|
15253
|
-
|
|
15254
|
-
|
|
15255
|
-
return
|
|
15256
|
-
|
|
15257
|
-
|
|
15258
|
-
|
|
15259
|
-
|
|
15260
|
-
|
|
15261
|
-
|
|
15262
|
-
|
|
15366
|
+
async listClientModels(input = {}) {
|
|
15367
|
+
const pageSize = Math.max(1, Math.min(MAX_CLIENT_MODEL_PAGE_SIZE, Math.trunc(input.pageSize ?? 50)));
|
|
15368
|
+
const page = Math.max(1, Math.trunc(input.page ?? 1));
|
|
15369
|
+
if (!Number.isSafeInteger(page) || page > MAX_CLIENT_MODEL_PAGE)
|
|
15370
|
+
throw new TokenStatsRepositoryError("invalid token-stats page");
|
|
15371
|
+
const keyword = input.keyword;
|
|
15372
|
+
if (keyword !== undefined && (typeof keyword !== "string" || keyword.length > CLIENT_MODEL_KEYWORD_MAX)) {
|
|
15373
|
+
throw new TokenStatsRepositoryError("invalid token-stats keyword");
|
|
15374
|
+
}
|
|
15375
|
+
const observation = this.storage.observation;
|
|
15376
|
+
if (observation === undefined)
|
|
15377
|
+
throw new TokenStatsRepositoryError("token-stats observation storage is required");
|
|
15378
|
+
const since = Date.now() - TOKEN_STATS_RETENTION_MS;
|
|
15379
|
+
const hasKeyword = keyword !== undefined && keyword.length > 0;
|
|
15380
|
+
const escaped = hasKeyword ? keyword.replace(/[\\%_]/g, (character) => `\\${character}`) : null;
|
|
15381
|
+
return observation.withDatabase((db) => {
|
|
15382
|
+
const countSql = hasKeyword ? "SELECT COUNT(DISTINCT model) AS total FROM token_stats_attempts WHERE finished_at_ms >= ? AND model LIKE ? ESCAPE '\\'" : "SELECT COUNT(DISTINCT model) AS total FROM token_stats_attempts WHERE finished_at_ms >= ?";
|
|
15383
|
+
const totalRow = db.query(countSql).get(...hasKeyword ? [since, `%${escaped}%`] : [since]);
|
|
15384
|
+
const total = Number(totalRow?.total ?? 0);
|
|
15385
|
+
const pageSql = hasKeyword ? "SELECT DISTINCT model FROM token_stats_attempts WHERE finished_at_ms >= ? AND model LIKE ? ESCAPE '\\' ORDER BY model ASC LIMIT ? OFFSET ?" : "SELECT DISTINCT model FROM token_stats_attempts WHERE finished_at_ms >= ? ORDER BY model ASC LIMIT ? OFFSET ?";
|
|
15386
|
+
const rows = db.query(pageSql).all(...hasKeyword ? [since, `%${escaped}%`, pageSize, (page - 1) * pageSize] : [since, pageSize, (page - 1) * pageSize]);
|
|
15387
|
+
return {
|
|
15388
|
+
models: rows.map((row) => String(row.model)),
|
|
15389
|
+
total: Number.isSafeInteger(total) && total >= 0 ? total : 0,
|
|
15390
|
+
page,
|
|
15391
|
+
pageSize
|
|
15392
|
+
};
|
|
15263
15393
|
});
|
|
15264
15394
|
}
|
|
15265
|
-
|
|
15266
|
-
|
|
15267
|
-
res = await fetchImpl(url, opts.signal ? { signal: opts.signal } : undefined);
|
|
15268
|
-
} catch (err) {
|
|
15269
|
-
const message = err instanceof Error ? err.message : "Unknown network error";
|
|
15270
|
-
throw new FetchModelsError({ code: "NETWORK", message });
|
|
15395
|
+
async mappings() {
|
|
15396
|
+
return readPriceModelMappings(this.storage);
|
|
15271
15397
|
}
|
|
15272
|
-
|
|
15273
|
-
|
|
15274
|
-
try {
|
|
15275
|
-
body = await res.text();
|
|
15276
|
-
} catch {}
|
|
15277
|
-
const snippet = body ? ` Body: ${body.slice(0, 256)}${body.length > 256 ? "\u2026" : ""}` : "";
|
|
15278
|
-
throw new FetchModelsError({
|
|
15279
|
-
code: "HTTP",
|
|
15280
|
-
status: res.status,
|
|
15281
|
-
message: `Failed to fetch models.dev API (${res.status} ${res.statusText}).${snippet}`
|
|
15282
|
-
});
|
|
15398
|
+
async configureMappings(mappings) {
|
|
15399
|
+
await this.storage.set(PRICE_MODEL_MAPPINGS_KEY, mappings);
|
|
15283
15400
|
}
|
|
15284
|
-
let data;
|
|
15285
|
-
try {
|
|
15286
|
-
data = await res.json();
|
|
15287
|
-
} catch (err) {
|
|
15288
|
-
const message = err instanceof Error ? err.message : "Invalid JSON";
|
|
15289
|
-
throw new FetchModelsError({ code: "PARSE", message });
|
|
15290
|
-
}
|
|
15291
|
-
const catalog = data;
|
|
15292
|
-
const { provider, model } = opts;
|
|
15293
|
-
if (!provider && !model)
|
|
15294
|
-
return catalog;
|
|
15295
|
-
if (provider && !model)
|
|
15296
|
-
return catalog[provider];
|
|
15297
|
-
if (provider && model)
|
|
15298
|
-
return catalog[provider]?.models?.[model];
|
|
15299
|
-
const matches = [];
|
|
15300
|
-
if (!model)
|
|
15301
|
-
return matches;
|
|
15302
|
-
for (const [provKey, prov] of Object.entries(catalog)) {
|
|
15303
|
-
const m = prov?.models?.[model];
|
|
15304
|
-
if (m)
|
|
15305
|
-
matches.push({ provider: provKey, model: m });
|
|
15306
|
-
}
|
|
15307
|
-
return matches;
|
|
15308
|
-
}
|
|
15309
|
-
|
|
15310
|
-
// node_modules/@tokenlens/helpers/dist/context.js
|
|
15311
|
-
var TOKENS_PER_MILLION = 1e6;
|
|
15312
|
-
function normalizeUsage(usage) {
|
|
15313
|
-
if (!usage)
|
|
15314
|
-
return { input: 0, output: 0, total: 0 };
|
|
15315
|
-
if (typeof usage.input === "number" && typeof usage.output === "number") {
|
|
15316
|
-
const u = usage;
|
|
15317
|
-
return {
|
|
15318
|
-
input: u.input,
|
|
15319
|
-
output: u.output,
|
|
15320
|
-
total: u.total ?? u.input + u.output
|
|
15321
|
-
};
|
|
15322
|
-
}
|
|
15323
|
-
const ul = usage;
|
|
15324
|
-
const inputCandidates = [
|
|
15325
|
-
ul.input_tokens,
|
|
15326
|
-
ul.prompt_tokens,
|
|
15327
|
-
ul.promptTokens,
|
|
15328
|
-
ul.inputTokens
|
|
15329
|
-
].filter((v) => typeof v === "number");
|
|
15330
|
-
const outputCandidates = [
|
|
15331
|
-
ul.output_tokens,
|
|
15332
|
-
ul.completion_tokens,
|
|
15333
|
-
ul.completionTokens,
|
|
15334
|
-
ul.outputTokens
|
|
15335
|
-
].filter((v) => typeof v === "number");
|
|
15336
|
-
const totalCandidates = [
|
|
15337
|
-
ul.total_tokens,
|
|
15338
|
-
ul.totalTokens,
|
|
15339
|
-
ul.totalTokens
|
|
15340
|
-
].filter((v) => typeof v === "number");
|
|
15341
|
-
const input = inputCandidates[0] ?? 0;
|
|
15342
|
-
const output = outputCandidates[0] ?? 0;
|
|
15343
|
-
const total = totalCandidates[0];
|
|
15344
|
-
if (total !== undefined)
|
|
15345
|
-
return { input, output, total };
|
|
15346
|
-
return { input, output, total: input + output };
|
|
15347
|
-
}
|
|
15348
|
-
function breakdownTokens(usage) {
|
|
15349
|
-
if (!usage)
|
|
15350
|
-
return {
|
|
15351
|
-
input: 0,
|
|
15352
|
-
output: 0,
|
|
15353
|
-
total: 0,
|
|
15354
|
-
cacheReads: 0,
|
|
15355
|
-
cacheWrites: 0,
|
|
15356
|
-
reasoningTokens: 0
|
|
15357
|
-
};
|
|
15358
|
-
if (typeof usage.input === "number" && typeof usage.output === "number" && (("cacheReads" in usage) || ("cacheWrites" in usage) || ("total" in usage))) {
|
|
15359
|
-
const u0 = usage;
|
|
15360
|
-
return {
|
|
15361
|
-
input: u0.input,
|
|
15362
|
-
output: u0.output,
|
|
15363
|
-
total: u0.total ?? u0.input + u0.output,
|
|
15364
|
-
cacheReads: u0.cacheReads,
|
|
15365
|
-
cacheWrites: u0.cacheWrites,
|
|
15366
|
-
reasoningTokens: u0.reasoningTokens
|
|
15367
|
-
};
|
|
15368
|
-
}
|
|
15369
|
-
const base = normalizeUsage(usage);
|
|
15370
|
-
const u = usage;
|
|
15371
|
-
const cacheReadCandidates = [
|
|
15372
|
-
u.cache_read_input_tokens,
|
|
15373
|
-
u.cache_read_tokens,
|
|
15374
|
-
u.prompt_cache_hit_tokens,
|
|
15375
|
-
u.prompt_tokens_details?.cached_tokens,
|
|
15376
|
-
u.promptTokensDetails?.cachedTokens,
|
|
15377
|
-
u.cachedInputTokens,
|
|
15378
|
-
u.cacheReads
|
|
15379
|
-
].filter((v) => typeof v === "number");
|
|
15380
|
-
const cacheWriteCandidates = [
|
|
15381
|
-
u.cache_creation_input_tokens,
|
|
15382
|
-
u.cache_creation_tokens,
|
|
15383
|
-
u.prompt_cache_write_tokens,
|
|
15384
|
-
u.promptTokensDetails?.cacheCreationTokens,
|
|
15385
|
-
u.cacheWrites
|
|
15386
|
-
].filter((v) => typeof v === "number");
|
|
15387
|
-
const cacheReads = cacheReadCandidates[0];
|
|
15388
|
-
const cacheWrites = cacheWriteCandidates[0];
|
|
15389
|
-
const reasoningCandidates = [u.reasoning_tokens, u.reasoningTokens].filter((v) => typeof v === "number");
|
|
15390
|
-
const reasoningTokens = reasoningCandidates[0];
|
|
15391
|
-
return { ...base, cacheReads, cacheWrites, reasoningTokens };
|
|
15392
|
-
}
|
|
15393
|
-
function estimateCost({ modelId, usage, catalog }) {
|
|
15394
|
-
const model = resolveModelFromCatalog(modelId, catalog);
|
|
15395
|
-
if (!model?.pricing)
|
|
15396
|
-
return {};
|
|
15397
|
-
const base = normalizeUsage(usage);
|
|
15398
|
-
const breakdown = breakdownTokens(usage);
|
|
15399
|
-
const inputUSD = model.pricing.inputPerMTokens !== undefined ? base.input / TOKENS_PER_MILLION * model.pricing.inputPerMTokens : undefined;
|
|
15400
|
-
const outputUSD = model.pricing.outputPerMTokens !== undefined ? base.output / TOKENS_PER_MILLION * model.pricing.outputPerMTokens : undefined;
|
|
15401
|
-
const reasoningUSD = model.pricing.reasoningPerMTokens !== undefined && typeof breakdown.reasoningTokens === "number" ? breakdown.reasoningTokens / TOKENS_PER_MILLION * model.pricing.reasoningPerMTokens : undefined;
|
|
15402
|
-
const cacheReadUSD = model.pricing.cacheReadPerMTokens !== undefined && typeof breakdown.cacheReads === "number" ? breakdown.cacheReads / TOKENS_PER_MILLION * model.pricing.cacheReadPerMTokens : undefined;
|
|
15403
|
-
const cacheWriteUSD = model.pricing.cacheWritePerMTokens !== undefined && typeof breakdown.cacheWrites === "number" ? breakdown.cacheWrites / TOKENS_PER_MILLION * model.pricing.cacheWritePerMTokens : undefined;
|
|
15404
|
-
const totalParts = [
|
|
15405
|
-
inputUSD,
|
|
15406
|
-
outputUSD,
|
|
15407
|
-
reasoningUSD,
|
|
15408
|
-
cacheReadUSD,
|
|
15409
|
-
cacheWriteUSD
|
|
15410
|
-
].filter((v) => typeof v === "number");
|
|
15411
|
-
const totalUSD = totalParts.length ? totalParts.reduce((a, b) => a + b, 0) : undefined;
|
|
15412
|
-
return {
|
|
15413
|
-
inputUSD,
|
|
15414
|
-
outputUSD,
|
|
15415
|
-
reasoningUSD,
|
|
15416
|
-
cacheReadUSD,
|
|
15417
|
-
cacheWriteUSD,
|
|
15418
|
-
totalUSD
|
|
15419
|
-
};
|
|
15420
|
-
}
|
|
15421
|
-
function resolveModelFromCatalog(idOrAlias, providers) {
|
|
15422
|
-
if (!providers)
|
|
15423
|
-
return;
|
|
15424
|
-
const base = idOrAlias.trim().toLowerCase();
|
|
15425
|
-
const candidates = [base];
|
|
15426
|
-
if (base.includes("/")) {
|
|
15427
|
-
const i = base.indexOf("/");
|
|
15428
|
-
const p = base.slice(0, i);
|
|
15429
|
-
const rest = base.slice(i + 1);
|
|
15430
|
-
candidates.push(`${p}:${rest}`);
|
|
15431
|
-
candidates.push(`${p}:${rest.replace(/(\d)\.(\d)/g, "$1-$2")}`);
|
|
15432
|
-
}
|
|
15433
|
-
if (base.includes(":")) {
|
|
15434
|
-
const i = base.indexOf(":");
|
|
15435
|
-
const p = base.slice(0, i);
|
|
15436
|
-
const rest = base.slice(i + 1);
|
|
15437
|
-
candidates.push(`${p}:${rest.replace(/(\d)\.(\d)/g, "$1-$2")}`);
|
|
15438
|
-
}
|
|
15439
|
-
if (!base.includes(":") && !base.includes("/")) {
|
|
15440
|
-
candidates.push(base.replace(/(\d)\.(\d)/g, "$1-$2"));
|
|
15441
|
-
}
|
|
15442
|
-
for (const key of candidates) {
|
|
15443
|
-
const idx = key.indexOf(":");
|
|
15444
|
-
if (idx > 0) {
|
|
15445
|
-
const providerKey = key.slice(0, idx);
|
|
15446
|
-
const mid = key.slice(idx + 1);
|
|
15447
|
-
for (const [provKey, prov] of iterateProviders(providers)) {
|
|
15448
|
-
if (mid) {
|
|
15449
|
-
const m = prov?.models?.[mid];
|
|
15450
|
-
if (m && (provKey === providerKey || providerKey.length > 0))
|
|
15451
|
-
return mapModelsDevEntry(provKey, mid, m, prov);
|
|
15452
|
-
}
|
|
15453
|
-
}
|
|
15454
|
-
}
|
|
15455
|
-
for (const [provKey, prov] of iterateProviders(providers)) {
|
|
15456
|
-
const m = prov?.models?.[key];
|
|
15457
|
-
if (m)
|
|
15458
|
-
return mapModelsDevEntry(provKey, key, m, prov);
|
|
15459
|
-
}
|
|
15460
|
-
}
|
|
15461
|
-
return;
|
|
15462
|
-
}
|
|
15463
|
-
function mapModelsDevEntry(provider, modelId, m, prov) {
|
|
15464
|
-
const inputMods = Array.isArray(m?.modalities?.input) ? m.modalities?.input : [];
|
|
15465
|
-
const outputMods = Array.isArray(m?.modalities?.output) ? m.modalities?.output : [];
|
|
15466
|
-
const textIn = inputMods.includes("text");
|
|
15467
|
-
const imageIn = inputMods.includes("image");
|
|
15468
|
-
const textOut = outputMods.includes("text");
|
|
15469
|
-
const combinedMax = typeof m?.limit?.context === "number" ? m.limit.context : undefined;
|
|
15470
|
-
const inputMax = typeof m?.limit?.input === "number" ? m.limit.input : undefined;
|
|
15471
|
-
const outputMax = typeof m?.limit?.output === "number" ? m.limit.output : undefined;
|
|
15472
|
-
const pricing = m?.cost && (typeof m.cost.input === "number" || typeof m.cost.output === "number" || typeof m.cost.reasoning === "number" || typeof m.cost.cache_read === "number" || typeof m.cost.cache_write === "number") ? {
|
|
15473
|
-
...typeof m.cost.input === "number" ? { inputPerMTokens: m.cost.input } : {},
|
|
15474
|
-
...typeof m.cost.output === "number" ? { outputPerMTokens: m.cost.output } : {},
|
|
15475
|
-
...typeof m.cost.reasoning === "number" ? { reasoningPerMTokens: m.cost.reasoning } : {},
|
|
15476
|
-
...typeof m.cost.cache_read === "number" ? { cacheReadPerMTokens: m.cost.cache_read } : {},
|
|
15477
|
-
...typeof m.cost.cache_write === "number" ? { cacheWritePerMTokens: m.cost.cache_write } : {}
|
|
15478
|
-
} : undefined;
|
|
15479
|
-
return {
|
|
15480
|
-
id: `${provider}:${modelId}`,
|
|
15481
|
-
provider,
|
|
15482
|
-
vendorId: modelId,
|
|
15483
|
-
displayName: m?.name || modelId,
|
|
15484
|
-
family: modelId,
|
|
15485
|
-
status: "stable",
|
|
15486
|
-
context: Object.fromEntries(Object.entries({ combinedMax, inputMax, outputMax }).filter(([, v]) => typeof v === "number")),
|
|
15487
|
-
modalities: textIn || textOut || imageIn ? {
|
|
15488
|
-
...textIn && { textIn: true },
|
|
15489
|
-
...textOut && { textOut: true },
|
|
15490
|
-
...imageIn && { imageIn: true }
|
|
15491
|
-
} : undefined,
|
|
15492
|
-
pricing,
|
|
15493
|
-
pricingSource: "models.dev",
|
|
15494
|
-
aliases: [`${provider}/${modelId}`],
|
|
15495
|
-
releasedAt: m?.release_date || undefined,
|
|
15496
|
-
source: prov?.doc || prov.docs || "https://models.dev",
|
|
15497
|
-
contextSource: prov?.doc || prov.docs || undefined,
|
|
15498
|
-
verifiedAt: m?.last_updated || undefined
|
|
15499
|
-
};
|
|
15500
|
-
}
|
|
15501
|
-
function* iterateProviders(providers) {
|
|
15502
|
-
if (providers?.models) {
|
|
15503
|
-
const p = providers;
|
|
15504
|
-
const key = p.id || "provider";
|
|
15505
|
-
yield [key, p];
|
|
15506
|
-
return;
|
|
15507
|
-
}
|
|
15508
|
-
for (const [key, prov] of Object.entries(providers)) {
|
|
15509
|
-
yield [key, prov];
|
|
15510
|
-
}
|
|
15511
|
-
}
|
|
15512
|
-
|
|
15513
|
-
// node_modules/@tokenlens/helpers/dist/simple.js
|
|
15514
|
-
function costFromUsage(args) {
|
|
15515
|
-
const c = estimateCost({
|
|
15516
|
-
modelId: args.id,
|
|
15517
|
-
usage: args.usage,
|
|
15518
|
-
catalog: args.catalog
|
|
15519
|
-
});
|
|
15520
|
-
return c.totalUSD;
|
|
15521
|
-
}
|
|
15522
|
-
|
|
15523
|
-
// plugins/token-stats/server/price-catalog.ts
|
|
15524
|
-
var PRICE_CACHE_KEY = "pricing:catalog:v1";
|
|
15525
|
-
var PRICE_STATUS_KEY = "pricing:status:v1";
|
|
15526
|
-
function pricedCatalog(value) {
|
|
15527
|
-
if (!value || typeof value !== "object")
|
|
15528
|
-
throw new Error("invalid_catalog");
|
|
15529
|
-
const input = value;
|
|
15530
|
-
const output = {};
|
|
15531
|
-
let count = 0;
|
|
15532
|
-
for (const id of ["openai", "anthropic", "google", "xai"]) {
|
|
15533
|
-
const provider = input[id];
|
|
15534
|
-
if (!provider?.models || typeof provider.models !== "object")
|
|
15535
|
-
continue;
|
|
15536
|
-
const models = {};
|
|
15537
|
-
for (const [modelId, model] of Object.entries(provider.models)) {
|
|
15538
|
-
const cost = model?.cost;
|
|
15539
|
-
if (!cost || typeof cost.input !== "number" || !Number.isFinite(cost.input) || cost.input < 0 || typeof cost.output !== "number" || !Number.isFinite(cost.output) || cost.output < 0)
|
|
15540
|
-
continue;
|
|
15541
|
-
if (++count > 1e4)
|
|
15542
|
-
throw new Error("invalid_catalog");
|
|
15543
|
-
Object.defineProperty(models, modelId, { value: { id: modelId, name: model.name, cost }, enumerable: true });
|
|
15544
|
-
}
|
|
15545
|
-
if (Object.keys(models).length)
|
|
15546
|
-
output[id] = { id, name: provider.name, models };
|
|
15547
|
-
}
|
|
15548
|
-
if (!count)
|
|
15549
|
-
throw new Error("invalid_catalog");
|
|
15550
|
-
return output;
|
|
15551
15401
|
}
|
|
15552
15402
|
|
|
15553
15403
|
// plugins/token-stats/server/pricing.ts
|
|
15554
|
-
var
|
|
15555
|
-
|
|
15556
|
-
|
|
15557
|
-
function isDirectProvider(value) {
|
|
15558
|
-
return DIRECT_PROVIDERS.has(value);
|
|
15404
|
+
var TOKENS_PER_UNIT = 1e6;
|
|
15405
|
+
function isNonNegativeInteger(value) {
|
|
15406
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
15559
15407
|
}
|
|
15560
|
-
function
|
|
15561
|
-
|
|
15562
|
-
|
|
15563
|
-
|
|
15564
|
-
const delimiterIndex = [model.indexOf(":"), model.indexOf("/")].filter((index) => index > 0).sort((a, b) => a - b)[0];
|
|
15565
|
-
const prefixedProvider = delimiterIndex === undefined ? undefined : model.slice(0, delimiterIndex);
|
|
15566
|
-
const modelId = delimiterIndex === undefined ? model : model.slice(delimiterIndex + 1);
|
|
15567
|
-
if (!modelId)
|
|
15568
|
-
return;
|
|
15569
|
-
if (prefixedProvider === undefined && providerHint === undefined) {
|
|
15570
|
-
const matches = [...DIRECT_PROVIDERS].filter((provider) => catalog[provider]?.models?.[modelId]);
|
|
15571
|
-
if (matches.length !== 1)
|
|
15572
|
-
return;
|
|
15573
|
-
const provider = matches[0];
|
|
15574
|
-
return { provider, modelId, entry: catalog[provider].models[modelId] };
|
|
15575
|
-
}
|
|
15576
|
-
const provider = prefixedProvider ?? providerHint;
|
|
15577
|
-
if (!provider || !isDirectProvider(provider) || !modelId)
|
|
15578
|
-
return;
|
|
15579
|
-
const entry = catalog[provider]?.models?.[modelId];
|
|
15580
|
-
return entry ? { provider, modelId, entry } : undefined;
|
|
15581
|
-
}
|
|
15582
|
-
function hasTieredPricing(cost) {
|
|
15583
|
-
return Object.keys(cost).some((key) => /context|tier|over_\d/i.test(key));
|
|
15584
|
-
}
|
|
15585
|
-
function calculateTokenStatsCost(catalog, input) {
|
|
15586
|
-
const { model, inputTokens, outputTokens } = input;
|
|
15587
|
-
if (!catalog || !model || !Number.isSafeInteger(inputTokens) || inputTokens < 0 || !Number.isSafeInteger(outputTokens) || outputTokens < 0)
|
|
15588
|
-
return null;
|
|
15589
|
-
const match = exactModel(model, input.provider, catalog);
|
|
15590
|
-
if (!match)
|
|
15591
|
-
return null;
|
|
15592
|
-
const rawCost = match.entry.cost;
|
|
15593
|
-
if (!rawCost || !isNonNegativeNumber(rawCost.input) || !isNonNegativeNumber(rawCost.output))
|
|
15594
|
-
return null;
|
|
15595
|
-
if (hasTieredPricing(rawCost) && inputTokens >= 200000)
|
|
15408
|
+
function costOf(match, input) {
|
|
15409
|
+
const inputTokens = input.inputTokens;
|
|
15410
|
+
const outputTokens = input.outputTokens;
|
|
15411
|
+
if (!isNonNegativeInteger(inputTokens) || !isNonNegativeInteger(outputTokens))
|
|
15596
15412
|
return null;
|
|
15597
15413
|
const cacheReads = input.cacheReadTokens ?? 0;
|
|
15598
15414
|
const cacheWrites = input.cacheWriteTokens ?? 0;
|
|
15599
|
-
if (!
|
|
15600
|
-
return null;
|
|
15601
|
-
if (cacheReads > 0 && !isNonNegativeNumber(rawCost.cache_read))
|
|
15415
|
+
if (!isNonNegativeInteger(cacheReads) || !isNonNegativeInteger(cacheWrites) || cacheReads + cacheWrites > inputTokens)
|
|
15602
15416
|
return null;
|
|
15603
|
-
|
|
15604
|
-
|
|
15605
|
-
|
|
15606
|
-
|
|
15607
|
-
|
|
15608
|
-
|
|
15609
|
-
|
|
15610
|
-
|
|
15611
|
-
|
|
15612
|
-
|
|
15613
|
-
|
|
15614
|
-
|
|
15615
|
-
|
|
15616
|
-
|
|
15617
|
-
|
|
15618
|
-
});
|
|
15619
|
-
return isNonNegativeNumber(cost) ? cost : null;
|
|
15620
|
-
} catch {
|
|
15417
|
+
let rates = match;
|
|
15418
|
+
for (const tier of match.contextTiers ?? []) {
|
|
15419
|
+
if (inputTokens < tier.minimumInputTokens)
|
|
15420
|
+
break;
|
|
15421
|
+
rates = tier;
|
|
15422
|
+
}
|
|
15423
|
+
const cacheRead = rates.cacheRead ?? match.cacheRead ?? rates.input;
|
|
15424
|
+
const cacheWrite = rates.cacheWrite ?? match.cacheWrite ?? rates.input;
|
|
15425
|
+
const baseInput = inputTokens - cacheReads - cacheWrites;
|
|
15426
|
+
const total = baseInput * rates.input + outputTokens * rates.output + cacheReads * cacheRead + cacheWrites * cacheWrite;
|
|
15427
|
+
const cost = total / TOKENS_PER_UNIT;
|
|
15428
|
+
return Number.isFinite(cost) && cost >= 0 ? cost : null;
|
|
15429
|
+
}
|
|
15430
|
+
function calculateTokenStatsCost(catalog, input, mappings = []) {
|
|
15431
|
+
if (catalog === null || typeof input.model !== "string" || input.model.length === 0)
|
|
15621
15432
|
return null;
|
|
15622
|
-
|
|
15433
|
+
const mapping = mappings.find((item) => item.source === input.model);
|
|
15434
|
+
if (mapping !== undefined) {
|
|
15435
|
+
const target = catalog.resolveModel({ model: mapping.model, pricingProvider: mapping.provider });
|
|
15436
|
+
return target === null ? null : costOf(target, input);
|
|
15437
|
+
}
|
|
15438
|
+
const match = catalog.resolveModel({
|
|
15439
|
+
model: input.model,
|
|
15440
|
+
mode: "estimate",
|
|
15441
|
+
...input.provider === undefined ? {} : { pricingProvider: input.provider },
|
|
15442
|
+
...input.url === undefined ? {} : { url: input.url }
|
|
15443
|
+
});
|
|
15444
|
+
return match === null ? null : costOf(match, input);
|
|
15623
15445
|
}
|
|
15624
15446
|
|
|
15625
15447
|
class TokenStatsPricing {
|
|
15626
|
-
options;
|
|
15627
15448
|
catalog;
|
|
15628
|
-
|
|
15629
|
-
|
|
15630
|
-
|
|
15631
|
-
initialLoadStarted = false;
|
|
15632
|
-
initialLoadReady = Promise.resolve();
|
|
15633
|
-
cacheTimestamp = null;
|
|
15634
|
-
constructor(options = {}) {
|
|
15635
|
-
this.options = options;
|
|
15636
|
-
}
|
|
15637
|
-
start() {
|
|
15638
|
-
if (this.options.storage) {
|
|
15639
|
-
this.initialLoadReady = this.readCache();
|
|
15640
|
-
return;
|
|
15641
|
-
}
|
|
15642
|
-
if (this.interval !== undefined)
|
|
15643
|
-
return;
|
|
15644
|
-
this.refresh();
|
|
15645
|
-
this.interval = setInterval(() => {
|
|
15646
|
-
this.refresh();
|
|
15647
|
-
}, this.options.refreshMs ?? DEFAULT_REFRESH_MS);
|
|
15648
|
-
}
|
|
15649
|
-
stop() {
|
|
15650
|
-
if (this.interval !== undefined)
|
|
15651
|
-
clearInterval(this.interval);
|
|
15652
|
-
this.interval = undefined;
|
|
15653
|
-
this.controller?.abort();
|
|
15654
|
-
}
|
|
15655
|
-
async refresh() {
|
|
15656
|
-
if (this.inFlight)
|
|
15657
|
-
return this.inFlight;
|
|
15658
|
-
const task = this.loadCatalog();
|
|
15659
|
-
this.inFlight = task;
|
|
15660
|
-
if (!this.initialLoadStarted) {
|
|
15661
|
-
this.initialLoadStarted = true;
|
|
15662
|
-
this.initialLoadReady = this.waitForInitialLoad(task);
|
|
15663
|
-
}
|
|
15664
|
-
try {
|
|
15665
|
-
return await task;
|
|
15666
|
-
} finally {
|
|
15667
|
-
if (this.inFlight === task)
|
|
15668
|
-
this.inFlight = undefined;
|
|
15669
|
-
}
|
|
15449
|
+
mappings = [];
|
|
15450
|
+
constructor(catalog) {
|
|
15451
|
+
this.catalog = catalog;
|
|
15670
15452
|
}
|
|
15671
|
-
|
|
15672
|
-
|
|
15673
|
-
return this.readCache();
|
|
15674
|
-
return this.initialLoadReady;
|
|
15453
|
+
setMappings(mappings) {
|
|
15454
|
+
this.mappings = mappings;
|
|
15675
15455
|
}
|
|
15676
|
-
|
|
15677
|
-
|
|
15678
|
-
const status = await this.options.storage.get(PRICE_STATUS_KEY);
|
|
15679
|
-
if (this.catalog && status?.lastSuccessAt === this.cacheTimestamp)
|
|
15680
|
-
return;
|
|
15681
|
-
const cache = await this.options.storage.get(PRICE_CACHE_KEY);
|
|
15682
|
-
if (cache?.version !== 1 || !Number.isFinite(cache.fetchedAt) || cache.fetchedAt === this.cacheTimestamp)
|
|
15683
|
-
return;
|
|
15684
|
-
this.catalog = pricedCatalog(cache.catalog);
|
|
15685
|
-
this.cacheTimestamp = cache.fetchedAt;
|
|
15686
|
-
} catch {}
|
|
15456
|
+
hasMappings() {
|
|
15457
|
+
return this.mappings !== undefined;
|
|
15687
15458
|
}
|
|
15688
15459
|
estimate(input) {
|
|
15689
|
-
|
|
15690
|
-
|
|
15691
|
-
|
|
15692
|
-
const controller = new AbortController;
|
|
15693
|
-
this.controller = controller;
|
|
15694
|
-
let timeout;
|
|
15695
|
-
try {
|
|
15696
|
-
const fetch = fetchModels(this.options.fetch ? { fetch: this.options.fetch, signal: controller.signal } : { signal: controller.signal });
|
|
15697
|
-
const timeoutFailure = new Promise((_resolve, reject) => {
|
|
15698
|
-
timeout = setTimeout(() => {
|
|
15699
|
-
controller.abort();
|
|
15700
|
-
reject(new Error("TokenLens catalog request timed out"));
|
|
15701
|
-
}, this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
15702
|
-
});
|
|
15703
|
-
const catalog = await Promise.race([fetch, timeoutFailure]);
|
|
15704
|
-
if (!catalog || typeof catalog !== "object")
|
|
15705
|
-
return false;
|
|
15706
|
-
this.catalog = catalog;
|
|
15707
|
-
return true;
|
|
15708
|
-
} catch {
|
|
15709
|
-
return false;
|
|
15710
|
-
} finally {
|
|
15711
|
-
if (timeout !== undefined)
|
|
15712
|
-
clearTimeout(timeout);
|
|
15713
|
-
if (this.controller === controller)
|
|
15714
|
-
this.controller = undefined;
|
|
15715
|
-
}
|
|
15460
|
+
if (this.mappings === undefined)
|
|
15461
|
+
return null;
|
|
15462
|
+
return calculateTokenStatsCost(this.catalog, input, this.mappings);
|
|
15716
15463
|
}
|
|
15717
|
-
|
|
15718
|
-
|
|
15719
|
-
try {
|
|
15720
|
-
await Promise.race([
|
|
15721
|
-
task.then(() => {
|
|
15722
|
-
return;
|
|
15723
|
-
}, () => {
|
|
15724
|
-
return;
|
|
15725
|
-
}),
|
|
15726
|
-
new Promise((resolve) => {
|
|
15727
|
-
timeout = setTimeout(resolve, this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
15728
|
-
})
|
|
15729
|
-
]);
|
|
15730
|
-
} finally {
|
|
15731
|
-
if (timeout !== undefined)
|
|
15732
|
-
clearTimeout(timeout);
|
|
15733
|
-
}
|
|
15464
|
+
ready() {
|
|
15465
|
+
return Promise.resolve();
|
|
15734
15466
|
}
|
|
15735
15467
|
}
|
|
15736
15468
|
|
|
15469
|
+
// plugins/models-dev/contract.ts
|
|
15470
|
+
var MODELS_DEV_CATALOG_SERVICE_ID = "models-dev.catalog.v1";
|
|
15471
|
+
var MODELS_DEV_CATALOG_CONTRACT_VERSION = 1;
|
|
15472
|
+
var MODELS_DEV_CATALOG_SNAPSHOT_ID = "models-dev.catalog.snapshot.v1";
|
|
15473
|
+
var MODELS_DEV_CATALOG_SNAPSHOT_CONTRACT_VERSION = 1;
|
|
15474
|
+
var MODELS_DEV_CATALOG_SNAPSHOT_SCHEMA = Object.freeze({
|
|
15475
|
+
type: "object",
|
|
15476
|
+
properties: {
|
|
15477
|
+
version: { type: "number", integer: true, minimum: 1 },
|
|
15478
|
+
fetchedAt: { type: "number", minimum: 0 },
|
|
15479
|
+
catalog: { type: "json", maxBytes: 32 * 1024 * 1024 }
|
|
15480
|
+
}
|
|
15481
|
+
});
|
|
15482
|
+
var MODELS_DEV_CATALOG_SNAPSHOT_CONTRACT = Object.freeze({
|
|
15483
|
+
id: MODELS_DEV_CATALOG_SNAPSHOT_ID,
|
|
15484
|
+
version: MODELS_DEV_CATALOG_SNAPSHOT_CONTRACT_VERSION,
|
|
15485
|
+
content: MODELS_DEV_CATALOG_SNAPSHOT_SCHEMA
|
|
15486
|
+
});
|
|
15487
|
+
|
|
15737
15488
|
// plugins/token-stats/server/index.ts
|
|
15489
|
+
var MAPPING_REFRESH_INTERVAL_MS = 1e4;
|
|
15738
15490
|
function rowFromResult(result) {
|
|
15739
15491
|
const inputKnown = result.outcome === "completed" || result.inputSource === "official";
|
|
15740
15492
|
const outputKnown = result.outcome === "completed" || result.outputSource === "official";
|
|
@@ -15767,14 +15519,7 @@ class TokenStatsPricingService {
|
|
|
15767
15519
|
this.pricing = pricing;
|
|
15768
15520
|
}
|
|
15769
15521
|
canPrice = async (input) => {
|
|
15770
|
-
|
|
15771
|
-
const cost = this.pricing.estimate({
|
|
15772
|
-
model: input.model,
|
|
15773
|
-
provider: input.pricingProvider,
|
|
15774
|
-
inputTokens: 0,
|
|
15775
|
-
outputTokens: 0
|
|
15776
|
-
});
|
|
15777
|
-
return cost !== null && Number.isFinite(cost) && cost >= 0;
|
|
15522
|
+
return this.pricing.estimate({ model: input.model, provider: input.pricingProvider, inputTokens: 0, outputTokens: 0 }) !== null;
|
|
15778
15523
|
};
|
|
15779
15524
|
price = (result) => {
|
|
15780
15525
|
const row = rowFromResult(result);
|
|
@@ -15793,7 +15538,6 @@ class TokenStatsPricingService {
|
|
|
15793
15538
|
if (existing)
|
|
15794
15539
|
return existing;
|
|
15795
15540
|
const pending = Promise.resolve().then(async () => {
|
|
15796
|
-
await this.pricing.ready().catch(() => {});
|
|
15797
15541
|
const raw = this.pricing.estimate({
|
|
15798
15542
|
model: result.model,
|
|
15799
15543
|
provider: result.pricingProvider,
|
|
@@ -15812,16 +15556,48 @@ class TokenStatsPricingService {
|
|
|
15812
15556
|
return pending;
|
|
15813
15557
|
};
|
|
15814
15558
|
}
|
|
15559
|
+
|
|
15560
|
+
class MappingCache {
|
|
15561
|
+
load;
|
|
15562
|
+
mappings = undefined;
|
|
15563
|
+
loadedAt = 0;
|
|
15564
|
+
inFlight = false;
|
|
15565
|
+
constructor(load) {
|
|
15566
|
+
this.load = load;
|
|
15567
|
+
}
|
|
15568
|
+
current() {
|
|
15569
|
+
return this.mappings;
|
|
15570
|
+
}
|
|
15571
|
+
async refresh(now = Date.now()) {
|
|
15572
|
+
if (this.inFlight)
|
|
15573
|
+
return;
|
|
15574
|
+
this.inFlight = true;
|
|
15575
|
+
try {
|
|
15576
|
+
this.mappings = await this.load();
|
|
15577
|
+
this.loadedAt = now;
|
|
15578
|
+
} catch {
|
|
15579
|
+
this.mappings = undefined;
|
|
15580
|
+
} finally {
|
|
15581
|
+
this.inFlight = false;
|
|
15582
|
+
}
|
|
15583
|
+
}
|
|
15584
|
+
maybeRefresh(now = Date.now()) {
|
|
15585
|
+
if (this.inFlight || now - this.loadedAt < MAPPING_REFRESH_INTERVAL_MS)
|
|
15586
|
+
return;
|
|
15587
|
+
this.refresh(now);
|
|
15588
|
+
}
|
|
15589
|
+
}
|
|
15815
15590
|
var TokenStatsPlugin = definePlugin(class {
|
|
15816
15591
|
pricingFactory;
|
|
15817
15592
|
static name = "token-stats";
|
|
15818
|
-
static version = "
|
|
15593
|
+
static version = "4.0.0";
|
|
15819
15594
|
storage;
|
|
15820
15595
|
logger;
|
|
15821
15596
|
repository;
|
|
15822
15597
|
pricing;
|
|
15823
15598
|
reportingIncomplete = false;
|
|
15824
15599
|
unsubscribe;
|
|
15600
|
+
mappingRefresh;
|
|
15825
15601
|
markReportingIncomplete() {
|
|
15826
15602
|
if (this.reportingIncomplete)
|
|
15827
15603
|
return;
|
|
@@ -15844,19 +15620,34 @@ var TokenStatsPlugin = definePlugin(class {
|
|
|
15844
15620
|
constructor(_config = {}, pricingFactory) {
|
|
15845
15621
|
this.pricingFactory = pricingFactory;
|
|
15846
15622
|
}
|
|
15623
|
+
applyMappings(mappings) {
|
|
15624
|
+
if (typeof this.pricing.setMappings === "function")
|
|
15625
|
+
this.pricing.setMappings(mappings);
|
|
15626
|
+
}
|
|
15847
15627
|
async init(context) {
|
|
15848
15628
|
if (!context.services)
|
|
15849
15629
|
throw new Error("token-stats requires token-metering service");
|
|
15850
15630
|
const metering = context.services.consume("token-metering", TOKEN_METERING_SERVICE_ID, TOKEN_METERING_CONTRACT_VERSION);
|
|
15631
|
+
let catalog = null;
|
|
15632
|
+
try {
|
|
15633
|
+
catalog = context.services.consume("models-dev", MODELS_DEV_CATALOG_SERVICE_ID, MODELS_DEV_CATALOG_CONTRACT_VERSION);
|
|
15634
|
+
} catch {
|
|
15635
|
+
catalog = null;
|
|
15636
|
+
}
|
|
15851
15637
|
this.storage = context.storage.uncached?.() ?? context.storage;
|
|
15852
15638
|
this.logger = context.logger;
|
|
15853
15639
|
this.repository = new TokenStatsRepository(context.storage);
|
|
15854
|
-
this.pricing = this.pricingFactory?.() ?? new TokenStatsPricing(
|
|
15640
|
+
this.pricing = this.pricingFactory?.() ?? new TokenStatsPricing(catalog);
|
|
15641
|
+
const mappings = new MappingCache(() => this.repository.mappings());
|
|
15642
|
+
await mappings.refresh();
|
|
15643
|
+
this.applyMappings(mappings.current());
|
|
15855
15644
|
const pricingService = new TokenStatsPricingService(this.pricing);
|
|
15856
15645
|
context.services.publish(TOKEN_PRICING_SERVICE_ID, TOKEN_PRICING_CONTRACT_VERSION, { canPrice: pricingService.canPrice, price: pricingService.price });
|
|
15857
15646
|
this.unsubscribe = metering.subscribe({
|
|
15858
15647
|
onResult: (result) => {
|
|
15859
15648
|
const row = rowFromResult(result);
|
|
15649
|
+
mappings.maybeRefresh();
|
|
15650
|
+
this.applyMappings(mappings.current());
|
|
15860
15651
|
const price = pricingService.price(result);
|
|
15861
15652
|
price.catch(() => {});
|
|
15862
15653
|
const accepted = this.repository.enqueueAttempt(async () => {
|
|
@@ -15884,18 +15675,24 @@ var TokenStatsPlugin = definePlugin(class {
|
|
|
15884
15675
|
this.markReportingIncomplete();
|
|
15885
15676
|
}
|
|
15886
15677
|
});
|
|
15678
|
+
this.mappingRefresh = setInterval(() => {
|
|
15679
|
+
mappings.refresh().then(() => this.applyMappings(mappings.current()));
|
|
15680
|
+
}, MAPPING_REFRESH_INTERVAL_MS);
|
|
15681
|
+
if (typeof this.mappingRefresh.unref === "function")
|
|
15682
|
+
this.mappingRefresh.unref();
|
|
15887
15683
|
context.services.onDispose(() => {
|
|
15888
15684
|
this.unsubscribe?.();
|
|
15889
15685
|
this.unsubscribe = undefined;
|
|
15890
15686
|
});
|
|
15891
|
-
this.pricing.start();
|
|
15892
15687
|
this.logger.info("TokenStatsPlugin initialized");
|
|
15893
15688
|
}
|
|
15894
15689
|
register(_hooks) {}
|
|
15895
15690
|
async onDestroy() {
|
|
15896
15691
|
this.unsubscribe?.();
|
|
15897
15692
|
this.unsubscribe = undefined;
|
|
15898
|
-
this.
|
|
15693
|
+
if (this.mappingRefresh !== undefined)
|
|
15694
|
+
clearInterval(this.mappingRefresh);
|
|
15695
|
+
this.mappingRefresh = undefined;
|
|
15899
15696
|
this.logger?.info("TokenStatsPlugin destroyed");
|
|
15900
15697
|
}
|
|
15901
15698
|
});
|
|
@@ -15906,4 +15703,4 @@ export {
|
|
|
15906
15703
|
server_default as default
|
|
15907
15704
|
};
|
|
15908
15705
|
|
|
15909
|
-
//# debugId=
|
|
15706
|
+
//# debugId=667505D0D6B6158D64756E2164756E21
|