@trazum/core 1.50.4 → 1.50.6

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/ladder.js ADDED
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Cheap first, escalate on measured failure — and the number that says whether
3
+ * that is a saving or a bill.
4
+ *
5
+ * "Route it to the cheaper model" has been a recommendation with a quality
6
+ * question attached since 1.23. Outcomes answered the quality question. This
7
+ * answers the one nobody asks out loud: **an escalation pays twice**, so a
8
+ * ladder is only a saving below a specific escalation rate, and above it the
9
+ * ladder costs more than never having built it.
10
+ *
11
+ * ## The arithmetic, stated rather than assumed
12
+ *
13
+ * Without a ladder, every call costs `dear`. With one, every call costs
14
+ * `cheap`, and the escalated share pays `dear` **on top** — the cheap attempt
15
+ * is not refunded.
16
+ *
17
+ * with a ladder: cheap + rate x dear
18
+ * without one: dear
19
+ *
20
+ * Those are equal at `rate = (dear - cheap) / dear`. Below it the ladder saves;
21
+ * above it the ladder is a more expensive way to get the same answers. A ladder
22
+ * sold as a saving without that number is the same head-arithmetic error `plan`
23
+ * was built to kill — and it is worse here, because the mistake compounds with
24
+ * traffic and nobody notices until a quarter is over.
25
+ *
26
+ * ## The escalation signal is the caller's
27
+ *
28
+ * Never inferred from length, latency, refusal text, a stop reason or a retry.
29
+ * The same refusal `outcome` makes, for a sharper reason: this is a **control
30
+ * loop**, not a report. A report built on a guess prints a wrong number; a
31
+ * control loop built on a guess sends real traffic to a more expensive model on
32
+ * the strength of that guess, forever, and bills for it.
33
+ *
34
+ * No signal, no ladder.
35
+ *
36
+ * ## What this module does not do
37
+ *
38
+ * It does not execute anything. A ladder escalates *after* a failure is known,
39
+ * which is after the answer came back and usually after something downstream
40
+ * judged it — so the retry belongs to the caller's own loop, not to a proxy
41
+ * sitting on one request. What lives here is the policy and the arithmetic that
42
+ * says whether the policy is worth running, measured against what actually
43
+ * happened.
44
+ */
45
+ import { judgeOutcome } from './outcome.js';
46
+ import { effectivePricing } from './pricing.js';
47
+ /**
48
+ * Calls a workload needs before its escalation rate is treated as a rate.
49
+ *
50
+ * The same floor `per-outcome` uses for successes, and for the same reason: a
51
+ * rate over fewer than ten observations moves more from one more observation
52
+ * than from anything a team could do about it. A control loop switched on
53
+ * because of nine calls is a control loop switched on for no reason.
54
+ */
55
+ export const MIN_CALLS_FOR_LADDER = 10;
56
+ /**
57
+ * How close to break-even counts as break-even.
58
+ *
59
+ * Two percentage points. Inside that band the ladder's sign flips on ordinary
60
+ * week-to-week variation, and reporting "saving" on Monday and "costing" on
61
+ * Thursday from the same policy would teach a reader to ignore the figure.
62
+ */
63
+ export const BREAK_EVEN_BAND = 0.02;
64
+ /**
65
+ * The cost of one call on a model, for a described shape of work.
66
+ *
67
+ * Deliberately takes the token shape rather than reading it from a log: the
68
+ * break-even rate is a property of the *models and the work*, and somebody
69
+ * should be able to compute it before running a single call through a ladder.
70
+ */
71
+ export function ladderArithmetic(policy, shape, catalogue, on = new Date()) {
72
+ const priceOf = (id) => {
73
+ if (id === undefined)
74
+ return null;
75
+ const model = catalogue.byId.get(id);
76
+ if (model === undefined)
77
+ return null;
78
+ const rates = effectivePricing(model, on);
79
+ return ((shape.inputTokens / 1_000_000) * rates.inputPerMTok +
80
+ (shape.outputTokens / 1_000_000) * rates.outputPerMTok);
81
+ };
82
+ const cheap = priceOf(policy.tiers[0]);
83
+ /**
84
+ * The **last** tier, not the second.
85
+ *
86
+ * A three-rung ladder's alternative is the model it would have used without
87
+ * one, which is the top rung. Comparing against the middle would report a
88
+ * saving against a model nobody was going to use.
89
+ */
90
+ const dear = priceOf(policy.tiers[policy.tiers.length - 1]);
91
+ return {
92
+ cheapUsd: cheap ?? 0,
93
+ dearUsd: dear ?? 0,
94
+ breakEvenRate: cheap === null || dear === null || dear <= 0 ? null : (dear - cheap) / dear,
95
+ };
96
+ }
97
+ export function ladderPosition(policy, tally, shape, vocabulary, catalogue, on = new Date()) {
98
+ const arithmetic = ladderArithmetic(policy, shape, catalogue, on);
99
+ const bare = (unknown) => ({
100
+ arithmetic,
101
+ measuredRate: null,
102
+ verdict: 'cannot-tell',
103
+ unknown,
104
+ calls: 0,
105
+ escalations: 0,
106
+ deltaUsdPerCall: null,
107
+ });
108
+ if (arithmetic.breakEvenRate === null)
109
+ return bare('tier-unpriced');
110
+ if (policy.escalateOn.length === 0)
111
+ return bare('no-escalation-values-declared');
112
+ const declared = vocabulary ?? { values: [], success: [] };
113
+ let calls = 0;
114
+ let escalations = 0;
115
+ for (const entry of tally.byValue) {
116
+ // Undeclared values are out of the denominator as well as the numerator —
117
+ // a typo in an exporter must not move a control loop's break-even.
118
+ if (judgeOutcome(entry.value, declared) === 'undeclared')
119
+ continue;
120
+ calls += entry.calls;
121
+ if (policy.escalateOn.includes(entry.value))
122
+ escalations += entry.calls;
123
+ }
124
+ if (calls === 0)
125
+ return bare('no-outcomes-recorded');
126
+ if (calls < MIN_CALLS_FOR_LADDER) {
127
+ return {
128
+ arithmetic,
129
+ measuredRate: null,
130
+ verdict: 'cannot-tell',
131
+ unknown: 'too-few-calls',
132
+ calls,
133
+ escalations,
134
+ deltaUsdPerCall: null,
135
+ };
136
+ }
137
+ const measuredRate = escalations / calls;
138
+ const withLadder = arithmetic.cheapUsd + measuredRate * arithmetic.dearUsd;
139
+ const deltaUsdPerCall = withLadder - arithmetic.dearUsd;
140
+ const distance = measuredRate - arithmetic.breakEvenRate;
141
+ const verdict = Math.abs(distance) <= BREAK_EVEN_BAND ? 'at-break-even' : distance < 0 ? 'saving' : 'costing';
142
+ return { arithmetic, measuredRate, verdict, unknown: null, calls, escalations, deltaUsdPerCall };
143
+ }
144
+ /**
145
+ * Everything wrong with a ladder before it is ever run.
146
+ *
147
+ * Returned rather than thrown, so a caller can report all of it at once — a
148
+ * config that has to be fixed one error per run is a config people give up on.
149
+ *
150
+ * The last two are the interesting ones. Escalating on a value the vocabulary
151
+ * never declared is a ladder that silently never fires; escalating on a value
152
+ * declared as a **success** is a ladder that pays twice for work that already
153
+ * worked, which is the most expensive possible typo in this file.
154
+ */
155
+ export function validateLadder(policy, vocabulary, catalogue, on = new Date()) {
156
+ const problems = [];
157
+ if (policy.tiers.length < 2) {
158
+ problems.push({ kind: 'too-few-tiers', tiers: policy.tiers.length });
159
+ }
160
+ const seen = new Set();
161
+ let previous = null;
162
+ for (const id of policy.tiers) {
163
+ if (seen.has(id))
164
+ problems.push({ kind: 'duplicate-tier', model: id });
165
+ seen.add(id);
166
+ const model = catalogue.byId.get(id);
167
+ if (model === undefined) {
168
+ problems.push({ kind: 'unknown-model', model: id });
169
+ continue;
170
+ }
171
+ const price = effectivePricing(model, on).inputPerMTok;
172
+ if (previous !== null && price < previous.price) {
173
+ // A ladder whose rungs go down is not a ladder; it is a routing rule
174
+ // that escalates to something cheaper and then reports a saving for it.
175
+ problems.push({ kind: 'tiers-not-cheapest-first', model: id, after: previous.id });
176
+ }
177
+ previous = { id, price };
178
+ }
179
+ if (vocabulary !== null) {
180
+ for (const value of policy.escalateOn) {
181
+ if (!vocabulary.values.includes(value)) {
182
+ problems.push({ kind: 'escalate-on-undeclared', value });
183
+ }
184
+ else if (vocabulary.success.includes(value)) {
185
+ problems.push({ kind: 'escalate-on-a-success', value });
186
+ }
187
+ }
188
+ }
189
+ return problems;
190
+ }
191
+ //# sourceMappingURL=ladder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ladder.js","sourceRoot":"","sources":["../src/ladder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Dollars per outcome — and the three ways this refuses to state one.
3
+ *
4
+ * 1.50.4 recorded the numerator. This divides by it, which sounds like
5
+ * arithmetic and is almost entirely a set of decisions about when *not* to do
6
+ * the arithmetic. A cost per resolution is the most quotable number this
7
+ * product will ever print — it ends up in a slide, in a quarterly review, in an
8
+ * argument about whether to keep a feature — and every way of getting it
9
+ * slightly wrong is a way of getting somebody's decision badly wrong.
10
+ *
11
+ * ## Which bill is the numerator
12
+ *
13
+ * The obvious implementation divides the **whole** slice bill by its successes.
14
+ * It is wrong, and wrong in the direction that makes a product look worse than
15
+ * it is: any call that carried no outcome is spend with no chance of appearing
16
+ * in the denominator, so the ratio is inflated by exactly the uninstrumented
17
+ * share — silently, and by an amount nobody can see from the figure.
18
+ *
19
+ * A team that instruments half its traffic would read a cost per resolution
20
+ * twice the real one, conclude the feature is uneconomic, and kill it.
21
+ *
22
+ * So the numerator is **recorded spend only**: the dollars on calls that
23
+ * carried an outcome. That makes it a ratio over a sample, which is fine and
24
+ * only fine because the coverage is stated beside it every single time, and
25
+ * because below a floor it is not stated at all.
26
+ *
27
+ * ## Three refusals
28
+ *
29
+ * 1. **Too few outcomes is not a rate.** Two resolutions and one figure is
30
+ * noise with a dollar sign. The count is shown instead — the same refusal
31
+ * `route` makes about small case sets and `history` makes about short runs.
32
+ * 2. **Too little coverage is not a rate.** A slice where an eighth of the
33
+ * spend carried an outcome yields a ratio over an unknown denominator. The
34
+ * coverage is shown instead.
35
+ * 3. **No successes recorded is not a rate of infinity.** A slice that spent
36
+ * money and resolved nothing is a real and alarming measurement, and it is
37
+ * reported as what it is rather than as a division by zero dressed up.
38
+ *
39
+ * ## Two rankings, and the product prints both
40
+ *
41
+ * Cheapest per call and cheapest per outcome are **different orders**, and the
42
+ * whole reason this chapter exists is that a workload can move up one while
43
+ * moving down the other. Picking one would be this tool making the choice it
44
+ * spent the last release refusing to make. Both are returned; the disagreement
45
+ * between them is itself a finding, and it is named.
46
+ */
47
+ import type { OutcomeTally, OutcomeVocabulary } from './outcome.js';
48
+ /**
49
+ * Recorded successes a slice needs before a per-outcome figure is stated.
50
+ *
51
+ * Ten, matching `history`'s `MIN_RUN` reasoning rather than a fresh number: a
52
+ * figure over fewer than ten observations moves more from one more observation
53
+ * than from anything a team could do about it.
54
+ */
55
+ export declare const MIN_OUTCOMES_FOR_RATE = 10;
56
+ /**
57
+ * Share of a slice's spend that must carry an outcome before a rate is stated.
58
+ *
59
+ * 0.8, the same floor `watch` uses for a measured day. Below it the figure is a
60
+ * ratio over an unknown denominator, and the gap between what it says and what
61
+ * is true is not bounded by anything the reader can see.
62
+ */
63
+ export declare const MIN_COVERAGE_FOR_RATE = 0.8;
64
+ export type WithheldReason = 'too-few-outcomes' | 'too-little-coverage' | 'no-successes-recorded' | 'no-vocabulary' | 'nothing-recorded';
65
+ export interface PerOutcome {
66
+ /**
67
+ * Dollars per recorded success, over **recorded spend only**, or null.
68
+ *
69
+ * Null is never a zero and never an infinity: `withheld` says which of the
70
+ * five reasons applies, and a caller that prints the figure without reading
71
+ * that field will print nothing rather than something wrong.
72
+ */
73
+ usdPerSuccess: number | null;
74
+ withheld: WithheldReason | null;
75
+ /** Successes counted. Shown in place of the rate when it is withheld. */
76
+ successes: number;
77
+ /** Spend on calls that carried a declared outcome — the numerator. */
78
+ recordedUsd: number;
79
+ /** Everything the slice spent, recorded or not — for the coverage share. */
80
+ totalUsd: number;
81
+ /**
82
+ * Recorded share of the slice's spend, 0-1. Printed beside the rate every
83
+ * time it is printed, because a ratio over a sample presented without its
84
+ * sample size is a claim about the whole.
85
+ */
86
+ coverage: number;
87
+ }
88
+ export declare function perOutcome(tally: OutcomeTally, totalUsd: number, vocabulary: OutcomeVocabulary | null): PerOutcome;
89
+ export interface RankedSlice {
90
+ key: string;
91
+ calls: number;
92
+ totalUsd: number;
93
+ per: PerOutcome;
94
+ /** Dollars per call — the ranking this product has always been able to make. */
95
+ usdPerCall: number;
96
+ }
97
+ export interface PerOutcomeRanking {
98
+ /** Dearest per call first. Every slice appears. */
99
+ byCall: RankedSlice[];
100
+ /**
101
+ * Dearest per **outcome** first. Only slices with a stated rate appear —
102
+ * a withheld figure has no position in an order, and giving it one would put
103
+ * a slice somewhere on the strength of a number this module declined to
104
+ * state.
105
+ */
106
+ byOutcome: RankedSlice[];
107
+ /**
108
+ * Slices whose two ranks disagree by more than a place, dearest-per-outcome
109
+ * first.
110
+ *
111
+ * **The finding a total cannot make**: the workload that looks cheap per
112
+ * call and is expensive per resolution, or the reverse. Somebody optimising
113
+ * on the first number has been moving the wrong one, and nothing in this
114
+ * product could tell them until now.
115
+ */
116
+ disagreements: Array<{
117
+ slice: RankedSlice;
118
+ callRank: number;
119
+ outcomeRank: number;
120
+ }>;
121
+ }
122
+ export interface SliceInput {
123
+ key: string;
124
+ calls: number;
125
+ totalUsd: number;
126
+ tally: OutcomeTally;
127
+ }
128
+ export declare function rankPerOutcome(slices: readonly SliceInput[], vocabulary: OutcomeVocabulary | null): PerOutcomeRanking;
129
+ //# sourceMappingURL=per-outcome.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"per-outcome.d.ts","sourceRoot":"","sources":["../src/per-outcome.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEpE;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC,MAAM,MAAM,cAAc,GACtB,kBAAkB,GAClB,qBAAqB,GACrB,uBAAuB,GACvB,eAAe,GACf,kBAAkB,CAAC;AAEvB,MAAM,WAAW,UAAU;IACzB;;;;;;OAMG;IACH,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;IAChC,yEAAyE;IACzE,SAAS,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,WAAW,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,UAAU,CACxB,KAAK,EAAE,YAAY,EACnB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,iBAAiB,GAAG,IAAI,GACnC,UAAU,CAyCZ;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,UAAU,CAAC;IAChB,gFAAgF;IAChF,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,mDAAmD;IACnD,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB;;;;;OAKG;IACH,SAAS,EAAE,WAAW,EAAE,CAAC;IACzB;;;;;;;;OAQG;IACH,aAAa,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,WAAW,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACrF;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,YAAY,CAAC;CACrB;AAED,wBAAgB,cAAc,CAC5B,MAAM,EAAE,SAAS,UAAU,EAAE,EAC7B,UAAU,EAAE,iBAAiB,GAAG,IAAI,GACnC,iBAAiB,CA+CnB"}
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Dollars per outcome — and the three ways this refuses to state one.
3
+ *
4
+ * 1.50.4 recorded the numerator. This divides by it, which sounds like
5
+ * arithmetic and is almost entirely a set of decisions about when *not* to do
6
+ * the arithmetic. A cost per resolution is the most quotable number this
7
+ * product will ever print — it ends up in a slide, in a quarterly review, in an
8
+ * argument about whether to keep a feature — and every way of getting it
9
+ * slightly wrong is a way of getting somebody's decision badly wrong.
10
+ *
11
+ * ## Which bill is the numerator
12
+ *
13
+ * The obvious implementation divides the **whole** slice bill by its successes.
14
+ * It is wrong, and wrong in the direction that makes a product look worse than
15
+ * it is: any call that carried no outcome is spend with no chance of appearing
16
+ * in the denominator, so the ratio is inflated by exactly the uninstrumented
17
+ * share — silently, and by an amount nobody can see from the figure.
18
+ *
19
+ * A team that instruments half its traffic would read a cost per resolution
20
+ * twice the real one, conclude the feature is uneconomic, and kill it.
21
+ *
22
+ * So the numerator is **recorded spend only**: the dollars on calls that
23
+ * carried an outcome. That makes it a ratio over a sample, which is fine and
24
+ * only fine because the coverage is stated beside it every single time, and
25
+ * because below a floor it is not stated at all.
26
+ *
27
+ * ## Three refusals
28
+ *
29
+ * 1. **Too few outcomes is not a rate.** Two resolutions and one figure is
30
+ * noise with a dollar sign. The count is shown instead — the same refusal
31
+ * `route` makes about small case sets and `history` makes about short runs.
32
+ * 2. **Too little coverage is not a rate.** A slice where an eighth of the
33
+ * spend carried an outcome yields a ratio over an unknown denominator. The
34
+ * coverage is shown instead.
35
+ * 3. **No successes recorded is not a rate of infinity.** A slice that spent
36
+ * money and resolved nothing is a real and alarming measurement, and it is
37
+ * reported as what it is rather than as a division by zero dressed up.
38
+ *
39
+ * ## Two rankings, and the product prints both
40
+ *
41
+ * Cheapest per call and cheapest per outcome are **different orders**, and the
42
+ * whole reason this chapter exists is that a workload can move up one while
43
+ * moving down the other. Picking one would be this tool making the choice it
44
+ * spent the last release refusing to make. Both are returned; the disagreement
45
+ * between them is itself a finding, and it is named.
46
+ */
47
+ import { judgeOutcome } from './outcome.js';
48
+ /**
49
+ * Recorded successes a slice needs before a per-outcome figure is stated.
50
+ *
51
+ * Ten, matching `history`'s `MIN_RUN` reasoning rather than a fresh number: a
52
+ * figure over fewer than ten observations moves more from one more observation
53
+ * than from anything a team could do about it.
54
+ */
55
+ export const MIN_OUTCOMES_FOR_RATE = 10;
56
+ /**
57
+ * Share of a slice's spend that must carry an outcome before a rate is stated.
58
+ *
59
+ * 0.8, the same floor `watch` uses for a measured day. Below it the figure is a
60
+ * ratio over an unknown denominator, and the gap between what it says and what
61
+ * is true is not bounded by anything the reader can see.
62
+ */
63
+ export const MIN_COVERAGE_FOR_RATE = 0.8;
64
+ export function perOutcome(tally, totalUsd, vocabulary) {
65
+ const declared = vocabulary ?? { values: [], success: [] };
66
+ let successes = 0;
67
+ let recordedUsd = 0;
68
+ for (const entry of tally.byValue) {
69
+ const verdict = judgeOutcome(entry.value, declared);
70
+ // Undeclared values are in neither the numerator nor the denominator, the
71
+ // same rule the success rate has: a typo in an exporter is a broken
72
+ // exporter, not a result.
73
+ if (verdict === 'undeclared')
74
+ continue;
75
+ recordedUsd += entry.usd;
76
+ if (verdict === 'success')
77
+ successes += entry.calls;
78
+ }
79
+ const coverage = totalUsd > 0 ? recordedUsd / totalUsd : 0;
80
+ const base = {
81
+ successes,
82
+ recordedUsd,
83
+ totalUsd,
84
+ coverage,
85
+ };
86
+ if (vocabulary === null || declared.success.length === 0) {
87
+ return { ...base, usdPerSuccess: null, withheld: 'no-vocabulary' };
88
+ }
89
+ if (tally.recorded === 0) {
90
+ return { ...base, usdPerSuccess: null, withheld: 'nothing-recorded' };
91
+ }
92
+ if (successes === 0) {
93
+ // Money spent and nothing resolved. A real and alarming measurement, and
94
+ // reported as one rather than as a division by zero dressed up as a figure.
95
+ return { ...base, usdPerSuccess: null, withheld: 'no-successes-recorded' };
96
+ }
97
+ if (successes < MIN_OUTCOMES_FOR_RATE) {
98
+ return { ...base, usdPerSuccess: null, withheld: 'too-few-outcomes' };
99
+ }
100
+ if (coverage < MIN_COVERAGE_FOR_RATE) {
101
+ return { ...base, usdPerSuccess: null, withheld: 'too-little-coverage' };
102
+ }
103
+ return { ...base, usdPerSuccess: recordedUsd / successes, withheld: null };
104
+ }
105
+ export function rankPerOutcome(slices, vocabulary) {
106
+ const ranked = slices.map((slice) => ({
107
+ key: slice.key,
108
+ calls: slice.calls,
109
+ totalUsd: slice.totalUsd,
110
+ per: perOutcome(slice.tally, slice.totalUsd, vocabulary),
111
+ usdPerCall: slice.calls > 0 ? slice.totalUsd / slice.calls : 0,
112
+ }));
113
+ const byCall = [...ranked].sort((a, b) => b.usdPerCall - a.usdPerCall);
114
+ const byOutcome = ranked
115
+ .filter((slice) => slice.per.usdPerSuccess !== null)
116
+ .sort((a, b) => b.per.usdPerSuccess - a.per.usdPerSuccess);
117
+ const callRankOf = new Map(byCall.map((slice, index) => [slice.key, index]));
118
+ const disagreements = [];
119
+ byOutcome.forEach((slice, outcomeRank) => {
120
+ /**
121
+ * Compared against this slice's position among **the rankable slices
122
+ * only**, not among all of them.
123
+ *
124
+ * Ranking it at position 4 of ten by call and 1 of three by outcome would
125
+ * report a disagreement produced entirely by the two lists having different
126
+ * lengths — an artefact, printed as a finding.
127
+ */
128
+ const callRank = byCall
129
+ .filter((other) => other.per.usdPerSuccess !== null)
130
+ .findIndex((other) => other.key === slice.key);
131
+ /**
132
+ * More than one place — **or** a change at the top.
133
+ *
134
+ * The distance threshold alone misses the sharpest case there is: with two
135
+ * rankable slices a complete reversal is a distance of exactly one, and
136
+ * that is not noise, it is the finding. Whoever is dearest per call and
137
+ * whoever is dearest per resolution are the two names in the conversation,
138
+ * and them being different names is the whole point of computing both.
139
+ */
140
+ const changedAtTheTop = (callRank === 0) !== (outcomeRank === 0);
141
+ if (Math.abs(callRank - outcomeRank) > 1 || changedAtTheTop) {
142
+ disagreements.push({ slice, callRank, outcomeRank });
143
+ }
144
+ });
145
+ // `callRankOf` is kept for callers that want the position among everything;
146
+ // the disagreement test deliberately does not use it, for the reason above.
147
+ void callRankOf;
148
+ return { byCall, byOutcome, disagreements };
149
+ }
150
+ //# sourceMappingURL=per-outcome.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"per-outcome.js","sourceRoot":"","sources":["../src/per-outcome.ts"],"names":[],"mappings":"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"}
package/dist/usage.d.ts CHANGED
@@ -447,6 +447,25 @@ export interface UsageProfileReport {
447
447
  * and a boolean would call it that.
448
448
  */
449
449
  fieldCoverage: FieldCoverage;
450
+ /**
451
+ * The same tally per label, and per model.
452
+ *
453
+ * The groupings a decision is made at. "This workload costs more per
454
+ * resolution than that one" is the finding a total cannot make, and it needs
455
+ * the numerator sliced the same way the bill already is.
456
+ */
457
+ outcomeTallyByLabel: Array<{
458
+ label: string;
459
+ calls: number;
460
+ totalUsd: number;
461
+ tally: OutcomeTally;
462
+ }>;
463
+ outcomeTallyByModel: Array<{
464
+ model: string;
465
+ calls: number;
466
+ totalUsd: number;
467
+ tally: OutcomeTally;
468
+ }>;
450
469
  /**
451
470
  * What each recorded outcome cost — measurement only, no judgement.
452
471
  *
@@ -1 +1 @@
1
- {"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../src/usage.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../src/usage.ts"],"names":[],"mappings":"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"}
package/dist/usage.js CHANGED
@@ -207,6 +207,23 @@ export function parseUsageLine(line) {
207
207
  })(),
208
208
  };
209
209
  }
210
+ /**
211
+ * One slice's outcome tally, in the same shape as the whole log's.
212
+ *
213
+ * `parsed` is the slice's own call count rather than the log's, so a coverage
214
+ * share computed from it describes this slice and not the file it came from.
215
+ */
216
+ function tallyOf(buckets, unrecordedUsd, parsed) {
217
+ const byValue = [...(buckets ?? new Map()).entries()]
218
+ .map(([value, bucket]) => ({ value, calls: bucket.calls, usd: bucket.usd }))
219
+ .sort((a, b) => b.usd - a.usd);
220
+ return {
221
+ byValue,
222
+ recorded: byValue.reduce((sum, entry) => sum + entry.calls, 0),
223
+ parsed,
224
+ unrecordedUsd,
225
+ };
226
+ }
210
227
  /**
211
228
  * A label or session identifier, from whatever a real log holds.
212
229
  *
@@ -355,6 +372,11 @@ export function profileUsage(text, options) {
355
372
  let hasSessions = false;
356
373
  const outcomes = new Map();
357
374
  let unrecordedOutcomeUsd = 0;
375
+ /** Per-slice outcome tallies, keyed by label and by model. */
376
+ const outcomesByLabel = new Map();
377
+ const outcomesByModel = new Map();
378
+ const unrecordedByLabel = new Map();
379
+ const unrecordedByModel = new Map();
358
380
  const coverage = { label: 0, session: 0, outcome: 0, ts: 0, stopReason: 0, cacheTtl: 0, cacheWrites: 0, parsed: 0 };
359
381
  /**
360
382
  * Raw lines already seen, for the duplicate check. Bounded by the number of
@@ -494,6 +516,20 @@ export function profileUsage(text, options) {
494
516
  bucket.usd += usd;
495
517
  outcomes.set(record.outcome, bucket);
496
518
  }
519
+ const into = (map, unrecorded, key) => {
520
+ if (record.outcome === null) {
521
+ unrecorded.set(key, (unrecorded.get(key) ?? 0) + usd);
522
+ return;
523
+ }
524
+ const slice = map.get(key) ?? new Map();
525
+ const bucket = slice.get(record.outcome) ?? { calls: 0, usd: 0 };
526
+ bucket.calls += 1;
527
+ bucket.usd += usd;
528
+ slice.set(record.outcome, bucket);
529
+ map.set(key, slice);
530
+ };
531
+ into(outcomesByLabel, unrecordedByLabel, record.label ?? UNLABELLED);
532
+ into(outcomesByModel, unrecordedByModel, record.model);
497
533
  }
498
534
  if (record.ts !== null) {
499
535
  const day = new Date(record.ts).toISOString().slice(0, 10);
@@ -589,6 +625,18 @@ export function profileUsage(text, options) {
589
625
  parsed: coverage.parsed,
590
626
  unrecordedUsd: unrecordedOutcomeUsd,
591
627
  },
628
+ outcomeTallyByLabel: sorted(byLabel, 'label').map((entry) => ({
629
+ label: entry.label,
630
+ calls: entry.breakdown.calls,
631
+ totalUsd: entry.breakdown.totalUsd,
632
+ tally: tallyOf(outcomesByLabel.get(entry.label), unrecordedByLabel.get(entry.label) ?? 0, entry.breakdown.calls),
633
+ })),
634
+ outcomeTallyByModel: sorted(byModel, 'model').map((entry) => ({
635
+ model: entry.model,
636
+ calls: entry.breakdown.calls,
637
+ totalUsd: entry.breakdown.totalUsd,
638
+ tally: tallyOf(outcomesByModel.get(entry.model), unrecordedByModel.get(entry.model) ?? 0, entry.breakdown.calls),
639
+ })),
592
640
  modelMixDrift: (() => {
593
641
  const ordered = [...days.entries()].sort((a, b) => a[0].localeCompare(b[0]));
594
642
  if (ordered.length < 4)