@trazum/cli 1.26.0 → 1.28.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/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +13 -3
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/es.d.ts.map +1 -1
- package/dist/i18n/es.js +13 -3
- package/dist/i18n/es.js.map +1 -1
- package/dist/i18n/types.d.ts +26 -0
- package/dist/i18n/types.d.ts.map +1 -1
- package/dist/index.js +79 -4
- package/dist/index.js.map +1 -1
- package/dist/markdown.d.ts +7 -1
- package/dist/markdown.d.ts.map +1 -1
- package/dist/markdown.js +52 -1
- package/dist/markdown.js.map +1 -1
- package/package.json +2 -2
- package/src/i18n/en.ts +19 -3
- package/src/i18n/es.ts +19 -3
- package/src/i18n/types.ts +41 -0
- package/src/index.ts +93 -3
- package/src/markdown.ts +69 -2
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { readdir, readFile, stat, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { join, resolve as resolvePath } from 'node:path';
|
|
4
4
|
import { gunzipSync } from 'node:zlib';
|
|
5
|
-
import { applyRewrites, BASELINE_FILENAME, BASELINE_VERSION, breaches, cacheableMinimum, analyzeCachePrefix, billLevers, cacheEconomics, cacheHitRate, comparePrompts, compareToBaseline, computeSavings, countTokensAnthropic, DEFAULT_USAGE, detectFromSource, driversBetween, estimateTokens, evaluate, extractPrompts, findExamples, formatBaseline, formatSignedUsd, formatUsd, getMessages, getModel, hasMarker, LOCALES, MAX_BASELINE_BYTES, moneyIsComparable, mostSpecificMatch, nearestName, optimize, parseBaseline, PHRASE_LANGUAGES, plannedCalls, profilePrompt, profileToCsv, profileUsage, promptId, providerFromEnv, pruneExamples, refineWithLlm, rejectionText, reorderForCache, repriceProfile, reviewAgeDays, reviewExamples, RULES, sharedPrefixes, sharesOf, SOURCE_EXTENSIONS, suggestRewrites, toOtlpMetrics, toPromptfoo, TTL_1H_MS, UNLABELLED, withExactTokenCounts, } from '@trazum/core';
|
|
5
|
+
import { applyRewrites, BASELINE_FILENAME, BASELINE_VERSION, breaches, cacheableMinimum, analyzeCachePrefix, billLevers, cacheEconomics, cacheHitRate, contextPressure, comparePrompts, compareToBaseline, computeSavings, countTokensAnthropic, DEFAULT_USAGE, detectFromSource, driversBetween, estimateTokens, evaluate, extractPrompts, findExamples, formatBaseline, formatSignedUsd, formatUsd, getMessages, getModel, hasMarker, LOCALES, MAX_BASELINE_BYTES, moneyIsComparable, mostSpecificMatch, nearestName, optimize, parseBaseline, PHRASE_LANGUAGES, plannedCalls, profilePrompt, profileToCsv, profileUsage, promptId, providerFromEnv, pruneExamples, refineWithLlm, rejectionText, reorderForCache, repriceProfile, reviewAgeDays, reviewExamples, RULES, sharedPrefixes, sharesOf, SOURCE_EXTENSIONS, suggestRewrites, toOtlpMetrics, toPromptfoo, TTL_1H_MS, UNLABELLED, withExactTokenCounts, } from '@trazum/core';
|
|
6
6
|
import { cacheDir, cacheStats, cachingProvider, clearCache } from './suggest-cache.js';
|
|
7
7
|
import { dayOf, formatGap, median, spanDays } from './time.js';
|
|
8
8
|
// Everything that reads the filesystem, on its own entry point so the web
|
|
@@ -1660,6 +1660,12 @@ async function commandProfile(args, config, pricing, t) {
|
|
|
1660
1660
|
* got a report with no answer in it, and has no way to tell a typo from a
|
|
1661
1661
|
* model this comparison had nothing to say about.
|
|
1662
1662
|
*/
|
|
1663
|
+
/**
|
|
1664
|
+
* The largest call against each model's window, computed once here so the
|
|
1665
|
+
* terminal, the JSON and the markdown state the same ratio — the
|
|
1666
|
+
* denominator lives in the (possibly overlaid) catalogue.
|
|
1667
|
+
*/
|
|
1668
|
+
const pressures = contextPressure(report, pricing);
|
|
1663
1669
|
const whatIfModel = stringFlag(args, 'what-if');
|
|
1664
1670
|
const whatIf = whatIfModel !== undefined ? repriceProfile(report, whatIfModel, pricing) : null;
|
|
1665
1671
|
if (whatIfModel !== undefined && whatIf === null) {
|
|
@@ -1832,8 +1838,12 @@ async function commandProfile(args, config, pricing, t) {
|
|
|
1832
1838
|
* sound in both directions, the pass is a floor, and the pass message
|
|
1833
1839
|
* says so when the span does not start and end on a day boundary.
|
|
1834
1840
|
*/
|
|
1835
|
-
if (typeof args.flags.get('max-day-usd') === 'string') {
|
|
1836
|
-
|
|
1841
|
+
if (typeof args.flags.get('max-day-usd') === 'string' || config.spend?.maxDayUsd !== undefined) {
|
|
1842
|
+
// The flag beats the config, like every gate here: the config is the
|
|
1843
|
+
// repository's standing policy, the flag is this invocation's word.
|
|
1844
|
+
const maxDay = typeof args.flags.get('max-day-usd') === 'string'
|
|
1845
|
+
? numberFlag(args, 'max-day-usd', 0, t)
|
|
1846
|
+
: config.spend.maxDayUsd;
|
|
1837
1847
|
if (report.spendByDay.length === 0) {
|
|
1838
1848
|
console.error(c.red(t.profile.maxDayNoClock()));
|
|
1839
1849
|
process.exitCode = 1;
|
|
@@ -1899,6 +1909,7 @@ async function commandProfile(args, config, pricing, t) {
|
|
|
1899
1909
|
// handed over, so the summary in a pull request cannot disagree
|
|
1900
1910
|
// with the terminal about what a move would cost.
|
|
1901
1911
|
...(whatIf !== null ? { whatIf } : {}),
|
|
1912
|
+
pressure: pressures,
|
|
1902
1913
|
// The comparison, when there was one — the same figures and the same
|
|
1903
1914
|
// drivers the terminal printed, never re-derived here.
|
|
1904
1915
|
...(previous !== null
|
|
@@ -1937,7 +1948,7 @@ async function commandProfile(args, config, pricing, t) {
|
|
|
1937
1948
|
* somebody sums wrong.
|
|
1938
1949
|
*/
|
|
1939
1950
|
const shape = stringFlag(args, 'csv-shape') ?? 'slice';
|
|
1940
|
-
if (shape !== 'slice' && shape !== 'day' && shape !== 'hour') {
|
|
1951
|
+
if (shape !== 'slice' && shape !== 'day' && shape !== 'hour' && shape !== 'model-day') {
|
|
1941
1952
|
throw new Error(t.profile.badCsvShape(shape));
|
|
1942
1953
|
}
|
|
1943
1954
|
await writeFile(csvOut, profileToCsv(report, { unlabelled: t.profile.unlabelled(), shape }), 'utf8');
|
|
@@ -1994,6 +2005,10 @@ async function commandProfile(args, config, pricing, t) {
|
|
|
1994
2005
|
},
|
|
1995
2006
|
}
|
|
1996
2007
|
: {}),
|
|
2008
|
+
// How close each slice's largest call is to its model's window —
|
|
2009
|
+
// derived like `cache` and `levers`, so a dashboard need not
|
|
2010
|
+
// re-derive a ratio whose denominator lives in the catalogue.
|
|
2011
|
+
contextPressure: pressures,
|
|
1997
2012
|
// Present only when --what-if was passed. `sameTokensAssumed` rides
|
|
1998
2013
|
// along inside it so a consumer cannot print the dollar figure
|
|
1999
2014
|
// without the caveat being in the same object.
|
|
@@ -2697,6 +2712,52 @@ async function commandProfile(args, config, pricing, t) {
|
|
|
2697
2712
|
}
|
|
2698
2713
|
}
|
|
2699
2714
|
}
|
|
2715
|
+
/**
|
|
2716
|
+
* How close the largest call is to the model's ceiling.
|
|
2717
|
+
*
|
|
2718
|
+
* The failure a bill cannot show: input grows turn by turn or document by
|
|
2719
|
+
* document, costs nothing extra to grow, and then one call crosses the
|
|
2720
|
+
* context window and the API refuses it. Loud from 85% — close enough
|
|
2721
|
+
* that the next retrieval bump or long conversation plausibly crosses —
|
|
2722
|
+
* and quiet above half, so the reader sees it coming either way. No
|
|
2723
|
+
* prediction of *when*: a straight line through two points is a guess
|
|
2724
|
+
* wearing arithmetic's clothes.
|
|
2725
|
+
*/
|
|
2726
|
+
{
|
|
2727
|
+
if (pressures.length > 0) {
|
|
2728
|
+
console.log();
|
|
2729
|
+
console.log(c.bold(t.profile.pressureHeading()));
|
|
2730
|
+
for (const row of pressures.slice(0, 3)) {
|
|
2731
|
+
const label = row.label === UNLABELLED ? t.profile.unlabelled() : row.label;
|
|
2732
|
+
const line = t.profile.pressureLine(label, row.modelName, n(row.maxCallInputTokens), n(row.contextWindow), pct(row.share));
|
|
2733
|
+
console.log();
|
|
2734
|
+
if (row.share >= 0.85) {
|
|
2735
|
+
console.log(` ${c.yellow('!')} ${c.bold(wrap(line, 74, ' '))}`);
|
|
2736
|
+
console.log(` ${c.dim(wrap(t.profile.pressureAdvice(), 74, ' '))}`);
|
|
2737
|
+
}
|
|
2738
|
+
else {
|
|
2739
|
+
console.log(` ${c.dim(wrap(line, 74, ' '))}`);
|
|
2740
|
+
}
|
|
2741
|
+
}
|
|
2742
|
+
}
|
|
2743
|
+
}
|
|
2744
|
+
/**
|
|
2745
|
+
* The mix moving inside one log — the drift `--against` needs a second log
|
|
2746
|
+
* to see. Spoken only past fifteen points, a presentation threshold stated
|
|
2747
|
+
* in the copy; the data states exact shares and the JSON carries them all.
|
|
2748
|
+
*/
|
|
2749
|
+
if (report.modelMixDrift !== null) {
|
|
2750
|
+
const moved = report.modelMixDrift.models.filter((m) => Math.abs(m.lastShare - m.firstShare) >= 0.15);
|
|
2751
|
+
if (moved.length > 0) {
|
|
2752
|
+
console.log();
|
|
2753
|
+
console.log(c.bold(t.profile.mixDriftHeading()));
|
|
2754
|
+
for (const m of moved.slice(0, 3)) {
|
|
2755
|
+
console.log();
|
|
2756
|
+
console.log(` ${c.yellow('!')} ${c.bold(wrap(t.profile.mixDriftLine(m.model, pct(m.firstShare), pct(m.lastShare), n(report.modelMixDrift.firstDays), n(report.modelMixDrift.lastDays), formatUsd(m.lastUsd)), 74, ' '))}`);
|
|
2757
|
+
}
|
|
2758
|
+
console.log(` ${c.dim(wrap(t.profile.mixDriftNote(), 74, ' '))}`);
|
|
2759
|
+
}
|
|
2760
|
+
}
|
|
2700
2761
|
/**
|
|
2701
2762
|
* The same request, sent again a moment later.
|
|
2702
2763
|
*
|
|
@@ -2761,6 +2822,20 @@ async function commandProfile(args, config, pricing, t) {
|
|
|
2761
2822
|
if (ceiling !== undefined) {
|
|
2762
2823
|
console.log(` ${c.dim(wrap(t.profile.truncatedCeiling(n(ceiling.p95WithinTokens)), 74, ' '))}`);
|
|
2763
2824
|
}
|
|
2825
|
+
/**
|
|
2826
|
+
* The "billed again" half, measured. The sentence above has always said
|
|
2827
|
+
* truncated answers are frequently retried; this is the count and the
|
|
2828
|
+
* money, when the log can carry it — a pattern, never a certainty, and
|
|
2829
|
+
* attributed to the truncated call's slice, where the ceiling that
|
|
2830
|
+
* caused it lives.
|
|
2831
|
+
*/
|
|
2832
|
+
for (const row of report.truncationRetries.slice(0, 3)) {
|
|
2833
|
+
const name = row.label === UNLABELLED ? t.profile.unlabelled() : row.label;
|
|
2834
|
+
console.log(` ${c.yellow('!')} ${c.bold(wrap(t.profile.truncationRetryLine(name, row.modelName, n(row.retried), n(row.truncatedCalls), n(Math.round(row.withinMs / 1000)), formatUsd(row.wastedUsd), formatUsd(row.retryUsd)), 74, ' '))}`);
|
|
2835
|
+
}
|
|
2836
|
+
if (report.truncationRetries.length > 0) {
|
|
2837
|
+
console.log(` ${c.dim(wrap(t.profile.truncationRetryNote(), 74, ' '))}`);
|
|
2838
|
+
}
|
|
2764
2839
|
}
|
|
2765
2840
|
else if (report.total.stopReasonCalls === 0) {
|
|
2766
2841
|
console.log();
|