@trazum/core 1.9.0 → 1.25.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.
Files changed (108) hide show
  1. package/README.md +12 -5
  2. package/dist/advisories.d.ts.map +1 -1
  3. package/dist/advisories.js +94 -4
  4. package/dist/advisories.js.map +1 -1
  5. package/dist/against.d.ts +32 -0
  6. package/dist/against.d.ts.map +1 -0
  7. package/dist/against.js +34 -0
  8. package/dist/against.js.map +1 -0
  9. package/dist/config-schema.d.ts +42 -1
  10. package/dist/config-schema.d.ts.map +1 -1
  11. package/dist/config-schema.js +61 -0
  12. package/dist/config-schema.js.map +1 -1
  13. package/dist/conversation.d.ts +121 -0
  14. package/dist/conversation.d.ts.map +1 -0
  15. package/dist/conversation.js +157 -0
  16. package/dist/conversation.js.map +1 -0
  17. package/dist/csv.d.ts +61 -0
  18. package/dist/csv.d.ts.map +1 -0
  19. package/dist/csv.js +149 -0
  20. package/dist/csv.js.map +1 -0
  21. package/dist/evaluate.d.ts +24 -0
  22. package/dist/evaluate.d.ts.map +1 -1
  23. package/dist/evaluate.js +5 -2
  24. package/dist/evaluate.js.map +1 -1
  25. package/dist/i18n/en.d.ts.map +1 -1
  26. package/dist/i18n/en.js +18 -7
  27. package/dist/i18n/en.js.map +1 -1
  28. package/dist/i18n/es.d.ts.map +1 -1
  29. package/dist/i18n/es.js +18 -7
  30. package/dist/i18n/es.js.map +1 -1
  31. package/dist/i18n/types.d.ts +28 -1
  32. package/dist/i18n/types.d.ts.map +1 -1
  33. package/dist/index.d.ts +25 -1
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +28 -0
  36. package/dist/index.js.map +1 -1
  37. package/dist/input-shape.d.ts +104 -0
  38. package/dist/input-shape.d.ts.map +1 -0
  39. package/dist/input-shape.js +132 -0
  40. package/dist/input-shape.js.map +1 -0
  41. package/dist/levers.d.ts +151 -0
  42. package/dist/levers.d.ts.map +1 -0
  43. package/dist/levers.js +160 -0
  44. package/dist/levers.js.map +1 -0
  45. package/dist/node.d.ts +1 -1
  46. package/dist/node.d.ts.map +1 -1
  47. package/dist/output-shape.d.ts +96 -0
  48. package/dist/output-shape.d.ts.map +1 -0
  49. package/dist/output-shape.js +145 -0
  50. package/dist/output-shape.js.map +1 -0
  51. package/dist/pricing-overlay.d.ts +1 -1
  52. package/dist/pricing-overlay.d.ts.map +1 -1
  53. package/dist/pricing-overlay.js +46 -0
  54. package/dist/pricing-overlay.js.map +1 -1
  55. package/dist/repeats.d.ts +75 -0
  56. package/dist/repeats.d.ts.map +1 -0
  57. package/dist/repeats.js +82 -0
  58. package/dist/repeats.js.map +1 -0
  59. package/dist/reprice.d.ts +143 -0
  60. package/dist/reprice.d.ts.map +1 -0
  61. package/dist/reprice.js +82 -0
  62. package/dist/reprice.js.map +1 -0
  63. package/dist/session-cost.d.ts +70 -0
  64. package/dist/session-cost.d.ts.map +1 -0
  65. package/dist/session-cost.js +90 -0
  66. package/dist/session-cost.js.map +1 -0
  67. package/dist/session-ledger.d.ts +77 -0
  68. package/dist/session-ledger.d.ts.map +1 -0
  69. package/dist/session-ledger.js +99 -0
  70. package/dist/session-ledger.js.map +1 -0
  71. package/dist/tokenizer.d.ts +33 -26
  72. package/dist/tokenizer.d.ts.map +1 -1
  73. package/dist/tokenizer.js +95 -34
  74. package/dist/tokenizer.js.map +1 -1
  75. package/dist/ttl-fit.d.ts +103 -0
  76. package/dist/ttl-fit.d.ts.map +1 -0
  77. package/dist/ttl-fit.js +184 -0
  78. package/dist/ttl-fit.js.map +1 -0
  79. package/dist/types.d.ts +1 -1
  80. package/dist/types.d.ts.map +1 -1
  81. package/dist/usage.d.ts +637 -0
  82. package/dist/usage.d.ts.map +1 -0
  83. package/dist/usage.js +634 -0
  84. package/dist/usage.js.map +1 -0
  85. package/package.json +1 -1
  86. package/src/advisories.ts +96 -4
  87. package/src/against.ts +48 -0
  88. package/src/config-schema.ts +106 -0
  89. package/src/conversation.ts +305 -0
  90. package/src/csv.ts +184 -0
  91. package/src/evaluate.ts +33 -3
  92. package/src/i18n/en.ts +19 -6
  93. package/src/i18n/es.ts +19 -6
  94. package/src/i18n/types.ts +29 -1
  95. package/src/index.ts +65 -1
  96. package/src/input-shape.ts +259 -0
  97. package/src/levers.ts +331 -0
  98. package/src/node.ts +1 -1
  99. package/src/output-shape.ts +254 -0
  100. package/src/pricing-overlay.ts +52 -1
  101. package/src/repeats.ts +166 -0
  102. package/src/reprice.ts +227 -0
  103. package/src/session-cost.ts +170 -0
  104. package/src/session-ledger.ts +189 -0
  105. package/src/tokenizer.ts +93 -30
  106. package/src/ttl-fit.ts +251 -0
  107. package/src/types.ts +1 -0
  108. package/src/usage.ts +1267 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input-shape.js","sourceRoot":"","sources":["../src/input-shape.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,151 @@
1
+ import { UNLABELLED } from './usage.js';
2
+ import type { PricingCatalogue } from './pricing.js';
3
+ import type { UsageProfileReport } from './usage.js';
4
+ /**
5
+ * What would actually move this bill.
6
+ *
7
+ * ## The number this exists to answer
8
+ *
9
+ * Trazum's rules recover about **1%** of a bill. Measured, on an ordinary support
10
+ * prompt: three tokens out of three hundred and six. On a company spending twenty
11
+ * thousand a month that is two hundred, and nobody installs a tool for two
12
+ * hundred. The complaint is correct and it is the most important thing anybody has
13
+ * said about this product.
14
+ *
15
+ * The rest of the package reads a prompt file and shortens it. This reads what was
16
+ * charged and prices the levers that are **not** the prompt, from the same log, at
17
+ * the same arithmetic:
18
+ *
19
+ * | lever | what it moves |
20
+ * |---|---|
21
+ * | which model the call goes to | Opus 5 → Sonnet 5 is 40% off; → Haiku 4.5 is 80% |
22
+ * | the Batch API | 50% flat, on input and output |
23
+ * | prompt caching | 3–4x the rules |
24
+ * | shortening the prompt | ~1% |
25
+ *
26
+ * So the honest headline is that **the money is in what you call, not in how long
27
+ * the prompt is** — and the tool that only did the last row should say so, in the
28
+ * reader's own figures, rather than reporting a 1% win as a success.
29
+ *
30
+ * ## Every figure here is arithmetic on tokens that were billed
31
+ *
32
+ * Nothing is modelled and nothing is extrapolated. A route lever is the same token
33
+ * counts at another model's published rate. A batch lever is the same tokens at the
34
+ * provider's batch multiplier. There is no assumed traffic, no assumed prompt, no
35
+ * assumed anything — which is the whole reason this reads a usage log instead of a
36
+ * directory.
37
+ *
38
+ * ## What it refuses to do
39
+ *
40
+ * **It never says a lever is safe to take.** Routing a workload to a cheaper model
41
+ * is a quality question that arithmetic cannot answer, and this module has never
42
+ * seen the prompt or a single answer. So a route carries its dollar figure *and*
43
+ * the command that measures whether it holds, and it is described as worth testing
44
+ * rather than worth doing. The same posture the `model-downgrade` advisory has
45
+ * always had, for the same reason.
46
+ *
47
+ * **It never says "per month".** A usage log covers whatever period somebody
48
+ * happened to record, and this module is not told which. Every figure is "on this
49
+ * bill" — over exactly the calls in the file. Multiplying an unknown period into a
50
+ * monthly headline is how a tool ends up quoting a saving four times the real one.
51
+ *
52
+ * **It never crosses a vendor.** A cheaper model at another provider is a
53
+ * migration, not a routing change, and pricing one as though it were a switch you
54
+ * could make on Tuesday is a saving nobody can take.
55
+ */
56
+ /** What a lever is. */
57
+ export type LeverId =
58
+ /** Send these calls to a cheaper model of the same family. */
59
+ 'route'
60
+ /** Send these calls through the Batch API. */
61
+ | 'batch';
62
+ /**
63
+ * Everything available on one label-and-model slice, and what it comes to.
64
+ *
65
+ * **Grouped by slice rather than listed as separate levers, because the levers
66
+ * are not additive and a list invites adding them.** The first version printed
67
+ * "route support-rag: $12.60" and "batch support-rag: $10.50" as two rows against
68
+ * a slice that had only spent $21.00 — a reader who added them got $23.10, a
69
+ * saving larger than the bill it came from. Impossible, and in the flattering
70
+ * direction.
71
+ *
72
+ * They do combine, just not by addition: batching a routed call saves half of the
73
+ * *cheaper* model's price, not half of the one you left. `combinedUsd` is that
74
+ * figure, computed rather than summed.
75
+ */
76
+ export interface SliceLevers {
77
+ /** The label these calls carry, or `UNLABELLED`. */
78
+ label: string;
79
+ /** The model they go to now. */
80
+ model: string;
81
+ modelName: string;
82
+ /** Calls affected — the reader's own judgement of whether it is worth a day. */
83
+ calls: number;
84
+ /** What these exact calls cost. */
85
+ spentUsd: number;
86
+ /** A cheaper model one capability step down, if the catalogue has one. */
87
+ route: {
88
+ candidate: {
89
+ id: string;
90
+ displayName: string;
91
+ };
92
+ savingUsd: number;
93
+ } | null;
94
+ /** The Batch API, where the provider sells one. */
95
+ batch: {
96
+ savingUsd: number;
97
+ } | null;
98
+ /**
99
+ * Both together, **computed and never summed**. Equal to the single available
100
+ * lever when only one is.
101
+ */
102
+ combinedUsd: number;
103
+ /** `combinedUsd` as a fraction of the whole bill in the log, not of this slice. */
104
+ shareOfBill: number;
105
+ }
106
+ export interface BillLevers {
107
+ /** Ranked by what the whole slice could save, largest first. */
108
+ slices: SliceLevers[];
109
+ /**
110
+ * The most that shortening prompt text could ever be worth on this bill.
111
+ *
112
+ * Everything that is not output: plain input, cache reads, cache writes. It is a
113
+ * **ceiling and not an estimate** — deliberately generous, because it counts
114
+ * retrieved context, conversation history and tool results, none of which live in
115
+ * a prompt file and none of which a rules pass can touch. The real figure is
116
+ * below it, usually far below.
117
+ *
118
+ * It is here so the levers above have something to be compared against. A tool
119
+ * that reports a 1% win without saying 1% of what is not being useful.
120
+ */
121
+ promptCeilingUsd: number;
122
+ promptCeilingShare: number;
123
+ /** The bill the shares are taken against. */
124
+ totalUsd: number;
125
+ }
126
+ export interface BillLeverOptions {
127
+ catalogue: PricingCatalogue;
128
+ /** Date the prices are read at, so a promotional rate resolves the same way. */
129
+ on?: Date;
130
+ /**
131
+ * Slices worth less than this share of the bill are dropped.
132
+ *
133
+ * Not a judgement about small money — a judgement about attention. Thirty rows
134
+ * worth a tenth of a percent each bury the two worth twenty, and a report nobody
135
+ * finishes reading is a report that changed nothing. Default 1%.
136
+ */
137
+ minShare?: number;
138
+ }
139
+ /**
140
+ * Prices the levers that are not the prompt, from a profile of real calls.
141
+ *
142
+ * Returns them ranked by money, with the ceiling on prompt shortening beside them
143
+ * so the comparison is unavoidable. Empty when nothing clears `minShare`, which is
144
+ * a legitimate answer: a bill already on the cheapest model of its family, with no
145
+ * batch API to reach for, has no lever here, and saying so is more useful than
146
+ * manufacturing one.
147
+ */
148
+ export declare function billLevers(report: UsageProfileReport, options: BillLeverOptions): BillLevers;
149
+ /** Named so a report can say "unlabelled" in the reader's language. */
150
+ export { UNLABELLED };
151
+ //# sourceMappingURL=levers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"levers.d.ts","sourceRoot":"","sources":["../src/levers.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAkB,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAGrE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAEH,uBAAuB;AACvB,MAAM,MAAM,OAAO;AACjB,8DAA8D;AAC5D,OAAO;AACT,8CAA8C;GAC5C,OAAO,CAAC;AAEZ;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,WAAW;IAC1B,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,KAAK,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,KAAK,EAAE;QAAE,SAAS,EAAE;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACpF,mDAAmD;IACnD,KAAK,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACpC;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,mFAAmF;IACnF,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,gEAAgE;IAChE,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB;;;;;;;;;;;OAWG;IACH,gBAAgB,EAAE,MAAM,CAAC;IACzB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,CAAC;CAClB;AA8FD,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,gBAAgB,CAAC;IAC5B,gFAAgF;IAChF,EAAE,CAAC,EAAE,IAAI,CAAC;IACV;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,kBAAkB,EAC1B,OAAO,EAAE,gBAAgB,GACxB,UAAU,CAqFZ;AAED,uEAAuE;AACvE,OAAO,EAAE,UAAU,EAAE,CAAC"}
package/dist/levers.js ADDED
@@ -0,0 +1,160 @@
1
+ import { effectivePricing, multipliersFor } from './pricing.js';
2
+ import { UNLABELLED } from './usage.js';
3
+ /**
4
+ * Weakest first. A step *down* this ladder is what a route offers.
5
+ *
6
+ * `unknown` is absent on purpose rather than placed at one end: a model whose
7
+ * capability nobody recorded cannot be ranked against one whose capability is
8
+ * known, and guessing puts a real workload on a model chosen by a default value.
9
+ */
10
+ const CAPABILITY_LADDER = ['small', 'mid', 'large', 'frontier'];
11
+ /**
12
+ * The next step down, or `null` at the bottom and for `unknown`.
13
+ *
14
+ * One step, not the cheapest available. Frontier to small is an 80% saving and a
15
+ * different product, and offering it as the headline would be the arithmetic
16
+ * leading the advice — exactly the failure this file is written against. The
17
+ * reader who wants the bigger jump can ask for it once the first one holds.
18
+ */
19
+ function stepDown(capability) {
20
+ const at = CAPABILITY_LADDER.indexOf(capability);
21
+ return at <= 0 ? null : CAPABILITY_LADDER[at - 1];
22
+ }
23
+ /** What a breakdown's tokens would cost at a model's rates, split so batch can apply. */
24
+ function repriceAt(breakdown, model, on) {
25
+ const { inputPerMTok, outputPerMTok } = effectivePricing(model, on);
26
+ const rates = multipliersFor(model);
27
+ const per = (tokens, rate) => (tokens / 1_000_000) * rate;
28
+ const inputUsd = per(breakdown.inputTokens, inputPerMTok);
29
+ const outputUsd = per(breakdown.outputTokens, outputPerMTok);
30
+ /**
31
+ * Cache writes at the 5-minute rate. The breakdown does not carry the recorded
32
+ * TTL split per class, so one has to be chosen — and the same choice sits on
33
+ * both sides of every subtraction here, so it cancels out of the saving. Worth
34
+ * stating rather than papering over: it would not cancel if the two models had
35
+ * different write multipliers, which is why a route never crosses a vendor.
36
+ */
37
+ const cacheUsd = per(breakdown.cacheReadTokens, inputPerMTok * rates.cacheRead) +
38
+ per(breakdown.cacheWriteTokens, inputPerMTok * rates.cacheWrite5m);
39
+ return { inputUsd, outputUsd, cacheUsd, totalUsd: inputUsd + outputUsd + cacheUsd };
40
+ }
41
+ /**
42
+ * The cheapest recommendable model one capability step below, same provider.
43
+ *
44
+ * Same provider because switching vendor is a migration rather than a routing
45
+ * change, and the context window has to hold what these calls already sent — a
46
+ * cheaper model that cannot fit the prompt is not cheaper, it is broken.
47
+ */
48
+ function candidateFor(model, breakdown, catalogue, on) {
49
+ const target = stepDown(model.capability);
50
+ if (target === null)
51
+ return null;
52
+ /**
53
+ * The largest single call cannot be recovered from a total, so this uses the
54
+ * **average** input per call and refuses any candidate that could not hold it.
55
+ * An average understates the peak, so this is the permissive direction — stated
56
+ * rather than hidden, because the reader will check the window properly when
57
+ * they run the evaluation this points them at.
58
+ */
59
+ const avgInput = breakdown.calls === 0
60
+ ? 0
61
+ : (breakdown.inputTokens + breakdown.cacheReadTokens + breakdown.cacheWriteTokens) /
62
+ breakdown.calls;
63
+ const candidates = catalogue.models.filter((m) => m.id !== model.id &&
64
+ m.capability === target &&
65
+ m.provider === model.provider &&
66
+ m.recommendable !== false &&
67
+ m.contextWindow >= avgInput);
68
+ if (candidates.length === 0)
69
+ return null;
70
+ return candidates.reduce((best, m) => repriceAt(breakdown, m, on).totalUsd < repriceAt(breakdown, best, on).totalUsd ? m : best);
71
+ }
72
+ /**
73
+ * Prices the levers that are not the prompt, from a profile of real calls.
74
+ *
75
+ * Returns them ranked by money, with the ceiling on prompt shortening beside them
76
+ * so the comparison is unavoidable. Empty when nothing clears `minShare`, which is
77
+ * a legitimate answer: a bill already on the cheapest model of its family, with no
78
+ * batch API to reach for, has no lever here, and saying so is more useful than
79
+ * manufacturing one.
80
+ */
81
+ export function billLevers(report, options) {
82
+ const { catalogue, on = new Date(), minShare = 0.01 } = options;
83
+ const totalUsd = report.total.totalUsd;
84
+ const promptCeilingUsd = report.total.inputUsd + report.total.cacheReadUsd + report.total.cacheWriteUsd;
85
+ if (totalUsd <= 0) {
86
+ return { slices: [], promptCeilingUsd, promptCeilingShare: 0, totalUsd };
87
+ }
88
+ const slices = [];
89
+ for (const { label, model: modelId, breakdown } of report.byLabelAndModel) {
90
+ const model = catalogue.byId.get(modelId);
91
+ // An unpriced model never reaches this list with dollars on it, and a lever
92
+ // computed from a zero bill is a saving invented out of nothing.
93
+ if (!model || breakdown.totalUsd <= 0)
94
+ continue;
95
+ const candidate = candidateFor(model, breakdown, catalogue, on);
96
+ const routed = candidate ? repriceAt(breakdown, candidate, on) : null;
97
+ const route = candidate && routed && breakdown.totalUsd - routed.totalUsd > 0
98
+ ? {
99
+ candidate: { id: candidate.id, displayName: candidate.displayName },
100
+ savingUsd: breakdown.totalUsd - routed.totalUsd,
101
+ }
102
+ : null;
103
+ /**
104
+ * `null` means the provider has no batch API, which is different from an
105
+ * unstated one — offering a discount nobody sells is worse than staying quiet.
106
+ *
107
+ * Applied to input and output only. The published discount covers those two
108
+ * lines; whether it also reaches cache reads and writes is not something this
109
+ * catalogue records, so they stay at full price. That understates the saving,
110
+ * which is the direction to be wrong in.
111
+ */
112
+ const batchRate = multipliersFor(model).batch;
113
+ const batchable = batchRate !== null && batchRate < 1;
114
+ const batch = batchable
115
+ ? { savingUsd: (breakdown.inputUsd + breakdown.outputUsd) * (1 - batchRate) }
116
+ : null;
117
+ /**
118
+ * Both together — **computed, never summed.**
119
+ *
120
+ * Batching a routed call discounts the cheaper model's price, not the one you
121
+ * left behind. Adding the two figures produced a saving larger than the slice
122
+ * had ever cost: $12.60 and $10.50 against $21.00 spent.
123
+ */
124
+ const afterBoth = routed ?? {
125
+ inputUsd: breakdown.inputUsd,
126
+ outputUsd: breakdown.outputUsd,
127
+ cacheUsd: breakdown.cacheReadUsd + breakdown.cacheWriteUsd,
128
+ totalUsd: breakdown.totalUsd,
129
+ };
130
+ const combinedCost = batchable
131
+ ? afterBoth.totalUsd - (afterBoth.inputUsd + afterBoth.outputUsd) * (1 - batchRate)
132
+ : afterBoth.totalUsd;
133
+ const combinedUsd = breakdown.totalUsd - combinedCost;
134
+ if (combinedUsd <= 0)
135
+ continue;
136
+ const shareOfBill = combinedUsd / totalUsd;
137
+ if (shareOfBill < minShare)
138
+ continue;
139
+ slices.push({
140
+ label,
141
+ model: modelId,
142
+ modelName: model.displayName,
143
+ calls: breakdown.calls,
144
+ spentUsd: breakdown.totalUsd,
145
+ route,
146
+ batch,
147
+ combinedUsd,
148
+ shareOfBill,
149
+ });
150
+ }
151
+ return {
152
+ slices: slices.sort((a, b) => b.combinedUsd - a.combinedUsd),
153
+ promptCeilingUsd,
154
+ promptCeilingShare: promptCeilingUsd / totalUsd,
155
+ totalUsd,
156
+ };
157
+ }
158
+ /** Named so a report can say "unlabelled" in the reader's language. */
159
+ export { UNLABELLED };
160
+ //# sourceMappingURL=levers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"levers.js","sourceRoot":"","sources":["../src/levers.ts"],"names":[],"mappings":"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"}
package/dist/node.d.ts CHANGED
@@ -17,7 +17,7 @@
17
17
  export { loadConfig } from './config.js';
18
18
  export type { LoadConfigOptions, LoadedConfig } from './config.js';
19
19
  export { CONFIG_FILENAME, CONFIG_KEYS, CONFIG_USAGE_KEYS, ConfigError, DEFAULT_EXTENSIONS, MAX_CONFIG_BYTES, MAX_CONFIG_SEARCH_DEPTH, budgetFor, parseConfig, validateConfigModel, } from './config-schema.js';
20
- export type { ResolvedBudget, TrazumConfig } from './config-schema.js';
20
+ export type { ResolvedBudget, SpendConfig, TrazumConfig } from './config-schema.js';
21
21
  export { MAX_PRICING_BYTES, PricingOverlayError, applyPricingOverlay, catalogueFromOverlay, parsePricingOverlay, } from './pricing-overlay.js';
22
22
  export type { PricingOverlay } from './pricing-overlay.js';
23
23
  export { openrouterOverlay } from './openrouter.js';
@@ -1 +1 @@
1
- {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,YAAY,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAInE,OAAO,EACL,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,WAAW,EACX,kBAAkB,EAClB,gBAAgB,EAChB,uBAAuB,EACvB,SAAS,EACT,WAAW,EACX,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIvE,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAI3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAGxD,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGrD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,YAAY,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEjD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,YAAY,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAInE,OAAO,EACL,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,WAAW,EACX,kBAAkB,EAClB,gBAAgB,EAChB,uBAAuB,EACvB,SAAS,EACT,WAAW,EACX,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIpF,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAI3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAGxD,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGrD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,YAAY,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEjD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,96 @@
1
+ import type { PricingCatalogue } from './pricing.js';
2
+ import type { UsageRecord } from './usage.js';
3
+ /**
4
+ * Where the output spend concentrates.
5
+ *
6
+ * ## The biggest line, and nothing said anything actionable about it
7
+ *
8
+ * Output is over half of many real bills — **87%** on the support prompt this
9
+ * repository measures itself against. `profile` could say that much and then
10
+ * stopped, because the advice that follows from "output dominates" is about
11
+ * answers rather than prompts, and the rules engine has nothing to offer there.
12
+ *
13
+ * But a total hides the shape, and the shape is the actionable part. Two bills
14
+ * with identical output spend want opposite responses:
15
+ *
16
+ * - **A tail.** Six per cent of calls hold half the output spend. Those calls are
17
+ * doing something the other ninety-four are not — a different path through the
18
+ * prompt, a runaway with no `max_tokens`, a retrieval that returned a book. They
19
+ * are a morning's work and they are worth finding.
20
+ * - **Flat.** Forty-five per cent of calls hold half of it, which is what "evenly
21
+ * spread" looks like. There is no tail to hunt; the answer length is inherent to
22
+ * the task, and the only lever is asking every answer to be shorter.
23
+ *
24
+ * ## The split is derived, not chosen
25
+ *
26
+ * The figure reported is **the smallest group of calls that holds at least half the
27
+ * output spend**. Half is the point that divides the spend in two — a median over
28
+ * money rather than a threshold somebody picked — and the group is found by walking
29
+ * the distribution down from the longest answers until half the spend is covered.
30
+ *
31
+ * "At least half" is meant literally. The walk stops on a bucket boundary, so the
32
+ * group it names is a whole number of buckets and can overshoot; saying "half"
33
+ * flat would be claiming a precision the histogram does not have.
34
+ *
35
+ * ## Bounded memory, exact statement
36
+ *
37
+ * The counts live in fixed buckets rather than a list of every call, because a
38
+ * usage log is measured in megabytes. Every call inside an included bucket is at or
39
+ * above that bucket's lower edge, so **"calls producing more than N tokens" is
40
+ * exact** for the N this reports — it is only ever a bucket edge.
41
+ */
42
+ /** How the output spend of one label-and-model slice is distributed. */
43
+ export interface OutputShape {
44
+ label: string;
45
+ model: string;
46
+ modelName: string;
47
+ calls: number;
48
+ outputTokens: number;
49
+ outputUsd: number;
50
+ /** The bucket edge the heaviest group sits above. Always a bucket boundary. */
51
+ aboveTokens: number;
52
+ /** How many calls are in that group. */
53
+ heavyCalls: number;
54
+ /** Their share of the calls in this slice. */
55
+ heavyCallShare: number;
56
+ /** Their share of this slice's output spend — at least a half, by construction. */
57
+ heavySpendShare: number;
58
+ /** This slice's output spend as a fraction of the whole bill. */
59
+ shareOfBill: number;
60
+ /**
61
+ * The bucket ceiling that at least half the measured answers fit within.
62
+ *
63
+ * A ceiling by construction, never an interpolation: the histogram knows
64
+ * which bucket the median call landed in, and the honest sentence is "half
65
+ * the answers fit within N tokens" where N is that bucket's upper edge.
66
+ * `null` only when the covering bucket is the open-ended last one, which has
67
+ * no ceiling to name.
68
+ */
69
+ medianWithinTokens: number | null;
70
+ /**
71
+ * The same ceiling for 95% of the measured answers — the number somebody
72
+ * setting `max_tokens` actually wants. Measured on these calls, promised for
73
+ * nothing.
74
+ */
75
+ p95WithinTokens: number | null;
76
+ }
77
+ export interface OutputShapeOptions {
78
+ catalogue: PricingCatalogue;
79
+ on?: Date;
80
+ /** Slices whose output is below this share of the bill are dropped. Default 5%. */
81
+ minShare?: number;
82
+ }
83
+ export interface OutputShapeTracker {
84
+ add(record: UsageRecord): void;
85
+ finish(totalUsd: number): OutputShape[];
86
+ }
87
+ /**
88
+ * An accumulator, fed in the pass a profile already makes.
89
+ *
90
+ * What it holds is bounded by the number of slices times the number of buckets any
91
+ * of them actually touches, not by the size of the log.
92
+ */
93
+ export declare function createOutputShapeTracker(options: OutputShapeOptions): OutputShapeTracker;
94
+ /** The same measurement over a list of records, for a caller holding one. */
95
+ export declare function outputShapes(records: readonly UsageRecord[], totalUsd: number, options: OutputShapeOptions): OutputShape[];
96
+ //# sourceMappingURL=output-shape.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output-shape.d.ts","sourceRoot":"","sources":["../src/output-shape.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,wEAAwE;AACxE,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,WAAW,EAAE,MAAM,CAAC;IACpB,wCAAwC;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,cAAc,EAAE,MAAM,CAAC;IACvB,mFAAmF;IACnF,eAAe,EAAE,MAAM,CAAC;IACxB,iEAAiE;IACjE,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;;;OAQG;IACH,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC;;;;OAIG;IACH,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,gBAAgB,CAAC;IAC5B,EAAE,CAAC,EAAE,IAAI,CAAC;IACV,mFAAmF;IACnF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AA2DD,MAAM,WAAW,kBAAkB;IACjC,GAAG,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC;IAC/B,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,EAAE,CAAC;CACzC;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,kBAAkB,GAAG,kBAAkB,CAsFxF;AAED,6EAA6E;AAC7E,wBAAgB,YAAY,CAC1B,OAAO,EAAE,SAAS,WAAW,EAAE,EAC/B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,kBAAkB,GAC1B,WAAW,EAAE,CAIf"}
@@ -0,0 +1,145 @@
1
+ import { effectivePricing } from './pricing.js';
2
+ import { UNLABELLED } from './usage.js';
3
+ /**
4
+ * Bucket edges, fine where answers actually land and coarse in the tail.
5
+ *
6
+ * 64 tokens up to 8,192 covers ordinary answers at a resolution finer than anybody
7
+ * would act on; past that the buckets widen, because the difference between a
8
+ * 40,000-token answer and a 41,000-token one changes no decision. The last bucket is
9
+ * open-ended so nothing falls off the end — an answer longer than the largest edge
10
+ * still counts, in the group where it belongs.
11
+ */
12
+ const EDGES = (() => {
13
+ const edges = [];
14
+ for (let t = 0; t < 8192; t += 64)
15
+ edges.push(t);
16
+ for (let t = 8192; t < 131_072; t += 1024)
17
+ edges.push(t);
18
+ return edges;
19
+ })();
20
+ /** Index of the bucket a count falls in. The last bucket is open-ended. */
21
+ function bucketOf(tokens) {
22
+ if (tokens >= EDGES[EDGES.length - 1])
23
+ return EDGES.length - 1;
24
+ if (tokens < 8192)
25
+ return Math.floor(tokens / 64);
26
+ return 128 + Math.floor((tokens - 8192) / 1024);
27
+ }
28
+ /** A bucket's upper edge, or `null` for the open-ended last one. */
29
+ function upperEdgeOf(bucket) {
30
+ if (bucket >= EDGES.length - 1)
31
+ return null;
32
+ return EDGES[bucket + 1];
33
+ }
34
+ /**
35
+ * The bucket ceiling covering `share` of the calls, walking up from the
36
+ * shortest answers. Exact over the histogram: every call at or below the
37
+ * returned ceiling is counted, none is interpolated.
38
+ */
39
+ function ceilingFor(buckets, totalCalls, share) {
40
+ const ascending = [...buckets.keys()].sort((a, b) => a - b);
41
+ const target = totalCalls * share;
42
+ let covered = 0;
43
+ for (const b of ascending) {
44
+ covered += buckets.get(b).calls;
45
+ if (covered >= target)
46
+ return upperEdgeOf(b);
47
+ }
48
+ return upperEdgeOf(ascending[ascending.length - 1]);
49
+ }
50
+ /**
51
+ * An accumulator, fed in the pass a profile already makes.
52
+ *
53
+ * What it holds is bounded by the number of slices times the number of buckets any
54
+ * of them actually touches, not by the size of the log.
55
+ */
56
+ export function createOutputShapeTracker(options) {
57
+ const { catalogue, on = new Date(), minShare = 0.05 } = options;
58
+ const slices = new Map();
59
+ const add = (record) => {
60
+ const model = catalogue.byId.get(record.model);
61
+ // An unpriced model contributes no dollars anywhere else; a shape drawn from
62
+ // one would be a distribution of a bill that was never computed.
63
+ if (!model)
64
+ return;
65
+ if (record.outputTokens <= 0)
66
+ return;
67
+ const key = `${record.label ?? UNLABELLED}\n${record.model}`;
68
+ let slice = slices.get(key);
69
+ if (!slice) {
70
+ slice = { calls: 0, outputTokens: 0, outputUsd: 0, buckets: new Map() };
71
+ slices.set(key, slice);
72
+ }
73
+ const { outputPerMTok } = effectivePricing(model, on);
74
+ slice.calls += 1;
75
+ slice.outputTokens += record.outputTokens;
76
+ slice.outputUsd += (record.outputTokens / 1_000_000) * outputPerMTok;
77
+ const b = bucketOf(record.outputTokens);
78
+ const cell = slice.buckets.get(b);
79
+ if (cell) {
80
+ cell.calls += 1;
81
+ cell.tokens += record.outputTokens;
82
+ }
83
+ else {
84
+ slice.buckets.set(b, { calls: 1, tokens: record.outputTokens });
85
+ }
86
+ };
87
+ const finish = (totalUsd) => {
88
+ const out = [];
89
+ for (const [key, slice] of slices) {
90
+ const split = key.indexOf('\n');
91
+ const label = key.slice(0, split);
92
+ const modelId = key.slice(split + 1);
93
+ const model = catalogue.byId.get(modelId);
94
+ if (!model || slice.outputTokens === 0)
95
+ continue;
96
+ const shareOfBill = totalUsd > 0 ? slice.outputUsd / totalUsd : 0;
97
+ if (shareOfBill < minShare)
98
+ continue;
99
+ /**
100
+ * Walk down from the longest answers until half the output spend is
101
+ * covered. Spend is proportional to tokens inside one slice — one model,
102
+ * one rate — so the tokens are the money here and no second accumulator is
103
+ * needed.
104
+ */
105
+ const target = slice.outputTokens / 2;
106
+ const descending = [...slice.buckets.keys()].sort((a, b) => b - a);
107
+ let tokens = 0;
108
+ let heavyCalls = 0;
109
+ let lastBucket = descending[0];
110
+ for (const b of descending) {
111
+ const cell = slice.buckets.get(b);
112
+ tokens += cell.tokens;
113
+ heavyCalls += cell.calls;
114
+ lastBucket = b;
115
+ if (tokens >= target)
116
+ break;
117
+ }
118
+ out.push({
119
+ label,
120
+ model: modelId,
121
+ modelName: model.displayName,
122
+ calls: slice.calls,
123
+ outputTokens: slice.outputTokens,
124
+ outputUsd: slice.outputUsd,
125
+ aboveTokens: EDGES[lastBucket],
126
+ heavyCalls,
127
+ heavyCallShare: heavyCalls / slice.calls,
128
+ heavySpendShare: tokens / slice.outputTokens,
129
+ shareOfBill,
130
+ medianWithinTokens: ceilingFor(slice.buckets, slice.calls, 0.5),
131
+ p95WithinTokens: ceilingFor(slice.buckets, slice.calls, 0.95),
132
+ });
133
+ }
134
+ return out.sort((a, b) => b.outputUsd - a.outputUsd);
135
+ };
136
+ return { add, finish };
137
+ }
138
+ /** The same measurement over a list of records, for a caller holding one. */
139
+ export function outputShapes(records, totalUsd, options) {
140
+ const tracker = createOutputShapeTracker(options);
141
+ for (const record of records)
142
+ tracker.add(record);
143
+ return tracker.finish(totalUsd);
144
+ }
145
+ //# sourceMappingURL=output-shape.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output-shape.js","sourceRoot":"","sources":["../src/output-shape.ts"],"names":[],"mappings":"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"}
@@ -18,7 +18,7 @@ import type { ModelPricing } from './types.js';
18
18
  * exists to prevent.
19
19
  */
20
20
  export declare const PRICING_OVERLAY_KEYS: readonly ['lastReviewed', 'models'];
21
- export declare const PRICING_MODEL_KEYS: readonly ['displayName', 'inputPerMTok', 'outputPerMTok', 'contextWindow', 'cacheMinTokens', 'tier', 'capability', 'caching', 'notes', 'promo'];
21
+ export declare const PRICING_MODEL_KEYS: readonly ['displayName', 'inputPerMTok', 'outputPerMTok', 'contextWindow', 'cacheMinTokens', 'tier', 'capability', 'caching', 'notes', 'promo', 'multipliers'];
22
22
  /** Largest overlay this will read — a price list, not a dataset. */
23
23
  export declare const MAX_PRICING_BYTES: number;
24
24
  export interface PricingOverlay {
@@ -1 +1 @@
1
- {"version":3,"file":"pricing-overlay.d.ts","sourceRoot":"","sources":["../src/pricing-overlay.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAA2B,YAAY,EAAE,MAAM,YAAY,CAAC;AAExE;;;;;;;;;;;;;;;;GAgBG;AAEH,eAAO,MAAM,oBAAoB,YAAI,cAAc,EAAE,QAAQ,CAAU,CAAC;AAExE,eAAO,MAAM,kBAAkB,YAC7B,aAAa,EACb,cAAc,EACd,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,MAAM,EACN,YAAY,EACZ,SAAS,EACT,OAAO,EACP,OAAO,CACC,CAAC;AAQX,oEAAoE;AACpE,eAAO,MAAM,iBAAiB,QAAY,CAAC;AAE3C,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;CAC3D;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IAG1C,QAAQ,CAAC,MAAM,EAAE,MAAM;IAFzB,YACE,OAAO,EAAE,MAAM,EACN,MAAM,EAAE,MAAM,EAIxB;CACF;AAqJD,kFAAkF;AAClF,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,SAAoB,GAAG,cAAc,CAkC3F;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,gBAAgB,EACtB,OAAO,EAAE,cAAc,EACvB,MAAM,SAAoB,GACzB,gBAAgB,CA6ClB;AAED,2EAA2E;AAC3E,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,SAAoB,GAAG,gBAAgB,CAE9F"}
1
+ {"version":3,"file":"pricing-overlay.d.ts","sourceRoot":"","sources":["../src/pricing-overlay.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAA4C,YAAY,EAAE,MAAM,YAAY,CAAC;AAEzF;;;;;;;;;;;;;;;;GAgBG;AAEH,eAAO,MAAM,oBAAoB,YAAI,cAAc,EAAE,QAAQ,CAAU,CAAC;AAExE,eAAO,MAAM,kBAAkB,YAC7B,aAAa,EACb,cAAc,EACd,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,MAAM,EACN,YAAY,EACZ,SAAS,EACT,OAAO,EACP,OAAO,EACP,aAAa,CACL,CAAC;AAyBX,oEAAoE;AACpE,eAAO,MAAM,iBAAiB,QAAY,CAAC;AAE3C,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;CAC3D;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IAG1C,QAAQ,CAAC,MAAM,EAAE,MAAM;IAFzB,YACE,OAAO,EAAE,MAAM,EACN,MAAM,EAAE,MAAM,EAIxB;CACF;AAsLD,kFAAkF;AAClF,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,SAAoB,GAAG,cAAc,CAkC3F;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,gBAAgB,EACtB,OAAO,EAAE,cAAc,EACvB,MAAM,SAAoB,GACzB,gBAAgB,CA6ClB;AAED,2EAA2E;AAC3E,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,SAAoB,GAAG,gBAAgB,CAE9F"}