@trazum/cli 1.45.0 → 1.46.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 +69 -0
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/es.d.ts.map +1 -1
- package/dist/i18n/es.js +69 -0
- package/dist/i18n/es.js.map +1 -1
- package/dist/i18n/types.d.ts +52 -0
- package/dist/i18n/types.d.ts.map +1 -1
- package/dist/index.js +409 -5
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/i18n/en.ts +95 -0
- package/src/i18n/es.ts +95 -0
- package/src/i18n/types.ts +57 -0
- package/src/index.ts +462 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trazum/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.46.0",
|
|
4
4
|
"description": "Trazum CLI: find where your LLM bill goes, price every finding per month, and enforce token budgets in CI.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "David Mu\u00f1oz Rey",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"prepublishOnly": "npm run build && npm test"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@trazum/core": "1.
|
|
40
|
+
"@trazum/core": "1.46.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/node": "^26.2.0",
|
package/src/i18n/en.ts
CHANGED
|
@@ -33,6 +33,7 @@ export const en: CliMessages = {
|
|
|
33
33
|
help: (d, bold) => `${bold('trazum')} — cut the cost of your prompts without losing what they ask for.
|
|
34
34
|
|
|
35
35
|
${bold('USAGE')}
|
|
36
|
+
trazum init [dir] [--dry-run | --yes]
|
|
36
37
|
trazum optimize <file|-> [options]
|
|
37
38
|
trazum check <file|dir|-> --max-tokens <n> [options]
|
|
38
39
|
trazum baseline [dir] [options]
|
|
@@ -56,6 +57,22 @@ ${bold('USAGE')}
|
|
|
56
57
|
trazum models
|
|
57
58
|
trazum rules
|
|
58
59
|
|
|
60
|
+
${bold('OPTIONS FOR init')}
|
|
61
|
+
--dry-run Print the config it would write and write nothing.
|
|
62
|
+
--yes Replace a trazum.config.json that is already there.
|
|
63
|
+
Without it an existing config is left alone.
|
|
64
|
+
--json The proposal as data, including every key it
|
|
65
|
+
declined and why. Writes nothing.
|
|
66
|
+
|
|
67
|
+
The first five minutes: finds your prompts, reads your code for which provider
|
|
68
|
+
it calls, finds a usage log or a credential for one, and writes a config out of
|
|
69
|
+
what it can actually justify. Then it prints the single most valuable thing it
|
|
70
|
+
found, arithmetic first.
|
|
71
|
+
|
|
72
|
+
It never invents a threshold. A budget is a policy, so init hands you the
|
|
73
|
+
measured figure and leaves the limit to you — a generated config full of
|
|
74
|
+
guessed numbers is one nobody trusts.
|
|
75
|
+
|
|
59
76
|
${bold('OPTIONS FOR prune')}
|
|
60
77
|
--cases <file> One input per line, or a JSON array. Required.
|
|
61
78
|
--yes Actually spend the calls. Without it the estimate is
|
|
@@ -809,6 +826,84 @@ ${bold('EXAMPLES')}
|
|
|
809
826
|
wroteTo: (path) => `Optimised prompt written to ${path}`,
|
|
810
827
|
},
|
|
811
828
|
|
|
829
|
+
init: {
|
|
830
|
+
heading: () => 'What is here',
|
|
831
|
+
host: (name) => `Running inside ${name}.`,
|
|
832
|
+
prompts: (count) =>
|
|
833
|
+
`${count} prompt ${count === 1 ? 'file' : 'files'} found.`,
|
|
834
|
+
noPrompts: () =>
|
|
835
|
+
'No prompt files found. Directory mode reads .txt, .md, .prompt and .tmpl by default — set "extensions" if yours are named otherwise.',
|
|
836
|
+
sourcesTruncated: (cap) =>
|
|
837
|
+
`Stopped after ${cap} source files, so a provider named further down was not seen.`,
|
|
838
|
+
usageFound: (kind, where) =>
|
|
839
|
+
kind === 'connector-credential'
|
|
840
|
+
? `Usage can be pulled: a credential is in ${where}. Run trazum connect.`
|
|
841
|
+
: kind === 'store'
|
|
842
|
+
? `A local store of past measurements is in ${where}.`
|
|
843
|
+
: `Usage log found: ${where}.`,
|
|
844
|
+
noUsage: () =>
|
|
845
|
+
'No usage found. Point trazum at a log (trazum profile <log.jsonl>) or pull one (trazum connect anthropic) — every money figure in this tool comes from one.',
|
|
846
|
+
usageUnreadable: (where, because) =>
|
|
847
|
+
`${where} is there and could not be read: ${because}. That is a different problem from having no usage, and it is the one to fix first.`,
|
|
848
|
+
|
|
849
|
+
configHeading: () => 'What the config would say',
|
|
850
|
+
nothingJustified: () =>
|
|
851
|
+
'Nothing. Every key below needs evidence this run did not find, and a guessed config is worse than none.',
|
|
852
|
+
whyLocale: (locale) => `your environment asks for ${locale}`,
|
|
853
|
+
whyExtensions: (extensions, files) => `${files} prompt files use ${extensions}`,
|
|
854
|
+
whyModelMeasured: (model, sharePct) => `${sharePct}% of the measured bill went to ${model}`,
|
|
855
|
+
whyModelSource: (model, file, line) => `${file}:${line} names ${model}`,
|
|
856
|
+
whyCalls: (perMonth, calls, days) =>
|
|
857
|
+
`${calls} calls over ${days} days, stated as ${perMonth} a month`,
|
|
858
|
+
whyOutput: (average, outputTokens, calls) =>
|
|
859
|
+
`${outputTokens} output tokens over ${calls} calls averages ${average}`,
|
|
860
|
+
whyCache: (rate, cacheReadTokens, inputTokens) =>
|
|
861
|
+
`${cacheReadTokens} cached against ${inputTokens} fresh input is a hit rate of ${rate}`,
|
|
862
|
+
|
|
863
|
+
noModelEvidence: () => 'nothing measured or written in the source names one model',
|
|
864
|
+
modelConflict: (files) => `these files name more than one provider: ${files}`,
|
|
865
|
+
modelProviderOnly: (provider, file) =>
|
|
866
|
+
`${file} names ${provider} and no model — a provider default would read as your decision six weeks from now`,
|
|
867
|
+
nothingMeasured: () => 'nothing has been measured yet',
|
|
868
|
+
windowTooShort: (days, minimum) =>
|
|
869
|
+
`${days} days measured; ${minimum} are needed before that is a monthly rate rather than a forecast`,
|
|
870
|
+
undatedCalls: (undated, calls) =>
|
|
871
|
+
`${undated} of ${calls} calls carry no timestamp, so they cannot be placed inside the window a rate would divide by`,
|
|
872
|
+
cacheNotRecorded: () =>
|
|
873
|
+
'this log has no cache columns at all, which is not the same as a hit rate of zero',
|
|
874
|
+
batchOnlyYouKnow: () =>
|
|
875
|
+
'whether the work can wait for a batch window is a product decision, and no log records it',
|
|
876
|
+
labelsUnprovable: (labels) =>
|
|
877
|
+
`${labels} ${labels === 1 ? 'label' : 'labels'} in the log, and nothing here proves which prompt file sends which`,
|
|
878
|
+
budgetIsPolicy: () => 'a budget is a policy, and there is no measured figure to write one against yet',
|
|
879
|
+
budgetIsPolicyMeasured: (usd, days) =>
|
|
880
|
+
`a budget is a policy, so it is yours to set — the measured figure is $${usd} over ${days} days`,
|
|
881
|
+
|
|
882
|
+
findingHeading: () => 'The most valuable thing found',
|
|
883
|
+
noFinding: (why) =>
|
|
884
|
+
why === 'nothing-measured'
|
|
885
|
+
? 'Nothing, because nothing has been measured. Every figure this tool prints as money comes from a usage log.'
|
|
886
|
+
: why === 'nothing-could-be-priced'
|
|
887
|
+
? 'Nothing: the log was read, and no model in it is in the price catalogue. Run trazum models to see what is priced.'
|
|
888
|
+
: 'Nothing worth a line: no single slice of this bill can be moved by more than one per cent of it.',
|
|
889
|
+
findingCalls: (calls, label, model, days) =>
|
|
890
|
+
`${calls} calls labelled "${label}" went to ${model} over ${days} days.`,
|
|
891
|
+
findingSpent: (usd) => `They cost $${usd}.`,
|
|
892
|
+
findingRoute: (model) => `The same work fits ${model}, which is cheaper per token.`,
|
|
893
|
+
findingBatch: () => 'The Batch API halves both halves of the bill, for work that can wait.',
|
|
894
|
+
findingTotal: (usd, days) => `Together: $${usd} over the same ${days} days.`,
|
|
895
|
+
findingNext: () => 'trazum plan <your log> ranks every action, not just this one.',
|
|
896
|
+
|
|
897
|
+
wouldOverwrite: (keys) => `This replaces keys you already set: ${keys}. Pass --yes to write anyway.`,
|
|
898
|
+
nothingToWrite: () => 'No config written: nothing above could be justified from what is here.',
|
|
899
|
+
wouldWrite: (path) => `Would write ${path}:`,
|
|
900
|
+
wrote: (path) => `Written to ${path}.`,
|
|
901
|
+
existingRefused: (path) =>
|
|
902
|
+
`${path} already exists and was left alone. Pass --dry-run to see what would go in it, or --yes to replace it.`,
|
|
903
|
+
existingUnparseable: (path) =>
|
|
904
|
+
`${path} exists and could not be parsed, so nothing was written over it. Fix or move it first.`,
|
|
905
|
+
},
|
|
906
|
+
|
|
812
907
|
where: {
|
|
813
908
|
hostHeading: () => 'Running inside',
|
|
814
909
|
subscription: (host) =>
|
package/src/i18n/es.ts
CHANGED
|
@@ -20,6 +20,7 @@ export const es: CliMessages = {
|
|
|
20
20
|
help: (d, bold) => `${bold('trazum')} — reduce el coste de tus prompts sin perder lo que piden.
|
|
21
21
|
|
|
22
22
|
${bold('USO')}
|
|
23
|
+
trazum init [dir] [--dry-run | --yes]
|
|
23
24
|
trazum optimize <fichero|-> [opciones]
|
|
24
25
|
trazum check <fichero|dir|-> --max-tokens <n> [opciones]
|
|
25
26
|
trazum baseline [dir] [opciones]
|
|
@@ -43,6 +44,22 @@ ${bold('USO')}
|
|
|
43
44
|
trazum models
|
|
44
45
|
trazum rules
|
|
45
46
|
|
|
47
|
+
${bold('OPCIONES DE init')}
|
|
48
|
+
--dry-run Imprime la configuración que escribiría y no escribe nada.
|
|
49
|
+
--yes Reemplaza un trazum.config.json que ya exista. Sin
|
|
50
|
+
esto, una configuración existente se deja intacta.
|
|
51
|
+
--json La propuesta como datos, incluida cada clave que
|
|
52
|
+
descartó y por qué. No escribe nada.
|
|
53
|
+
|
|
54
|
+
Los primeros cinco minutos: encuentra tus prompts, lee tu código para saber a
|
|
55
|
+
qué proveedor llama, busca un registro de consumo o una credencial para uno, y
|
|
56
|
+
escribe una configuración con lo que puede justificar de verdad. Después imprime
|
|
57
|
+
lo más valioso que encontró, con la aritmética delante.
|
|
58
|
+
|
|
59
|
+
Nunca se inventa un umbral. Un presupuesto es una política, así que init te da la
|
|
60
|
+
cifra medida y te deja el límite a ti — una configuración generada llena de
|
|
61
|
+
números adivinados no se la cree nadie.
|
|
62
|
+
|
|
46
63
|
${bold('OPCIONES DE prune')}
|
|
47
64
|
--cases <fichero> Una entrada por línea, o un array JSON. Obligatorio.
|
|
48
65
|
--yes Gasta las llamadas de verdad. Sin él se imprime la
|
|
@@ -842,6 +859,84 @@ ${bold('EJEMPLOS')}
|
|
|
842
859
|
wroteTo: (path) => `Prompt optimizado escrito en ${path}`,
|
|
843
860
|
},
|
|
844
861
|
|
|
862
|
+
init: {
|
|
863
|
+
heading: () => 'Lo que hay aquí',
|
|
864
|
+
host: (name) => `Ejecutándose dentro de ${name}.`,
|
|
865
|
+
prompts: (count) =>
|
|
866
|
+
`${count} ${count === 1 ? 'archivo de prompt encontrado' : 'archivos de prompt encontrados'}.`,
|
|
867
|
+
noPrompts: () =>
|
|
868
|
+
'No se encontraron prompts. El modo directorio lee .txt, .md, .prompt y .tmpl por defecto — define "extensions" si los tuyos se llaman de otra forma.',
|
|
869
|
+
sourcesTruncated: (cap) =>
|
|
870
|
+
`Se paró tras ${cap} archivos de código, así que un proveedor nombrado más abajo no se vio.`,
|
|
871
|
+
usageFound: (kind, where) =>
|
|
872
|
+
kind === 'connector-credential'
|
|
873
|
+
? `El consumo se puede descargar: hay una credencial en ${where}. Ejecuta trazum connect.`
|
|
874
|
+
: kind === 'store'
|
|
875
|
+
? `Hay un almacén local de mediciones pasadas en ${where}.`
|
|
876
|
+
: `Registro de consumo encontrado: ${where}.`,
|
|
877
|
+
noUsage: () =>
|
|
878
|
+
'No se encontró consumo. Apunta trazum a un registro (trazum profile <log.jsonl>) o descárgalo (trazum connect anthropic) — toda cifra en dinero de esta herramienta sale de uno.',
|
|
879
|
+
usageUnreadable: (where, because) =>
|
|
880
|
+
`${where} está ahí y no se pudo leer: ${because}. Ese es un problema distinto de no tener consumo, y es el primero que hay que arreglar.`,
|
|
881
|
+
|
|
882
|
+
configHeading: () => 'Lo que diría la configuración',
|
|
883
|
+
nothingJustified: () =>
|
|
884
|
+
'Nada. Cada clave de abajo necesita pruebas que esta ejecución no encontró, y una configuración adivinada es peor que ninguna.',
|
|
885
|
+
whyLocale: (locale) => `tu entorno pide ${locale}`,
|
|
886
|
+
whyExtensions: (extensions, files) => `${files} prompts usan ${extensions}`,
|
|
887
|
+
whyModelMeasured: (model, sharePct) => `el ${sharePct}% de la factura medida fue a ${model}`,
|
|
888
|
+
whyModelSource: (model, file, line) => `${file}:${line} nombra ${model}`,
|
|
889
|
+
whyCalls: (perMonth, calls, days) =>
|
|
890
|
+
`${calls} llamadas en ${days} días, expresadas como ${perMonth} al mes`,
|
|
891
|
+
whyOutput: (average, outputTokens, calls) =>
|
|
892
|
+
`${outputTokens} tokens de salida en ${calls} llamadas dan una media de ${average}`,
|
|
893
|
+
whyCache: (rate, cacheReadTokens, inputTokens) =>
|
|
894
|
+
`${cacheReadTokens} en caché frente a ${inputTokens} de entrada nueva dan una tasa de ${rate}`,
|
|
895
|
+
|
|
896
|
+
noModelEvidence: () => 'nada medido ni escrito en el código nombra un único modelo',
|
|
897
|
+
modelConflict: (files) => `estos archivos nombran más de un proveedor: ${files}`,
|
|
898
|
+
modelProviderOnly: (provider, file) =>
|
|
899
|
+
`${file} nombra ${provider} y ningún modelo — el modelo por defecto del proveedor se leería como decisión tuya dentro de seis semanas`,
|
|
900
|
+
nothingMeasured: () => 'todavía no se ha medido nada',
|
|
901
|
+
windowTooShort: (days, minimum) =>
|
|
902
|
+
`${days} días medidos; hacen falta ${minimum} antes de que eso sea un ritmo mensual y no un pronóstico`,
|
|
903
|
+
undatedCalls: (undated, calls) =>
|
|
904
|
+
`${undated} de ${calls} llamadas no llevan fecha, así que no se pueden situar dentro de la ventana por la que dividiría un ritmo`,
|
|
905
|
+
cacheNotRecorded: () =>
|
|
906
|
+
'este registro no tiene columnas de caché en absoluto, que no es lo mismo que una tasa de cero',
|
|
907
|
+
batchOnlyYouKnow: () =>
|
|
908
|
+
'si el trabajo puede esperar a una ventana de lote es una decisión de producto, y ningún registro la anota',
|
|
909
|
+
labelsUnprovable: (labels) =>
|
|
910
|
+
`${labels} ${labels === 1 ? 'etiqueta' : 'etiquetas'} en el registro, y nada aquí demuestra qué prompt envía cuál`,
|
|
911
|
+
budgetIsPolicy: () => 'un presupuesto es una política, y todavía no hay cifra medida contra la que escribir una',
|
|
912
|
+
budgetIsPolicyMeasured: (usd, days) =>
|
|
913
|
+
`un presupuesto es una política, así que es tuyo — la cifra medida es $${usd} en ${days} días`,
|
|
914
|
+
|
|
915
|
+
findingHeading: () => 'Lo más valioso encontrado',
|
|
916
|
+
noFinding: (why) =>
|
|
917
|
+
why === 'nothing-measured'
|
|
918
|
+
? 'Nada, porque no se ha medido nada. Toda cifra que esta herramienta imprime como dinero sale de un registro de consumo.'
|
|
919
|
+
: why === 'nothing-could-be-priced'
|
|
920
|
+
? 'Nada: el registro se leyó y ningún modelo suyo está en el catálogo de precios. Ejecuta trazum models para ver cuáles lo están.'
|
|
921
|
+
: 'Nada que merezca una línea: ninguna porción de esta factura se puede mover más de un uno por ciento del total.',
|
|
922
|
+
findingCalls: (calls, label, model, days) =>
|
|
923
|
+
`${calls} llamadas con la etiqueta "${label}" fueron a ${model} en ${days} días.`,
|
|
924
|
+
findingSpent: (usd) => `Costaron $${usd}.`,
|
|
925
|
+
findingRoute: (model) => `El mismo trabajo cabe en ${model}, más barato por token.`,
|
|
926
|
+
findingBatch: () => 'La API de lotes reduce a la mitad ambas mitades de la factura, para trabajo que puede esperar.',
|
|
927
|
+
findingTotal: (usd, days) => `Juntas: $${usd} en esos mismos ${days} días.`,
|
|
928
|
+
findingNext: () => 'trazum plan <tu registro> ordena todas las acciones, no solo esta.',
|
|
929
|
+
|
|
930
|
+
wouldOverwrite: (keys) => `Esto reemplaza claves que ya tenías: ${keys}. Pasa --yes para escribir igualmente.`,
|
|
931
|
+
nothingToWrite: () => 'No se escribió configuración: nada de lo anterior se pudo justificar con lo que hay aquí.',
|
|
932
|
+
wouldWrite: (path) => `Escribiría ${path}:`,
|
|
933
|
+
wrote: (path) => `Escrito en ${path}.`,
|
|
934
|
+
existingRefused: (path) =>
|
|
935
|
+
`${path} ya existe y se dejó intacto. Pasa --dry-run para ver qué iría dentro, o --yes para reemplazarlo.`,
|
|
936
|
+
existingUnparseable: (path) =>
|
|
937
|
+
`${path} existe y no se pudo interpretar, así que no se escribió nada encima. Arréglalo o muévelo primero.`,
|
|
938
|
+
},
|
|
939
|
+
|
|
845
940
|
where: {
|
|
846
941
|
hostHeading: () => 'Ejecutándose dentro de',
|
|
847
942
|
subscription: (host) =>
|
package/src/i18n/types.ts
CHANGED
|
@@ -189,6 +189,63 @@ export interface CliMessages {
|
|
|
189
189
|
wroteTo(path: string): string;
|
|
190
190
|
};
|
|
191
191
|
|
|
192
|
+
/**
|
|
193
|
+
* The first run.
|
|
194
|
+
*
|
|
195
|
+
* Every string here is either something that was *found* or something that
|
|
196
|
+
* was *declined with what would settle it*. There is deliberately no
|
|
197
|
+
* congratulation copy and no next-steps list: a first run that celebrates
|
|
198
|
+
* itself before showing a number is the shape people have learned to skip.
|
|
199
|
+
*/
|
|
200
|
+
init: {
|
|
201
|
+
heading(): string;
|
|
202
|
+
host(name: string): string;
|
|
203
|
+
prompts(count: number): string;
|
|
204
|
+
noPrompts(): string;
|
|
205
|
+
sourcesTruncated(cap: number): string;
|
|
206
|
+
usageFound(kind: string, where: string): string;
|
|
207
|
+
noUsage(): string;
|
|
208
|
+
usageUnreadable(where: string, because: string): string;
|
|
209
|
+
|
|
210
|
+
configHeading(): string;
|
|
211
|
+
nothingJustified(): string;
|
|
212
|
+
whyLocale(locale: string): string;
|
|
213
|
+
whyExtensions(extensions: string, files: number): string;
|
|
214
|
+
whyModelMeasured(model: string, sharePct: number): string;
|
|
215
|
+
whyModelSource(model: string, file: string, line: number): string;
|
|
216
|
+
whyCalls(perMonth: number, calls: number, days: number): string;
|
|
217
|
+
whyOutput(average: number, outputTokens: number, calls: number): string;
|
|
218
|
+
whyCache(rate: number, cacheReadTokens: number, inputTokens: number): string;
|
|
219
|
+
|
|
220
|
+
noModelEvidence(): string;
|
|
221
|
+
modelConflict(files: string): string;
|
|
222
|
+
modelProviderOnly(provider: string, file: string): string;
|
|
223
|
+
nothingMeasured(): string;
|
|
224
|
+
windowTooShort(days: number, minimum: number): string;
|
|
225
|
+
undatedCalls(undated: number, calls: number): string;
|
|
226
|
+
cacheNotRecorded(): string;
|
|
227
|
+
batchOnlyYouKnow(): string;
|
|
228
|
+
labelsUnprovable(labels: number): string;
|
|
229
|
+
budgetIsPolicy(): string;
|
|
230
|
+
budgetIsPolicyMeasured(usd: string, days: number): string;
|
|
231
|
+
|
|
232
|
+
findingHeading(): string;
|
|
233
|
+
noFinding(why: string): string;
|
|
234
|
+
findingCalls(calls: string, label: string, model: string, days: number): string;
|
|
235
|
+
findingSpent(usd: string): string;
|
|
236
|
+
findingRoute(model: string): string;
|
|
237
|
+
findingBatch(): string;
|
|
238
|
+
findingTotal(usd: string, days: number): string;
|
|
239
|
+
findingNext(): string;
|
|
240
|
+
|
|
241
|
+
wouldOverwrite(keys: string): string;
|
|
242
|
+
nothingToWrite(): string;
|
|
243
|
+
wouldWrite(path: string): string;
|
|
244
|
+
wrote(path: string): string;
|
|
245
|
+
existingRefused(path: string): string;
|
|
246
|
+
existingUnparseable(path: string): string;
|
|
247
|
+
};
|
|
248
|
+
|
|
192
249
|
where: {
|
|
193
250
|
hostHeading(): string;
|
|
194
251
|
subscription(host: string): string;
|