llm-usage-metrics 0.3.6 → 0.4.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/README.md +17 -1
- package/dist/index.js +877 -81
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -538,7 +538,15 @@ function isTruthyEnvFlag(value) {
|
|
|
538
538
|
}
|
|
539
539
|
function shouldSkipUpdateCheckForArgv(argv) {
|
|
540
540
|
const executableArgs = argv.slice(2);
|
|
541
|
-
const commandNames = /* @__PURE__ */ new Set([
|
|
541
|
+
const commandNames = /* @__PURE__ */ new Set([
|
|
542
|
+
"daily",
|
|
543
|
+
"weekly",
|
|
544
|
+
"monthly",
|
|
545
|
+
"efficiency",
|
|
546
|
+
"optimize",
|
|
547
|
+
"help",
|
|
548
|
+
"version"
|
|
549
|
+
]);
|
|
542
550
|
if (executableArgs.length === 0) {
|
|
543
551
|
return false;
|
|
544
552
|
}
|
|
@@ -674,6 +682,35 @@ function normalizeModelList(models) {
|
|
|
674
682
|
return [...deduplicated].sort(compareByCodePoint);
|
|
675
683
|
}
|
|
676
684
|
|
|
685
|
+
// src/domain/provider-normalization.ts
|
|
686
|
+
var billingProviderAliases = /* @__PURE__ */ new Map([
|
|
687
|
+
["openai-codex", "openai"],
|
|
688
|
+
["github-copilot", "github"]
|
|
689
|
+
]);
|
|
690
|
+
var billingProviderPrefixAliases = [
|
|
691
|
+
["openai-", "openai"],
|
|
692
|
+
["openai/", "openai"]
|
|
693
|
+
];
|
|
694
|
+
function normalizeProviderToBillingEntity(provider) {
|
|
695
|
+
if (!provider) {
|
|
696
|
+
return void 0;
|
|
697
|
+
}
|
|
698
|
+
const normalizedProvider = provider.trim().toLowerCase();
|
|
699
|
+
if (normalizedProvider.length === 0) {
|
|
700
|
+
return void 0;
|
|
701
|
+
}
|
|
702
|
+
const aliasedProvider = billingProviderAliases.get(normalizedProvider);
|
|
703
|
+
if (aliasedProvider) {
|
|
704
|
+
return aliasedProvider;
|
|
705
|
+
}
|
|
706
|
+
for (const [prefix, billingProvider] of billingProviderPrefixAliases) {
|
|
707
|
+
if (normalizedProvider.startsWith(prefix)) {
|
|
708
|
+
return billingProvider;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
return normalizedProvider;
|
|
712
|
+
}
|
|
713
|
+
|
|
677
714
|
// src/domain/usage-event.ts
|
|
678
715
|
function normalizeSourceId(value) {
|
|
679
716
|
if (typeof value !== "string") {
|
|
@@ -696,6 +733,9 @@ function normalizeOptionalText(value) {
|
|
|
696
733
|
const normalized = value.trim();
|
|
697
734
|
return normalized || void 0;
|
|
698
735
|
}
|
|
736
|
+
function normalizeOptionalProvider(value) {
|
|
737
|
+
return normalizeProviderToBillingEntity(value);
|
|
738
|
+
}
|
|
699
739
|
function normalizeOptionalPath(value) {
|
|
700
740
|
return normalizeOptionalText(value);
|
|
701
741
|
}
|
|
@@ -735,7 +775,7 @@ function createUsageEvent(input) {
|
|
|
735
775
|
sessionId: requireText(input.sessionId, "sessionId"),
|
|
736
776
|
timestamp: normalizeTimestamp(input.timestamp),
|
|
737
777
|
repoRoot: normalizeOptionalPath(input.repoRoot),
|
|
738
|
-
provider:
|
|
778
|
+
provider: normalizeOptionalProvider(input.provider),
|
|
739
779
|
model: normalizeOptionalModel(input.model),
|
|
740
780
|
inputTokens,
|
|
741
781
|
outputTokens,
|
|
@@ -974,6 +1014,13 @@ function hasUsageSignal(usage) {
|
|
|
974
1014
|
function deriveDeltaUsage(info, previousTotalUsage) {
|
|
975
1015
|
const totalUsage = toUsage(info.total_token_usage);
|
|
976
1016
|
const lastUsage = toUsage(info.last_token_usage);
|
|
1017
|
+
if (totalUsage && previousTotalUsage) {
|
|
1018
|
+
const deltaFromTotals = subtractUsage(totalUsage, previousTotalUsage);
|
|
1019
|
+
if (hasUsageSignal(deltaFromTotals)) {
|
|
1020
|
+
return { deltaUsage: deltaFromTotals, latestTotalUsage: totalUsage };
|
|
1021
|
+
}
|
|
1022
|
+
return { latestTotalUsage: totalUsage };
|
|
1023
|
+
}
|
|
977
1024
|
if (lastUsage) {
|
|
978
1025
|
return { deltaUsage: lastUsage, latestTotalUsage: totalUsage };
|
|
979
1026
|
}
|
|
@@ -3264,6 +3311,42 @@ async function attributeUsageEventsToRepo(events, repoDir, resolveRepoRoot3 = re
|
|
|
3264
3311
|
};
|
|
3265
3312
|
}
|
|
3266
3313
|
|
|
3314
|
+
// src/cli/build-usage-data-diagnostics.ts
|
|
3315
|
+
function buildUsageDiagnostics(params) {
|
|
3316
|
+
const parseResultBySource = new Map(
|
|
3317
|
+
params.successfulParseResults.map((result) => [result.source.toLowerCase(), result])
|
|
3318
|
+
);
|
|
3319
|
+
const sessionStats = params.adaptersToParse.map((adapter) => {
|
|
3320
|
+
const parseResult = parseResultBySource.get(adapter.id.toLowerCase());
|
|
3321
|
+
return {
|
|
3322
|
+
source: adapter.id,
|
|
3323
|
+
filesFound: parseResult?.filesFound ?? 0,
|
|
3324
|
+
eventsParsed: parseResult?.events.length ?? 0
|
|
3325
|
+
};
|
|
3326
|
+
});
|
|
3327
|
+
const skippedRows = params.successfulParseResults.filter((result) => result.skippedRows > 0).map((result) => ({
|
|
3328
|
+
source: result.source,
|
|
3329
|
+
skippedRows: result.skippedRows,
|
|
3330
|
+
reasons: result.skippedRowReasons
|
|
3331
|
+
}));
|
|
3332
|
+
return {
|
|
3333
|
+
sessionStats,
|
|
3334
|
+
sourceFailures: params.sourceFailures,
|
|
3335
|
+
skippedRows,
|
|
3336
|
+
pricingOrigin: params.pricingOrigin,
|
|
3337
|
+
pricingWarning: params.pricingWarning,
|
|
3338
|
+
activeEnvOverrides: params.activeEnvOverrides,
|
|
3339
|
+
timezone: params.timezone
|
|
3340
|
+
};
|
|
3341
|
+
}
|
|
3342
|
+
function assembleUsageDataResult(events, rows, diagnostics) {
|
|
3343
|
+
return {
|
|
3344
|
+
events,
|
|
3345
|
+
rows,
|
|
3346
|
+
diagnostics
|
|
3347
|
+
};
|
|
3348
|
+
}
|
|
3349
|
+
|
|
3267
3350
|
// src/config/env-var-display.ts
|
|
3268
3351
|
var ENV_VARS_TO_DISPLAY = [
|
|
3269
3352
|
{ name: "LLM_USAGE_SKIP_UPDATE_CHECK", description: "skip startup update check" },
|
|
@@ -3304,42 +3387,6 @@ function formatEnvVarOverrides(overrides) {
|
|
|
3304
3387
|
return lines;
|
|
3305
3388
|
}
|
|
3306
3389
|
|
|
3307
|
-
// src/cli/build-usage-data-diagnostics.ts
|
|
3308
|
-
function buildUsageDiagnostics(params) {
|
|
3309
|
-
const parseResultBySource = new Map(
|
|
3310
|
-
params.successfulParseResults.map((result) => [result.source.toLowerCase(), result])
|
|
3311
|
-
);
|
|
3312
|
-
const sessionStats = params.adaptersToParse.map((adapter) => {
|
|
3313
|
-
const parseResult = parseResultBySource.get(adapter.id.toLowerCase());
|
|
3314
|
-
return {
|
|
3315
|
-
source: adapter.id,
|
|
3316
|
-
filesFound: parseResult?.filesFound ?? 0,
|
|
3317
|
-
eventsParsed: parseResult?.events.length ?? 0
|
|
3318
|
-
};
|
|
3319
|
-
});
|
|
3320
|
-
const skippedRows = params.successfulParseResults.filter((result) => result.skippedRows > 0).map((result) => ({
|
|
3321
|
-
source: result.source,
|
|
3322
|
-
skippedRows: result.skippedRows,
|
|
3323
|
-
reasons: result.skippedRowReasons
|
|
3324
|
-
}));
|
|
3325
|
-
return {
|
|
3326
|
-
sessionStats,
|
|
3327
|
-
sourceFailures: params.sourceFailures,
|
|
3328
|
-
skippedRows,
|
|
3329
|
-
pricingOrigin: params.pricingOrigin,
|
|
3330
|
-
pricingWarning: params.pricingWarning,
|
|
3331
|
-
activeEnvOverrides: params.activeEnvOverrides,
|
|
3332
|
-
timezone: params.timezone
|
|
3333
|
-
};
|
|
3334
|
-
}
|
|
3335
|
-
function assembleUsageDataResult(events, rows, diagnostics) {
|
|
3336
|
-
return {
|
|
3337
|
-
events,
|
|
3338
|
-
rows,
|
|
3339
|
-
diagnostics
|
|
3340
|
-
};
|
|
3341
|
-
}
|
|
3342
|
-
|
|
3343
3390
|
// src/cli/build-usage-data-inputs.ts
|
|
3344
3391
|
function validateDateInput(value, flagName) {
|
|
3345
3392
|
if (!/^\d{4}-\d{2}-\d{2}$/u.test(value)) {
|
|
@@ -3359,11 +3406,7 @@ function validateTimezone(timezone) {
|
|
|
3359
3406
|
}
|
|
3360
3407
|
}
|
|
3361
3408
|
function normalizeProviderFilter(provider) {
|
|
3362
|
-
|
|
3363
|
-
return void 0;
|
|
3364
|
-
}
|
|
3365
|
-
const normalized = provider.trim().toLowerCase();
|
|
3366
|
-
return normalized || void 0;
|
|
3409
|
+
return normalizeProviderToBillingEntity(provider);
|
|
3367
3410
|
}
|
|
3368
3411
|
function normalizeSourceFilter(source) {
|
|
3369
3412
|
if (!source || Array.isArray(source) && source.length === 0) {
|
|
@@ -3540,7 +3583,7 @@ function normalizeSkippedRowReasons(value) {
|
|
|
3540
3583
|
// src/cli/parse-file-cache.ts
|
|
3541
3584
|
import { mkdir as mkdir2, readFile as readFile4, rename, rm, stat as stat3, writeFile as writeFile2 } from "fs/promises";
|
|
3542
3585
|
import path11 from "path";
|
|
3543
|
-
var PARSE_FILE_CACHE_VERSION =
|
|
3586
|
+
var PARSE_FILE_CACHE_VERSION = 4;
|
|
3544
3587
|
var CACHE_KEY_SEPARATOR = "\0";
|
|
3545
3588
|
function createCacheKey(source, filePath) {
|
|
3546
3589
|
return `${source}${CACHE_KEY_SEPARATOR}${filePath}`;
|
|
@@ -3599,7 +3642,9 @@ function normalizeCachedUsageEvent(value) {
|
|
|
3599
3642
|
if (inputTokens === void 0 || outputTokens === void 0 || reasoningTokens === void 0 || cacheReadTokens === void 0 || cacheWriteTokens === void 0 || totalTokens === void 0) {
|
|
3600
3643
|
return void 0;
|
|
3601
3644
|
}
|
|
3602
|
-
const provider =
|
|
3645
|
+
const provider = normalizeProviderToBillingEntity(
|
|
3646
|
+
typeof record.provider === "string" ? record.provider : void 0
|
|
3647
|
+
);
|
|
3603
3648
|
const model = typeof record.model === "string" ? record.model.trim().toLowerCase() : "";
|
|
3604
3649
|
const costUsd = toNonNegativeNumber2(record.costUsd);
|
|
3605
3650
|
if (costMode === "explicit" && costUsd === void 0) {
|
|
@@ -3610,7 +3655,7 @@ function normalizeCachedUsageEvent(value) {
|
|
|
3610
3655
|
sessionId,
|
|
3611
3656
|
timestamp,
|
|
3612
3657
|
repoRoot: repoRoot || void 0,
|
|
3613
|
-
provider
|
|
3658
|
+
provider,
|
|
3614
3659
|
model: model || void 0,
|
|
3615
3660
|
inputTokens,
|
|
3616
3661
|
outputTokens,
|
|
@@ -4745,9 +4790,20 @@ function shouldLoadPricingSource(events) {
|
|
|
4745
4790
|
}
|
|
4746
4791
|
return events.some((event) => eventNeedsPricingLookup(event));
|
|
4747
4792
|
}
|
|
4748
|
-
|
|
4793
|
+
function hasAnyBillableTokenBuckets(events) {
|
|
4794
|
+
return events.some(
|
|
4795
|
+
(event) => event.inputTokens > 0 || event.outputTokens > 0 || event.reasoningTokens > 0 || event.cacheReadTokens > 0 || event.cacheWriteTokens > 0
|
|
4796
|
+
);
|
|
4797
|
+
}
|
|
4798
|
+
function shouldLoadPricingSourceForMode(events, pricingLoadMode) {
|
|
4799
|
+
if (pricingLoadMode === "force") {
|
|
4800
|
+
return hasAnyBillableTokenBuckets(events);
|
|
4801
|
+
}
|
|
4802
|
+
return shouldLoadPricingSource(events);
|
|
4803
|
+
}
|
|
4804
|
+
async function resolveAndApplyPricingToEvents(events, options, runtimeConfig = getPricingFetcherRuntimeConfig(), loadPricingSource = resolvePricingSource, pricingLoadMode = "auto") {
|
|
4749
4805
|
let pricingOrigin = "none";
|
|
4750
|
-
if (!
|
|
4806
|
+
if (!shouldLoadPricingSourceForMode(events, pricingLoadMode)) {
|
|
4751
4807
|
return {
|
|
4752
4808
|
pricedEvents: events,
|
|
4753
4809
|
pricingOrigin
|
|
@@ -4771,11 +4827,12 @@ async function resolveAndApplyPricingToEvents(events, options, runtimeConfig = g
|
|
|
4771
4827
|
pricingOrigin = pricingResult.origin;
|
|
4772
4828
|
return {
|
|
4773
4829
|
pricedEvents: applyPricingToEvents(events, pricingResult.source),
|
|
4774
|
-
pricingOrigin
|
|
4830
|
+
pricingOrigin,
|
|
4831
|
+
pricingSource: pricingResult.source
|
|
4775
4832
|
};
|
|
4776
4833
|
}
|
|
4777
4834
|
|
|
4778
|
-
// src/cli/build-usage-
|
|
4835
|
+
// src/cli/build-usage-event-dataset.ts
|
|
4779
4836
|
function withNormalizedPricingUrl(options, normalizedPricingUrl) {
|
|
4780
4837
|
if (options.pricingUrl === normalizedPricingUrl) {
|
|
4781
4838
|
return options;
|
|
@@ -4785,13 +4842,11 @@ function withNormalizedPricingUrl(options, normalizedPricingUrl) {
|
|
|
4785
4842
|
pricingUrl: normalizedPricingUrl
|
|
4786
4843
|
};
|
|
4787
4844
|
}
|
|
4788
|
-
async function
|
|
4845
|
+
async function buildUsageEventDataset(options, deps = {}) {
|
|
4789
4846
|
const normalizedInputs = normalizeBuildUsageInputs(options);
|
|
4790
4847
|
const readParsingRuntimeConfig = deps.getParsingRuntimeConfig ?? getParsingRuntimeConfig;
|
|
4791
4848
|
const readPricingRuntimeConfig = deps.getPricingFetcherRuntimeConfig ?? getPricingFetcherRuntimeConfig;
|
|
4792
4849
|
const makeAdapters = deps.createAdapters ?? createDefaultAdapters;
|
|
4793
|
-
const loadPricingSource = deps.resolvePricingSource ?? resolvePricingSource;
|
|
4794
|
-
const readEnvVarOverrides = deps.getActiveEnvVarOverrides ?? getActiveEnvVarOverrides;
|
|
4795
4850
|
const parsingRuntimeConfig = readParsingRuntimeConfig();
|
|
4796
4851
|
const pricingRuntimeConfig = readPricingRuntimeConfig();
|
|
4797
4852
|
const adapters = makeAdapters(options);
|
|
@@ -4816,26 +4871,53 @@ async function buildUsageData(granularity, options, deps = {}) {
|
|
|
4816
4871
|
providerFilter: normalizedInputs.providerFilter,
|
|
4817
4872
|
modelFilter: normalizedInputs.modelFilter
|
|
4818
4873
|
});
|
|
4819
|
-
|
|
4820
|
-
|
|
4874
|
+
return {
|
|
4875
|
+
options,
|
|
4876
|
+
normalizedInputs,
|
|
4877
|
+
adaptersToParse,
|
|
4878
|
+
successfulParseResults,
|
|
4879
|
+
sourceFailures,
|
|
4821
4880
|
filteredEvents,
|
|
4822
|
-
pricingOptions,
|
|
4823
4881
|
pricingRuntimeConfig,
|
|
4824
|
-
|
|
4882
|
+
readEnvVarOverrides: deps.getActiveEnvVarOverrides ?? getActiveEnvVarOverrides
|
|
4883
|
+
};
|
|
4884
|
+
}
|
|
4885
|
+
async function applyPricingToUsageEventDataset(dataset, deps = {}, pricingLoadMode = "auto") {
|
|
4886
|
+
const loadPricingSource = deps.resolvePricingSource ?? resolvePricingSource;
|
|
4887
|
+
const pricingOptions = withNormalizedPricingUrl(
|
|
4888
|
+
dataset.options,
|
|
4889
|
+
dataset.normalizedInputs.pricingUrl
|
|
4890
|
+
);
|
|
4891
|
+
return resolveAndApplyPricingToEvents(
|
|
4892
|
+
dataset.filteredEvents,
|
|
4893
|
+
pricingOptions,
|
|
4894
|
+
dataset.pricingRuntimeConfig,
|
|
4895
|
+
loadPricingSource,
|
|
4896
|
+
pricingLoadMode
|
|
4897
|
+
);
|
|
4898
|
+
}
|
|
4899
|
+
|
|
4900
|
+
// src/cli/build-usage-data.ts
|
|
4901
|
+
async function buildUsageData(granularity, options, deps = {}) {
|
|
4902
|
+
const dataset = await buildUsageEventDataset(options, deps);
|
|
4903
|
+
const { pricedEvents, pricingOrigin, pricingWarning } = await applyPricingToUsageEventDataset(
|
|
4904
|
+
dataset,
|
|
4905
|
+
deps,
|
|
4906
|
+
"auto"
|
|
4825
4907
|
);
|
|
4826
4908
|
const rows = aggregateUsage(pricedEvents, {
|
|
4827
4909
|
granularity,
|
|
4828
|
-
timezone: normalizedInputs.timezone,
|
|
4829
|
-
sourceOrder: adaptersToParse.map((adapter) => adapter.id)
|
|
4910
|
+
timezone: dataset.normalizedInputs.timezone,
|
|
4911
|
+
sourceOrder: dataset.adaptersToParse.map((adapter) => adapter.id)
|
|
4830
4912
|
});
|
|
4831
4913
|
const diagnostics = buildUsageDiagnostics({
|
|
4832
|
-
adaptersToParse,
|
|
4833
|
-
successfulParseResults,
|
|
4834
|
-
sourceFailures,
|
|
4914
|
+
adaptersToParse: dataset.adaptersToParse,
|
|
4915
|
+
successfulParseResults: dataset.successfulParseResults,
|
|
4916
|
+
sourceFailures: dataset.sourceFailures,
|
|
4835
4917
|
pricingOrigin,
|
|
4836
4918
|
pricingWarning,
|
|
4837
|
-
activeEnvOverrides: readEnvVarOverrides(),
|
|
4838
|
-
timezone: normalizedInputs.timezone
|
|
4919
|
+
activeEnvOverrides: dataset.readEnvVarOverrides(),
|
|
4920
|
+
timezone: dataset.normalizedInputs.timezone
|
|
4839
4921
|
});
|
|
4840
4922
|
return assembleUsageDataResult(pricedEvents, rows, diagnostics);
|
|
4841
4923
|
}
|
|
@@ -5252,6 +5334,7 @@ function warnIfTerminalTableOverflows(reportOutput, warn, stdoutState = process.
|
|
|
5252
5334
|
|
|
5253
5335
|
// src/render/render-efficiency-report.ts
|
|
5254
5336
|
import { markdownTable } from "markdown-table";
|
|
5337
|
+
import pc5 from "picocolors";
|
|
5255
5338
|
|
|
5256
5339
|
// src/render/report-header.ts
|
|
5257
5340
|
import pc2 from "picocolors";
|
|
@@ -5397,8 +5480,8 @@ function resolveSourceStyler(source, palette = defaultTerminalStylePalette) {
|
|
|
5397
5480
|
var rowTypeStylePolicies = {
|
|
5398
5481
|
period_source: (cells, palette) => {
|
|
5399
5482
|
const styledCells = [...cells];
|
|
5400
|
-
const
|
|
5401
|
-
styledCells[
|
|
5483
|
+
const costColumnIndex2 = styledCells.length - 1;
|
|
5484
|
+
styledCells[costColumnIndex2] = styleCellLines(styledCells[costColumnIndex2], palette.yellow);
|
|
5402
5485
|
return styledCells;
|
|
5403
5486
|
},
|
|
5404
5487
|
period_combined: (cells, palette) => cells.map((cell, cellIndex) => {
|
|
@@ -5843,6 +5926,14 @@ function renderTerminalTable(rows, options = {}) {
|
|
|
5843
5926
|
// src/render/render-efficiency-report.ts
|
|
5844
5927
|
var periodColumnIndex = 0;
|
|
5845
5928
|
var minimumEfficiencyColumnWidth = 1;
|
|
5929
|
+
var commitsColumnIndex = 1;
|
|
5930
|
+
var linesAddedColumnIndex = 2;
|
|
5931
|
+
var linesDeletedColumnIndex = 3;
|
|
5932
|
+
var linesChangedColumnIndex = 4;
|
|
5933
|
+
var costColumnIndex = 11;
|
|
5934
|
+
var usdPerCommitColumnIndex = 12;
|
|
5935
|
+
var usdPer1kLinesChangedColumnIndex = 13;
|
|
5936
|
+
var commitsPerUsdColumnIndex = 16;
|
|
5846
5937
|
function getReportTitle(granularity) {
|
|
5847
5938
|
switch (granularity) {
|
|
5848
5939
|
case "daily":
|
|
@@ -5981,9 +6072,67 @@ function fitTableCellsToTerminal(headerCells, bodyRows) {
|
|
|
5981
6072
|
widths: constrainedWidths
|
|
5982
6073
|
};
|
|
5983
6074
|
}
|
|
5984
|
-
function
|
|
6075
|
+
function styleDeltaCell(value, formattedValue, options) {
|
|
6076
|
+
if (!options.useColor) {
|
|
6077
|
+
return formattedValue;
|
|
6078
|
+
}
|
|
6079
|
+
if (value > 0) {
|
|
6080
|
+
return pc5.green(formattedValue);
|
|
6081
|
+
}
|
|
6082
|
+
if (value < 0) {
|
|
6083
|
+
return pc5.red(formattedValue);
|
|
6084
|
+
}
|
|
6085
|
+
return pc5.dim(formattedValue);
|
|
6086
|
+
}
|
|
6087
|
+
function styleEfficiencyTerminalRows(rows, bodyRows, options) {
|
|
6088
|
+
return bodyRows.map((cells, rowIndex) => {
|
|
6089
|
+
if (!options.useColor) {
|
|
6090
|
+
return [...cells];
|
|
6091
|
+
}
|
|
6092
|
+
const row = rows[rowIndex];
|
|
6093
|
+
const styledCells = [...cells];
|
|
6094
|
+
const periodCell = styledCells[periodColumnIndex];
|
|
6095
|
+
styledCells[periodColumnIndex] = row.rowType === "grand_total" ? pc5.bold(pc5.cyan(periodCell)) : pc5.bold(periodCell);
|
|
6096
|
+
styledCells[commitsColumnIndex] = pc5.bold(styledCells[commitsColumnIndex]);
|
|
6097
|
+
styledCells[linesAddedColumnIndex] = styleDeltaCell(
|
|
6098
|
+
row.linesAdded,
|
|
6099
|
+
styledCells[linesAddedColumnIndex],
|
|
6100
|
+
options
|
|
6101
|
+
);
|
|
6102
|
+
styledCells[linesDeletedColumnIndex] = styleDeltaCell(
|
|
6103
|
+
row.linesDeleted * -1,
|
|
6104
|
+
styledCells[linesDeletedColumnIndex],
|
|
6105
|
+
options
|
|
6106
|
+
);
|
|
6107
|
+
styledCells[linesChangedColumnIndex] = styleDeltaCell(
|
|
6108
|
+
row.linesChanged,
|
|
6109
|
+
styledCells[linesChangedColumnIndex],
|
|
6110
|
+
options
|
|
6111
|
+
);
|
|
6112
|
+
const costValue = row.costUsd;
|
|
6113
|
+
if (costValue !== void 0 && costValue > 0) {
|
|
6114
|
+
styledCells[costColumnIndex] = pc5.yellow(styledCells[costColumnIndex]);
|
|
6115
|
+
}
|
|
6116
|
+
const usdPerCommitValue = row.usdPerCommit;
|
|
6117
|
+
if (usdPerCommitValue !== void 0 && usdPerCommitValue > 0) {
|
|
6118
|
+
styledCells[usdPerCommitColumnIndex] = pc5.yellow(styledCells[usdPerCommitColumnIndex]);
|
|
6119
|
+
}
|
|
6120
|
+
const usdPer1kLinesChangedValue = row.usdPer1kLinesChanged;
|
|
6121
|
+
if (usdPer1kLinesChangedValue !== void 0 && usdPer1kLinesChangedValue > 0) {
|
|
6122
|
+
styledCells[usdPer1kLinesChangedColumnIndex] = pc5.yellow(
|
|
6123
|
+
styledCells[usdPer1kLinesChangedColumnIndex]
|
|
6124
|
+
);
|
|
6125
|
+
}
|
|
6126
|
+
const commitsPerUsdValue = row.commitsPerUsd;
|
|
6127
|
+
if (commitsPerUsdValue !== void 0 && commitsPerUsdValue > 0) {
|
|
6128
|
+
styledCells[commitsPerUsdColumnIndex] = pc5.green(styledCells[commitsPerUsdColumnIndex]);
|
|
6129
|
+
}
|
|
6130
|
+
return styledCells;
|
|
6131
|
+
});
|
|
6132
|
+
}
|
|
6133
|
+
function renderTerminalEfficiencyTable(rows, options) {
|
|
5985
6134
|
const headerCells = Array.from(efficiencyTableHeaders);
|
|
5986
|
-
const bodyRows = toEfficiencyTableCells(rows);
|
|
6135
|
+
const bodyRows = styleEfficiencyTerminalRows(rows, toEfficiencyTableCells(rows), options);
|
|
5987
6136
|
const tableSortRows = rows.map((row) => toTableSortRow(row));
|
|
5988
6137
|
const fittedCells = fitTableCellsToTerminal(headerCells, bodyRows);
|
|
5989
6138
|
return renderUnicodeTable({
|
|
@@ -6020,7 +6169,7 @@ function renderTerminalEfficiencyReport(efficiencyData, options) {
|
|
|
6020
6169
|
})
|
|
6021
6170
|
);
|
|
6022
6171
|
outputLines.push("");
|
|
6023
|
-
outputLines.push(renderTerminalEfficiencyTable(efficiencyData.rows));
|
|
6172
|
+
outputLines.push(renderTerminalEfficiencyTable(efficiencyData.rows, { useColor }));
|
|
6024
6173
|
return outputLines.join("\n");
|
|
6025
6174
|
}
|
|
6026
6175
|
function renderEfficiencyReport(efficiencyData, format, options) {
|
|
@@ -6083,25 +6232,656 @@ async function runEfficiencyReport(granularity, options) {
|
|
|
6083
6232
|
console.log(preparedReport.output);
|
|
6084
6233
|
}
|
|
6085
6234
|
|
|
6086
|
-
// src/render/
|
|
6235
|
+
// src/render/render-optimize-report.ts
|
|
6087
6236
|
import { markdownTable as markdownTable2 } from "markdown-table";
|
|
6088
|
-
|
|
6237
|
+
import pc6 from "picocolors";
|
|
6238
|
+
var optimizeTableHeadersWithNotes = [
|
|
6239
|
+
"Period",
|
|
6240
|
+
"Candidate",
|
|
6241
|
+
"Hypothetical Cost",
|
|
6242
|
+
"Baseline Cost",
|
|
6243
|
+
"Savings",
|
|
6244
|
+
"Savings %",
|
|
6245
|
+
"Notes"
|
|
6246
|
+
];
|
|
6247
|
+
var optimizeTableHeadersWithoutNotes = [
|
|
6248
|
+
"Period",
|
|
6249
|
+
"Candidate",
|
|
6250
|
+
"Hypothetical Cost",
|
|
6251
|
+
"Baseline Cost",
|
|
6252
|
+
"Savings",
|
|
6253
|
+
"Savings %"
|
|
6254
|
+
];
|
|
6255
|
+
var usdFormatter3 = new Intl.NumberFormat("en-US", {
|
|
6256
|
+
style: "currency",
|
|
6257
|
+
currency: "USD",
|
|
6258
|
+
minimumFractionDigits: 2,
|
|
6259
|
+
maximumFractionDigits: 2
|
|
6260
|
+
});
|
|
6261
|
+
function getReportTitle2(granularity) {
|
|
6262
|
+
switch (granularity) {
|
|
6263
|
+
case "daily":
|
|
6264
|
+
return "Daily Optimize Report";
|
|
6265
|
+
case "weekly":
|
|
6266
|
+
return "Weekly Optimize Report";
|
|
6267
|
+
case "monthly":
|
|
6268
|
+
return "Monthly Optimize Report";
|
|
6269
|
+
}
|
|
6270
|
+
}
|
|
6271
|
+
function formatUsd3(value, options = {}) {
|
|
6272
|
+
if (value === void 0) {
|
|
6273
|
+
return "-";
|
|
6274
|
+
}
|
|
6275
|
+
const formatted = usdFormatter3.format(value);
|
|
6276
|
+
return options.approximate ? `~${formatted}` : formatted;
|
|
6277
|
+
}
|
|
6278
|
+
function formatPercent(value) {
|
|
6279
|
+
if (value === void 0) {
|
|
6280
|
+
return "-";
|
|
6281
|
+
}
|
|
6282
|
+
return `${(value * 100).toFixed(2)}%`;
|
|
6283
|
+
}
|
|
6284
|
+
function formatNotes(notes) {
|
|
6285
|
+
if (!notes || notes.length === 0) {
|
|
6286
|
+
return "-";
|
|
6287
|
+
}
|
|
6288
|
+
return notes.join(", ");
|
|
6289
|
+
}
|
|
6290
|
+
function styleCandidateCell(candidateValue, rowType, useColor) {
|
|
6291
|
+
if (!useColor) {
|
|
6292
|
+
return candidateValue;
|
|
6293
|
+
}
|
|
6294
|
+
if (rowType === "baseline") {
|
|
6295
|
+
return pc6.bold(pc6.cyan(candidateValue));
|
|
6296
|
+
}
|
|
6297
|
+
return pc6.bold(candidateValue);
|
|
6298
|
+
}
|
|
6299
|
+
function styleDeltaCell2(value, formattedValue, useColor) {
|
|
6300
|
+
if (!useColor || value === void 0) {
|
|
6301
|
+
return formattedValue;
|
|
6302
|
+
}
|
|
6303
|
+
if (value > 0) {
|
|
6304
|
+
return pc6.green(formattedValue);
|
|
6305
|
+
}
|
|
6306
|
+
if (value < 0) {
|
|
6307
|
+
return pc6.red(formattedValue);
|
|
6308
|
+
}
|
|
6309
|
+
return pc6.dim(formattedValue);
|
|
6310
|
+
}
|
|
6311
|
+
function styleNotesCell(notes, formattedNotes, useColor) {
|
|
6312
|
+
if (!useColor || !notes || notes.length === 0) {
|
|
6313
|
+
return formattedNotes;
|
|
6314
|
+
}
|
|
6315
|
+
return pc6.yellow(formattedNotes);
|
|
6316
|
+
}
|
|
6317
|
+
function formatAbsoluteUsd(value) {
|
|
6318
|
+
return usdFormatter3.format(Math.abs(value));
|
|
6319
|
+
}
|
|
6320
|
+
function resolveTerminalContextLines(optimizeData, options) {
|
|
6321
|
+
const allBaselineRow = optimizeData.rows.find(
|
|
6322
|
+
(row) => row.rowType === "baseline" && row.periodKey === "ALL"
|
|
6323
|
+
);
|
|
6324
|
+
const allCandidateRows = optimizeData.rows.filter(
|
|
6325
|
+
(row) => row.rowType === "candidate" && row.periodKey === "ALL"
|
|
6326
|
+
);
|
|
6327
|
+
const lines = [];
|
|
6328
|
+
const providerLine = `Provider scope: ${optimizeData.diagnostics.provider}`;
|
|
6329
|
+
lines.push(options.useColor ? pc6.cyan(providerLine) : providerLine);
|
|
6330
|
+
if (allBaselineRow) {
|
|
6331
|
+
lines.push(
|
|
6332
|
+
`ALL baseline cost: ${formatUsd3(allBaselineRow.baselineCostUsd, { approximate: allBaselineRow.baselineCostIncomplete })}`
|
|
6333
|
+
);
|
|
6334
|
+
}
|
|
6335
|
+
if (allCandidateRows.length > 0) {
|
|
6336
|
+
const rowsWithSavings = allCandidateRows.filter((row) => row.savingsUsd !== void 0);
|
|
6337
|
+
const bestRow = rowsWithSavings.length > 0 ? rowsWithSavings.reduce(
|
|
6338
|
+
(best, current) => (current.savingsUsd ?? Number.NEGATIVE_INFINITY) > (best.savingsUsd ?? Number.NEGATIVE_INFINITY) ? current : best
|
|
6339
|
+
) : void 0;
|
|
6340
|
+
if (!bestRow || bestRow.savingsUsd === void 0) {
|
|
6341
|
+
lines.push("ALL best candidate: unavailable (missing baseline or candidate pricing)");
|
|
6342
|
+
} else if (bestRow.savingsUsd > 0) {
|
|
6343
|
+
lines.push(
|
|
6344
|
+
`ALL best candidate: ${bestRow.candidateModel} saves ${formatAbsoluteUsd(bestRow.savingsUsd)} (${formatPercent(bestRow.savingsPct)})`
|
|
6345
|
+
);
|
|
6346
|
+
} else if (bestRow.savingsUsd < 0) {
|
|
6347
|
+
lines.push(
|
|
6348
|
+
`ALL best candidate: ${bestRow.candidateModel} increases cost by ${formatAbsoluteUsd(bestRow.savingsUsd)} (${formatPercent(bestRow.savingsPct)})`
|
|
6349
|
+
);
|
|
6350
|
+
} else {
|
|
6351
|
+
lines.push(`ALL best candidate: ${bestRow.candidateModel} matches baseline cost`);
|
|
6352
|
+
}
|
|
6353
|
+
}
|
|
6354
|
+
if (optimizeData.diagnostics.candidatesWithMissingPricing.length > 0) {
|
|
6355
|
+
const missingLine = `Missing candidate pricing: ${optimizeData.diagnostics.candidatesWithMissingPricing.join(", ")}`;
|
|
6356
|
+
lines.push(options.useColor ? pc6.yellow(missingLine) : missingLine);
|
|
6357
|
+
}
|
|
6358
|
+
const legendLine = "Savings = Baseline - Hypothetical (positive means cheaper candidate)";
|
|
6359
|
+
lines.push(options.useColor ? pc6.dim(legendLine) : legendLine);
|
|
6360
|
+
return lines;
|
|
6361
|
+
}
|
|
6362
|
+
function toTableCells(optimizeData, options) {
|
|
6363
|
+
const baselineByPeriod = new Map(
|
|
6364
|
+
optimizeData.rows.filter((row) => row.rowType === "baseline").map((row) => [row.periodKey, row])
|
|
6365
|
+
);
|
|
6366
|
+
return optimizeData.rows.map((row) => {
|
|
6367
|
+
const baselineRow = baselineByPeriod.get(row.periodKey);
|
|
6368
|
+
const periodCell = options.useColor && row.periodKey === "ALL" ? pc6.bold(row.periodKey) : row.periodKey;
|
|
6369
|
+
if (row.rowType === "baseline") {
|
|
6370
|
+
const baselineCells = [
|
|
6371
|
+
periodCell,
|
|
6372
|
+
styleCandidateCell("BASELINE", "baseline", options.useColor),
|
|
6373
|
+
"-",
|
|
6374
|
+
formatUsd3(row.baselineCostUsd, { approximate: row.baselineCostIncomplete }),
|
|
6375
|
+
"-",
|
|
6376
|
+
"-"
|
|
6377
|
+
];
|
|
6378
|
+
return options.includeNotesColumn ? [...baselineCells, "-"] : baselineCells;
|
|
6379
|
+
}
|
|
6380
|
+
const savingsCell = formatUsd3(row.savingsUsd);
|
|
6381
|
+
const savingsPctCell = formatPercent(row.savingsPct);
|
|
6382
|
+
const notesCell = formatNotes(row.notes);
|
|
6383
|
+
const candidateCells = [
|
|
6384
|
+
periodCell,
|
|
6385
|
+
styleCandidateCell(row.candidateModel, "candidate", options.useColor),
|
|
6386
|
+
formatUsd3(row.hypotheticalCostUsd, { approximate: row.hypotheticalCostIncomplete }),
|
|
6387
|
+
formatUsd3(baselineRow?.baselineCostUsd, {
|
|
6388
|
+
approximate: baselineRow?.baselineCostIncomplete === true
|
|
6389
|
+
}),
|
|
6390
|
+
styleDeltaCell2(row.savingsUsd, savingsCell, options.useColor),
|
|
6391
|
+
styleDeltaCell2(row.savingsPct, savingsPctCell, options.useColor)
|
|
6392
|
+
];
|
|
6393
|
+
return options.includeNotesColumn ? [...candidateCells, styleNotesCell(row.notes, notesCell, options.useColor)] : candidateCells;
|
|
6394
|
+
});
|
|
6395
|
+
}
|
|
6089
6396
|
function toMarkdownSafeCell2(value) {
|
|
6090
6397
|
return value.replace(/\r?\n/gu, "<br>");
|
|
6091
6398
|
}
|
|
6399
|
+
function toSortingUsageRows(optimizeData) {
|
|
6400
|
+
return optimizeData.rows.map((row) => {
|
|
6401
|
+
return {
|
|
6402
|
+
rowType: "period_source",
|
|
6403
|
+
periodKey: row.periodKey,
|
|
6404
|
+
source: "combined",
|
|
6405
|
+
models: [],
|
|
6406
|
+
modelBreakdown: [],
|
|
6407
|
+
inputTokens: row.inputTokens,
|
|
6408
|
+
outputTokens: row.outputTokens,
|
|
6409
|
+
reasoningTokens: row.reasoningTokens,
|
|
6410
|
+
cacheReadTokens: row.cacheReadTokens,
|
|
6411
|
+
cacheWriteTokens: row.cacheWriteTokens,
|
|
6412
|
+
totalTokens: row.totalTokens,
|
|
6413
|
+
costUsd: row.rowType === "baseline" ? row.baselineCostUsd : row.hypotheticalCostUsd,
|
|
6414
|
+
costIncomplete: row.rowType === "baseline" ? row.baselineCostIncomplete : row.hypotheticalCostIncomplete
|
|
6415
|
+
};
|
|
6416
|
+
});
|
|
6417
|
+
}
|
|
6418
|
+
function resolveCandidateColumnWidth(tableCells) {
|
|
6419
|
+
return tableCells.reduce((maxWidth, row) => {
|
|
6420
|
+
const candidateValue = row[1] ?? "";
|
|
6421
|
+
return Math.max(maxWidth, visibleWidth(candidateValue));
|
|
6422
|
+
}, visibleWidth(optimizeTableHeadersWithNotes[1]));
|
|
6423
|
+
}
|
|
6424
|
+
function resolveIncludeNotesColumn(optimizeData) {
|
|
6425
|
+
return optimizeData.rows.some(
|
|
6426
|
+
(row) => row.rowType === "candidate" && row.notes !== void 0 && row.notes.length > 0
|
|
6427
|
+
);
|
|
6428
|
+
}
|
|
6429
|
+
function renderTerminalOptimizeReport(optimizeData, options) {
|
|
6430
|
+
const useColor = options.useColor ?? shouldUseColorByDefault();
|
|
6431
|
+
const includeNotesColumn = resolveIncludeNotesColumn(optimizeData);
|
|
6432
|
+
const tableCells = toTableCells(optimizeData, { useColor, includeNotesColumn });
|
|
6433
|
+
const candidateColumnWidth = resolveCandidateColumnWidth(tableCells);
|
|
6434
|
+
const contextLines = resolveTerminalContextLines(optimizeData, { useColor });
|
|
6435
|
+
const headerCells = includeNotesColumn ? [...optimizeTableHeadersWithNotes] : [...optimizeTableHeadersWithoutNotes];
|
|
6436
|
+
const outputLines = [];
|
|
6437
|
+
outputLines.push(
|
|
6438
|
+
renderReportHeader({
|
|
6439
|
+
title: getReportTitle2(options.granularity),
|
|
6440
|
+
useColor
|
|
6441
|
+
})
|
|
6442
|
+
);
|
|
6443
|
+
outputLines.push("");
|
|
6444
|
+
outputLines.push(...contextLines);
|
|
6445
|
+
outputLines.push("");
|
|
6446
|
+
outputLines.push(
|
|
6447
|
+
renderUnicodeTable({
|
|
6448
|
+
headerCells,
|
|
6449
|
+
bodyRows: tableCells,
|
|
6450
|
+
measureHeaderCells: headerCells,
|
|
6451
|
+
measureBodyRows: tableCells,
|
|
6452
|
+
usageRows: toSortingUsageRows(optimizeData),
|
|
6453
|
+
tableLayout: "compact",
|
|
6454
|
+
modelsColumnIndex: 1,
|
|
6455
|
+
modelsColumnWidth: candidateColumnWidth
|
|
6456
|
+
})
|
|
6457
|
+
);
|
|
6458
|
+
return outputLines.join("\n");
|
|
6459
|
+
}
|
|
6460
|
+
function renderMarkdownOptimizeReport(optimizeData) {
|
|
6461
|
+
const includeNotesColumn = resolveIncludeNotesColumn(optimizeData);
|
|
6462
|
+
const headerCells = includeNotesColumn ? [...optimizeTableHeadersWithNotes] : [...optimizeTableHeadersWithoutNotes];
|
|
6463
|
+
const bodyRows = toTableCells(optimizeData, {
|
|
6464
|
+
useColor: false,
|
|
6465
|
+
includeNotesColumn
|
|
6466
|
+
}).map((row) => row.map((cell) => toMarkdownSafeCell2(cell)));
|
|
6467
|
+
const tableRows = [headerCells, ...bodyRows];
|
|
6468
|
+
const alignment2 = headerCells.map((_, index) => index <= 1 ? "l" : "r");
|
|
6469
|
+
return markdownTable2(tableRows, { align: alignment2 });
|
|
6470
|
+
}
|
|
6471
|
+
function renderOptimizeReport(optimizeData, format, options) {
|
|
6472
|
+
switch (format) {
|
|
6473
|
+
case "json":
|
|
6474
|
+
return JSON.stringify(optimizeData.rows, null, 2);
|
|
6475
|
+
case "markdown":
|
|
6476
|
+
return renderMarkdownOptimizeReport(optimizeData);
|
|
6477
|
+
case "terminal":
|
|
6478
|
+
return renderTerminalOptimizeReport(optimizeData, options);
|
|
6479
|
+
}
|
|
6480
|
+
}
|
|
6481
|
+
|
|
6482
|
+
// src/optimize/aggregate-counterfactual.ts
|
|
6483
|
+
var USD_PRECISION_SCALE3 = 1e12;
|
|
6484
|
+
function roundUsd(value) {
|
|
6485
|
+
return Math.round(value * USD_PRECISION_SCALE3) / USD_PRECISION_SCALE3;
|
|
6486
|
+
}
|
|
6487
|
+
function hasZeroBillableTokenBuckets(totals) {
|
|
6488
|
+
return totals.inputTokens === 0 && totals.outputTokens === 0 && totals.reasoningTokens === 0 && totals.cacheReadTokens === 0 && totals.cacheWriteTokens === 0;
|
|
6489
|
+
}
|
|
6490
|
+
function parseCandidateModelsRaw(candidateModel) {
|
|
6491
|
+
if (!candidateModel || Array.isArray(candidateModel) && candidateModel.length === 0) {
|
|
6492
|
+
throw new Error("At least one --candidate-model is required");
|
|
6493
|
+
}
|
|
6494
|
+
const normalizedCandidates = (Array.isArray(candidateModel) ? candidateModel : [candidateModel]).flatMap((candidate) => candidate.split(",")).map((candidate) => candidate.trim().toLowerCase()).filter((candidate) => candidate.length > 0);
|
|
6495
|
+
if (normalizedCandidates.length === 0) {
|
|
6496
|
+
throw new Error("--candidate-model must contain at least one non-empty model name");
|
|
6497
|
+
}
|
|
6498
|
+
return [...new Set(normalizedCandidates)];
|
|
6499
|
+
}
|
|
6500
|
+
function normalizeCandidateModels(candidateModel) {
|
|
6501
|
+
return parseCandidateModelsRaw(candidateModel);
|
|
6502
|
+
}
|
|
6503
|
+
function parseTopOption(top) {
|
|
6504
|
+
if (top === void 0) {
|
|
6505
|
+
return void 0;
|
|
6506
|
+
}
|
|
6507
|
+
const normalized = top.trim();
|
|
6508
|
+
const parsed = Number.parseInt(normalized, 10);
|
|
6509
|
+
if (!/^\d+$/u.test(normalized) || Number.isNaN(parsed) || parsed < 1) {
|
|
6510
|
+
throw new Error("--top must be a positive integer");
|
|
6511
|
+
}
|
|
6512
|
+
return parsed;
|
|
6513
|
+
}
|
|
6514
|
+
function toBaselineRow(period, provider) {
|
|
6515
|
+
return {
|
|
6516
|
+
rowType: "baseline",
|
|
6517
|
+
periodKey: period.periodKey,
|
|
6518
|
+
provider,
|
|
6519
|
+
inputTokens: period.inputTokens,
|
|
6520
|
+
outputTokens: period.outputTokens,
|
|
6521
|
+
reasoningTokens: period.reasoningTokens,
|
|
6522
|
+
cacheReadTokens: period.cacheReadTokens,
|
|
6523
|
+
cacheWriteTokens: period.cacheWriteTokens,
|
|
6524
|
+
totalTokens: period.totalTokens,
|
|
6525
|
+
baselineCostUsd: period.baselineCostUsd,
|
|
6526
|
+
baselineCostIncomplete: period.baselineCostIncomplete
|
|
6527
|
+
};
|
|
6528
|
+
}
|
|
6529
|
+
function createSyntheticEvent(period) {
|
|
6530
|
+
return {
|
|
6531
|
+
source: "pi",
|
|
6532
|
+
sessionId: "optimize-period",
|
|
6533
|
+
timestamp: "1970-01-01T00:00:00.000Z",
|
|
6534
|
+
provider: void 0,
|
|
6535
|
+
model: "synthetic",
|
|
6536
|
+
inputTokens: period.inputTokens,
|
|
6537
|
+
outputTokens: period.outputTokens,
|
|
6538
|
+
reasoningTokens: period.reasoningTokens,
|
|
6539
|
+
cacheReadTokens: period.cacheReadTokens,
|
|
6540
|
+
cacheWriteTokens: period.cacheWriteTokens,
|
|
6541
|
+
totalTokens: period.totalTokens,
|
|
6542
|
+
costMode: "estimated",
|
|
6543
|
+
costUsd: void 0
|
|
6544
|
+
};
|
|
6545
|
+
}
|
|
6546
|
+
function withNotes(notes) {
|
|
6547
|
+
if (notes.size === 0) {
|
|
6548
|
+
return void 0;
|
|
6549
|
+
}
|
|
6550
|
+
return [...notes].sort(compareByCodePoint);
|
|
6551
|
+
}
|
|
6552
|
+
function evaluateCandidateForPeriod(period, provider, candidateModel, pricingSource) {
|
|
6553
|
+
const notes = /* @__PURE__ */ new Set();
|
|
6554
|
+
const zeroBillableTokens = hasZeroBillableTokenBuckets(period);
|
|
6555
|
+
const candidateResolvedModel = pricingSource ? pricingSource.resolveModelAlias(candidateModel) : candidateModel;
|
|
6556
|
+
const pricing = pricingSource ? pricingSource.getPricing(candidateResolvedModel) : void 0;
|
|
6557
|
+
let hypotheticalCostUsd;
|
|
6558
|
+
let hypotheticalCostIncomplete = false;
|
|
6559
|
+
if (!pricing) {
|
|
6560
|
+
if (zeroBillableTokens) {
|
|
6561
|
+
hypotheticalCostUsd = 0;
|
|
6562
|
+
} else {
|
|
6563
|
+
hypotheticalCostUsd = void 0;
|
|
6564
|
+
hypotheticalCostIncomplete = true;
|
|
6565
|
+
notes.add("missing_pricing");
|
|
6566
|
+
}
|
|
6567
|
+
} else {
|
|
6568
|
+
hypotheticalCostUsd = roundUsd(
|
|
6569
|
+
calculateEstimatedCostUsd(createSyntheticEvent(period), pricing)
|
|
6570
|
+
);
|
|
6571
|
+
}
|
|
6572
|
+
let savingsUsd;
|
|
6573
|
+
let savingsPct;
|
|
6574
|
+
let hasBaselineTokenMismatch = false;
|
|
6575
|
+
if (period.baselineCostIncomplete || period.baselineCostUsd === void 0) {
|
|
6576
|
+
notes.add("baseline_incomplete");
|
|
6577
|
+
} else if (zeroBillableTokens && period.baselineCostUsd > 0) {
|
|
6578
|
+
notes.add("baseline_tokens_missing");
|
|
6579
|
+
hasBaselineTokenMismatch = true;
|
|
6580
|
+
} else if (hypotheticalCostUsd !== void 0) {
|
|
6581
|
+
savingsUsd = roundUsd(period.baselineCostUsd - hypotheticalCostUsd);
|
|
6582
|
+
savingsPct = period.baselineCostUsd === 0 ? void 0 : savingsUsd / period.baselineCostUsd;
|
|
6583
|
+
}
|
|
6584
|
+
return {
|
|
6585
|
+
candidateRow: {
|
|
6586
|
+
rowType: "candidate",
|
|
6587
|
+
periodKey: period.periodKey,
|
|
6588
|
+
provider,
|
|
6589
|
+
inputTokens: period.inputTokens,
|
|
6590
|
+
outputTokens: period.outputTokens,
|
|
6591
|
+
reasoningTokens: period.reasoningTokens,
|
|
6592
|
+
cacheReadTokens: period.cacheReadTokens,
|
|
6593
|
+
cacheWriteTokens: period.cacheWriteTokens,
|
|
6594
|
+
totalTokens: period.totalTokens,
|
|
6595
|
+
candidateModel,
|
|
6596
|
+
candidateResolvedModel,
|
|
6597
|
+
hypotheticalCostUsd,
|
|
6598
|
+
hypotheticalCostIncomplete,
|
|
6599
|
+
savingsUsd,
|
|
6600
|
+
savingsPct,
|
|
6601
|
+
notes: withNotes(notes)
|
|
6602
|
+
},
|
|
6603
|
+
missingPricing: notes.has("missing_pricing"),
|
|
6604
|
+
hasBaselineTokenMismatch
|
|
6605
|
+
};
|
|
6606
|
+
}
|
|
6607
|
+
function compareCandidateRank(left, right) {
|
|
6608
|
+
if (left.hypotheticalCostUsd === void 0 && right.hypotheticalCostUsd !== void 0) {
|
|
6609
|
+
return 1;
|
|
6610
|
+
}
|
|
6611
|
+
if (left.hypotheticalCostUsd !== void 0 && right.hypotheticalCostUsd === void 0) {
|
|
6612
|
+
return -1;
|
|
6613
|
+
}
|
|
6614
|
+
if (left.hypotheticalCostUsd !== void 0 && right.hypotheticalCostUsd !== void 0) {
|
|
6615
|
+
if (left.hypotheticalCostUsd !== right.hypotheticalCostUsd) {
|
|
6616
|
+
return left.hypotheticalCostUsd - right.hypotheticalCostUsd;
|
|
6617
|
+
}
|
|
6618
|
+
}
|
|
6619
|
+
return compareByCodePoint(left.candidateModel, right.candidateModel);
|
|
6620
|
+
}
|
|
6621
|
+
function resolveBaselinePeriods(usageRows) {
|
|
6622
|
+
const periodRows = /* @__PURE__ */ new Map();
|
|
6623
|
+
let grandTotalRow;
|
|
6624
|
+
for (const row of usageRows) {
|
|
6625
|
+
if (row.rowType === "grand_total") {
|
|
6626
|
+
grandTotalRow = row;
|
|
6627
|
+
continue;
|
|
6628
|
+
}
|
|
6629
|
+
if (row.rowType === "period_combined") {
|
|
6630
|
+
periodRows.set(row.periodKey, row);
|
|
6631
|
+
continue;
|
|
6632
|
+
}
|
|
6633
|
+
if (!periodRows.has(row.periodKey)) {
|
|
6634
|
+
periodRows.set(row.periodKey, row);
|
|
6635
|
+
}
|
|
6636
|
+
}
|
|
6637
|
+
const sortedPeriodKeys = [...periodRows.keys()].sort(compareByCodePoint);
|
|
6638
|
+
const periods = sortedPeriodKeys.map((periodKey) => {
|
|
6639
|
+
const row = periodRows.get(periodKey);
|
|
6640
|
+
if (!row) {
|
|
6641
|
+
throw new Error(`Missing baseline row for period ${periodKey}`);
|
|
6642
|
+
}
|
|
6643
|
+
return {
|
|
6644
|
+
periodKey,
|
|
6645
|
+
inputTokens: row.inputTokens,
|
|
6646
|
+
outputTokens: row.outputTokens,
|
|
6647
|
+
reasoningTokens: row.reasoningTokens,
|
|
6648
|
+
cacheReadTokens: row.cacheReadTokens,
|
|
6649
|
+
cacheWriteTokens: row.cacheWriteTokens,
|
|
6650
|
+
totalTokens: row.totalTokens,
|
|
6651
|
+
baselineCostUsd: row.costUsd,
|
|
6652
|
+
baselineCostIncomplete: row.costIncomplete === true
|
|
6653
|
+
};
|
|
6654
|
+
});
|
|
6655
|
+
const allRow = grandTotalRow;
|
|
6656
|
+
if (allRow) {
|
|
6657
|
+
periods.push({
|
|
6658
|
+
periodKey: "ALL",
|
|
6659
|
+
inputTokens: allRow.inputTokens,
|
|
6660
|
+
outputTokens: allRow.outputTokens,
|
|
6661
|
+
reasoningTokens: allRow.reasoningTokens,
|
|
6662
|
+
cacheReadTokens: allRow.cacheReadTokens,
|
|
6663
|
+
cacheWriteTokens: allRow.cacheWriteTokens,
|
|
6664
|
+
totalTokens: allRow.totalTokens,
|
|
6665
|
+
baselineCostUsd: allRow.costUsd,
|
|
6666
|
+
baselineCostIncomplete: allRow.costIncomplete === true
|
|
6667
|
+
});
|
|
6668
|
+
} else {
|
|
6669
|
+
periods.push({
|
|
6670
|
+
periodKey: "ALL",
|
|
6671
|
+
inputTokens: 0,
|
|
6672
|
+
outputTokens: 0,
|
|
6673
|
+
reasoningTokens: 0,
|
|
6674
|
+
cacheReadTokens: 0,
|
|
6675
|
+
cacheWriteTokens: 0,
|
|
6676
|
+
totalTokens: 0,
|
|
6677
|
+
baselineCostUsd: 0,
|
|
6678
|
+
baselineCostIncomplete: false
|
|
6679
|
+
});
|
|
6680
|
+
}
|
|
6681
|
+
return periods;
|
|
6682
|
+
}
|
|
6683
|
+
function buildWarning(periodKeys) {
|
|
6684
|
+
if (periodKeys.length === 0) {
|
|
6685
|
+
return void 0;
|
|
6686
|
+
}
|
|
6687
|
+
const sortedPeriodKeys = [...new Set(periodKeys)].sort(compareByCodePoint);
|
|
6688
|
+
return `Baseline cost exists for zero-token periods (${sortedPeriodKeys.join(", ")}); savings were omitted.`;
|
|
6689
|
+
}
|
|
6690
|
+
function buildCounterfactualRows(input) {
|
|
6691
|
+
const baselinePeriods = resolveBaselinePeriods(input.usageRows);
|
|
6692
|
+
const allPeriod = baselinePeriods.find((period) => period.periodKey === "ALL");
|
|
6693
|
+
if (!allPeriod) {
|
|
6694
|
+
throw new Error("Missing ALL baseline totals");
|
|
6695
|
+
}
|
|
6696
|
+
const allPeriodEvaluations = input.candidateModels.map(
|
|
6697
|
+
(candidateModel) => evaluateCandidateForPeriod(allPeriod, input.provider, candidateModel, input.pricingSource)
|
|
6698
|
+
);
|
|
6699
|
+
const rankedCandidates = allPeriodEvaluations.map(({ candidateRow }) => ({
|
|
6700
|
+
candidateModel: candidateRow.candidateModel,
|
|
6701
|
+
hypotheticalCostUsd: candidateRow.hypotheticalCostUsd
|
|
6702
|
+
})).sort(compareCandidateRank).map((candidate) => candidate.candidateModel);
|
|
6703
|
+
const selectedCandidates = input.top === void 0 ? rankedCandidates : rankedCandidates.slice(0, input.top);
|
|
6704
|
+
const allEvaluationByCandidate = new Map(
|
|
6705
|
+
allPeriodEvaluations.map((evaluation) => [evaluation.candidateRow.candidateModel, evaluation])
|
|
6706
|
+
);
|
|
6707
|
+
const candidatesWithMissingPricing = input.candidateModels.filter(
|
|
6708
|
+
(candidateModel) => allEvaluationByCandidate.get(candidateModel)?.missingPricing === true
|
|
6709
|
+
).sort(compareByCodePoint);
|
|
6710
|
+
const warningPeriods = [];
|
|
6711
|
+
const rows = [];
|
|
6712
|
+
for (const period of baselinePeriods) {
|
|
6713
|
+
rows.push(toBaselineRow(period, input.provider));
|
|
6714
|
+
for (const candidateModel of selectedCandidates) {
|
|
6715
|
+
const resolvedEvaluation = period.periodKey === "ALL" ? allEvaluationByCandidate.get(candidateModel) : evaluateCandidateForPeriod(period, input.provider, candidateModel, input.pricingSource);
|
|
6716
|
+
if (!resolvedEvaluation) {
|
|
6717
|
+
continue;
|
|
6718
|
+
}
|
|
6719
|
+
if (resolvedEvaluation.hasBaselineTokenMismatch) {
|
|
6720
|
+
warningPeriods.push(period.periodKey);
|
|
6721
|
+
}
|
|
6722
|
+
rows.push(resolvedEvaluation.candidateRow);
|
|
6723
|
+
}
|
|
6724
|
+
}
|
|
6725
|
+
return {
|
|
6726
|
+
rows,
|
|
6727
|
+
candidatesWithMissingPricing,
|
|
6728
|
+
baselineCostIncomplete: allPeriod.baselineCostIncomplete,
|
|
6729
|
+
warning: buildWarning(warningPeriods)
|
|
6730
|
+
};
|
|
6731
|
+
}
|
|
6732
|
+
|
|
6733
|
+
// src/cli/build-optimize-data.ts
|
|
6734
|
+
function resolveOptimizeProvider(providers, providerFilter) {
|
|
6735
|
+
const distinctProviders = [...providers].sort(compareByCodePoint);
|
|
6736
|
+
const normalizedProviderFilter = normalizeProviderFilter(providerFilter);
|
|
6737
|
+
if (distinctProviders.length > 1) {
|
|
6738
|
+
if (normalizedProviderFilter) {
|
|
6739
|
+
const matchingProviders = distinctProviders.filter(
|
|
6740
|
+
(provider) => provider.includes(normalizedProviderFilter)
|
|
6741
|
+
);
|
|
6742
|
+
if (matchingProviders.includes(normalizedProviderFilter)) {
|
|
6743
|
+
return normalizedProviderFilter;
|
|
6744
|
+
}
|
|
6745
|
+
if (matchingProviders.length === 1) {
|
|
6746
|
+
return matchingProviders[0];
|
|
6747
|
+
}
|
|
6748
|
+
if (matchingProviders.length === 0) {
|
|
6749
|
+
throw new Error(
|
|
6750
|
+
`Optimize --provider "${normalizedProviderFilter}" matched no providers. Available providers: ${distinctProviders.join(", ")}.`
|
|
6751
|
+
);
|
|
6752
|
+
}
|
|
6753
|
+
if (matchingProviders.length > 1) {
|
|
6754
|
+
throw new Error(
|
|
6755
|
+
`Optimize matched multiple providers for --provider "${normalizedProviderFilter}": ${matchingProviders.join(", ")}. Supply a more specific --provider value.`
|
|
6756
|
+
);
|
|
6757
|
+
}
|
|
6758
|
+
}
|
|
6759
|
+
throw new Error(
|
|
6760
|
+
`Optimize requires a single provider; found providers: ${distinctProviders.join(", ")}. Narrow with --provider.`
|
|
6761
|
+
);
|
|
6762
|
+
}
|
|
6763
|
+
if (distinctProviders.length === 1) {
|
|
6764
|
+
return distinctProviders[0];
|
|
6765
|
+
}
|
|
6766
|
+
return normalizedProviderFilter ?? "unknown";
|
|
6767
|
+
}
|
|
6768
|
+
async function buildOptimizeData(granularity, options, deps = {}) {
|
|
6769
|
+
const candidateModels = normalizeCandidateModels(options.candidateModel);
|
|
6770
|
+
const top = parseTopOption(options.top);
|
|
6771
|
+
const dataset = await buildUsageEventDataset(options, deps);
|
|
6772
|
+
const detectedProviders = new Set(
|
|
6773
|
+
dataset.filteredEvents.map((event) => normalizeProviderFilter(event.provider)).filter((provider2) => provider2 !== void 0)
|
|
6774
|
+
);
|
|
6775
|
+
const provider = resolveOptimizeProvider(
|
|
6776
|
+
detectedProviders,
|
|
6777
|
+
dataset.normalizedInputs.providerFilter
|
|
6778
|
+
);
|
|
6779
|
+
const { pricedEvents, pricingOrigin, pricingWarning, pricingSource } = await applyPricingToUsageEventDataset(dataset, deps, "force");
|
|
6780
|
+
const usageRows = aggregateUsage(pricedEvents, {
|
|
6781
|
+
granularity,
|
|
6782
|
+
timezone: dataset.normalizedInputs.timezone,
|
|
6783
|
+
sourceOrder: dataset.adaptersToParse.map((adapter) => adapter.id)
|
|
6784
|
+
});
|
|
6785
|
+
const counterfactual = buildCounterfactualRows({
|
|
6786
|
+
usageRows,
|
|
6787
|
+
provider,
|
|
6788
|
+
candidateModels,
|
|
6789
|
+
pricingSource,
|
|
6790
|
+
top
|
|
6791
|
+
});
|
|
6792
|
+
const usageDiagnostics = buildUsageDiagnostics({
|
|
6793
|
+
adaptersToParse: dataset.adaptersToParse,
|
|
6794
|
+
successfulParseResults: dataset.successfulParseResults,
|
|
6795
|
+
sourceFailures: dataset.sourceFailures,
|
|
6796
|
+
pricingOrigin,
|
|
6797
|
+
pricingWarning,
|
|
6798
|
+
activeEnvOverrides: dataset.readEnvVarOverrides(),
|
|
6799
|
+
timezone: dataset.normalizedInputs.timezone
|
|
6800
|
+
});
|
|
6801
|
+
return {
|
|
6802
|
+
rows: counterfactual.rows,
|
|
6803
|
+
diagnostics: {
|
|
6804
|
+
usage: usageDiagnostics,
|
|
6805
|
+
provider,
|
|
6806
|
+
baselineCostIncomplete: counterfactual.baselineCostIncomplete,
|
|
6807
|
+
candidatesWithMissingPricing: counterfactual.candidatesWithMissingPricing,
|
|
6808
|
+
warning: counterfactual.warning
|
|
6809
|
+
}
|
|
6810
|
+
};
|
|
6811
|
+
}
|
|
6812
|
+
|
|
6813
|
+
// src/cli/run-optimize-report.ts
|
|
6814
|
+
function validateOutputFormatOptions2(options) {
|
|
6815
|
+
if (options.markdown && options.json) {
|
|
6816
|
+
throw new Error("Choose either --markdown or --json, not both");
|
|
6817
|
+
}
|
|
6818
|
+
}
|
|
6819
|
+
function resolveReportFormat2(options) {
|
|
6820
|
+
if (options.json) {
|
|
6821
|
+
return "json";
|
|
6822
|
+
}
|
|
6823
|
+
if (options.markdown) {
|
|
6824
|
+
return "markdown";
|
|
6825
|
+
}
|
|
6826
|
+
return "terminal";
|
|
6827
|
+
}
|
|
6828
|
+
async function prepareOptimizeReport(granularity, options) {
|
|
6829
|
+
validateOutputFormatOptions2(options);
|
|
6830
|
+
const optimizeData = await buildOptimizeData(granularity, options);
|
|
6831
|
+
const format = resolveReportFormat2(options);
|
|
6832
|
+
return {
|
|
6833
|
+
format,
|
|
6834
|
+
diagnostics: optimizeData.diagnostics,
|
|
6835
|
+
candidateCount: optimizeData.rows.filter(
|
|
6836
|
+
(row) => row.rowType === "candidate" && row.periodKey === "ALL"
|
|
6837
|
+
).length,
|
|
6838
|
+
output: renderOptimizeReport(optimizeData, format, {
|
|
6839
|
+
granularity
|
|
6840
|
+
})
|
|
6841
|
+
};
|
|
6842
|
+
}
|
|
6843
|
+
async function runOptimizeReport(granularity, options) {
|
|
6844
|
+
const preparedReport = await prepareOptimizeReport(granularity, options);
|
|
6845
|
+
emitDiagnostics(preparedReport.diagnostics.usage, logger);
|
|
6846
|
+
emitEnvVarOverrides(preparedReport.diagnostics.usage.activeEnvOverrides, logger);
|
|
6847
|
+
logger.info(
|
|
6848
|
+
`Optimize provider scope: ${preparedReport.diagnostics.provider}; candidate(s): ${preparedReport.candidateCount}`
|
|
6849
|
+
);
|
|
6850
|
+
if (preparedReport.diagnostics.candidatesWithMissingPricing.length > 0) {
|
|
6851
|
+
logger.warn(
|
|
6852
|
+
`Missing pricing for candidate model(s): ${preparedReport.diagnostics.candidatesWithMissingPricing.join(", ")}`
|
|
6853
|
+
);
|
|
6854
|
+
}
|
|
6855
|
+
if (preparedReport.diagnostics.warning) {
|
|
6856
|
+
logger.warn(preparedReport.diagnostics.warning);
|
|
6857
|
+
}
|
|
6858
|
+
if (preparedReport.format === "terminal") {
|
|
6859
|
+
warnIfTerminalTableOverflows(preparedReport.output, (message) => {
|
|
6860
|
+
logger.warn(message);
|
|
6861
|
+
});
|
|
6862
|
+
}
|
|
6863
|
+
console.log(preparedReport.output);
|
|
6864
|
+
}
|
|
6865
|
+
|
|
6866
|
+
// src/render/markdown-table.ts
|
|
6867
|
+
import { markdownTable as markdownTable3 } from "markdown-table";
|
|
6868
|
+
var alignment = ["l", "l", "l", "r", "r", "r", "r", "r", "r", "r"];
|
|
6869
|
+
function toMarkdownSafeCell3(value) {
|
|
6870
|
+
return value.replace(/\r?\n/gu, "<br>");
|
|
6871
|
+
}
|
|
6092
6872
|
function renderMarkdownTable(rows, options = {}) {
|
|
6093
6873
|
const tableLayout = options.tableLayout ?? "compact";
|
|
6094
6874
|
const bodyRows = toUsageTableCells(rows, { layout: tableLayout }).map(
|
|
6095
|
-
(row) => row.map((cell) =>
|
|
6875
|
+
(row) => row.map((cell) => toMarkdownSafeCell3(cell))
|
|
6096
6876
|
);
|
|
6097
6877
|
const tableRows = [Array.from(usageTableHeaders), ...bodyRows];
|
|
6098
|
-
return
|
|
6878
|
+
return markdownTable3(tableRows, {
|
|
6099
6879
|
align: alignment
|
|
6100
6880
|
});
|
|
6101
6881
|
}
|
|
6102
6882
|
|
|
6103
6883
|
// src/render/render-usage-report.ts
|
|
6104
|
-
function
|
|
6884
|
+
function getReportTitle3(granularity) {
|
|
6105
6885
|
switch (granularity) {
|
|
6106
6886
|
case "daily":
|
|
6107
6887
|
return "Daily Token Usage Report";
|
|
@@ -6117,7 +6897,7 @@ function renderTerminalUsageReport(usageData, options) {
|
|
|
6117
6897
|
const tableLayout = options.tableLayout ?? "compact";
|
|
6118
6898
|
outputLines.push(
|
|
6119
6899
|
renderReportHeader({
|
|
6120
|
-
title:
|
|
6900
|
+
title: getReportTitle3(options.granularity),
|
|
6121
6901
|
useColor
|
|
6122
6902
|
})
|
|
6123
6903
|
);
|
|
@@ -6138,12 +6918,12 @@ function renderUsageReport(usageData, format, options) {
|
|
|
6138
6918
|
}
|
|
6139
6919
|
|
|
6140
6920
|
// src/cli/run-usage-report.ts
|
|
6141
|
-
function
|
|
6921
|
+
function validateOutputFormatOptions3(options) {
|
|
6142
6922
|
if (options.markdown && options.json) {
|
|
6143
6923
|
throw new Error("Choose either --markdown or --json, not both");
|
|
6144
6924
|
}
|
|
6145
6925
|
}
|
|
6146
|
-
function
|
|
6926
|
+
function resolveReportFormat3(options) {
|
|
6147
6927
|
if (options.json) {
|
|
6148
6928
|
return "json";
|
|
6149
6929
|
}
|
|
@@ -6156,9 +6936,9 @@ function resolveTableLayout(options) {
|
|
|
6156
6936
|
return options.perModelColumns ? "per_model_columns" : "compact";
|
|
6157
6937
|
}
|
|
6158
6938
|
async function prepareUsageReport(granularity, options) {
|
|
6159
|
-
|
|
6939
|
+
validateOutputFormatOptions3(options);
|
|
6160
6940
|
const usageData = await buildUsageData(granularity, options);
|
|
6161
|
-
const format =
|
|
6941
|
+
const format = resolveReportFormat3(options);
|
|
6162
6942
|
return {
|
|
6163
6943
|
format,
|
|
6164
6944
|
diagnostics: usageData.diagnostics,
|
|
@@ -6206,7 +6986,10 @@ function addSharedOptions(command, options = {}) {
|
|
|
6206
6986
|
`Filter by source id (repeatable or comma-separated, supported sources ${supportedSourcesSummary})`,
|
|
6207
6987
|
collectRepeatedOption,
|
|
6208
6988
|
[]
|
|
6209
|
-
).option("--since <YYYY-MM-DD>", "Inclusive start date filter").option("--until <YYYY-MM-DD>", "Inclusive end date filter").option("--timezone <iana>", "Timezone for bucketing", defaultTimezone).option(
|
|
6989
|
+
).option("--since <YYYY-MM-DD>", "Inclusive start date filter").option("--until <YYYY-MM-DD>", "Inclusive end date filter").option("--timezone <iana>", "Timezone for bucketing", defaultTimezone).option(
|
|
6990
|
+
"--provider <name>",
|
|
6991
|
+
"Billing-provider filter (substring match, optional; e.g. openai, anthropic, google)"
|
|
6992
|
+
).option(
|
|
6210
6993
|
"--model <name>",
|
|
6211
6994
|
"Filter by model (repeatable/comma-separated; exact when exact match exists after source/provider/date filters, otherwise substring)",
|
|
6212
6995
|
collectRepeatedOption,
|
|
@@ -6254,6 +7037,18 @@ function createEfficiencyCommand() {
|
|
|
6254
7037
|
});
|
|
6255
7038
|
return command;
|
|
6256
7039
|
}
|
|
7040
|
+
function createOptimizeCommand() {
|
|
7041
|
+
const command = new Command("optimize");
|
|
7042
|
+
addSharedOptions(command, { includePerModelColumns: false }).argument("<granularity>", "Granularity: daily | weekly | monthly", parseGranularityArgument).option(
|
|
7043
|
+
"--candidate-model <name>",
|
|
7044
|
+
"Candidate model for counterfactual pricing (repeatable or comma-separated)",
|
|
7045
|
+
collectRepeatedOption,
|
|
7046
|
+
[]
|
|
7047
|
+
).option("--top <n>", "Show only the top N cheapest candidates (positive integer)").description("Show counterfactual pricing report for candidate model(s)").action(async (granularity, options) => {
|
|
7048
|
+
await runOptimizeReport(granularity, options);
|
|
7049
|
+
});
|
|
7050
|
+
return command;
|
|
7051
|
+
}
|
|
6257
7052
|
function rootDescription() {
|
|
6258
7053
|
const supportedSourceIds = getSupportedSourceIds();
|
|
6259
7054
|
const allowedSourcesLabel = getAllowedSourcesLabel(supportedSourceIds);
|
|
@@ -6273,12 +7068,13 @@ function rootDescription() {
|
|
|
6273
7068
|
" $ llm-usage daily --source-dir pi=/tmp/pi-sessions --source-dir gemini=/tmp/.gemini --source-dir droid=/tmp/droid-sessions",
|
|
6274
7069
|
" $ llm-usage daily --pi-dir /tmp/pi-sessions --gemini-dir /tmp/.gemini --droid-dir /tmp/droid-sessions",
|
|
6275
7070
|
" $ llm-usage efficiency weekly --repo-dir /path/to/repo --json",
|
|
7071
|
+
" $ llm-usage optimize monthly --provider openai --candidate-model gpt-4.1 --candidate-model gpt-5-codex --json",
|
|
6276
7072
|
" $ npx --yes llm-usage-metrics@latest daily"
|
|
6277
7073
|
].join("\n");
|
|
6278
7074
|
}
|
|
6279
7075
|
function createCli(options = {}) {
|
|
6280
7076
|
const program = new Command();
|
|
6281
|
-
program.name("llm-usage").description(rootDescription()).version(options.version ?? "0.0.0").showHelpAfterError().addCommand(createCommand("daily")).addCommand(createCommand("weekly")).addCommand(createCommand("monthly")).addCommand(createEfficiencyCommand());
|
|
7077
|
+
program.name("llm-usage").description(rootDescription()).version(options.version ?? "0.0.0").showHelpAfterError().addCommand(createCommand("daily")).addCommand(createCommand("weekly")).addCommand(createCommand("monthly")).addCommand(createEfficiencyCommand()).addCommand(createOptimizeCommand());
|
|
6282
7078
|
return program;
|
|
6283
7079
|
}
|
|
6284
7080
|
|