@trazum/core 1.80.1 → 1.82.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -18
- package/dist/advisories.d.ts +33 -1
- package/dist/advisories.d.ts.map +1 -1
- package/dist/advisories.js +103 -16
- package/dist/advisories.js.map +1 -1
- package/dist/band.d.ts +131 -0
- package/dist/band.d.ts.map +1 -0
- package/dist/band.js +210 -0
- package/dist/band.js.map +1 -0
- package/dist/claude-code.d.ts +28 -0
- package/dist/claude-code.d.ts.map +1 -1
- package/dist/claude-code.js +25 -1
- package/dist/claude-code.js.map +1 -1
- package/dist/conform.d.ts +1 -1
- package/dist/conform.d.ts.map +1 -1
- package/dist/conform.js +84 -1
- package/dist/conform.js.map +1 -1
- package/dist/helicone.d.ts +92 -0
- package/dist/helicone.d.ts.map +1 -0
- package/dist/helicone.js +194 -0
- package/dist/helicone.js.map +1 -0
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +26 -20
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/es.d.ts.map +1 -1
- package/dist/i18n/es.js +9 -3
- package/dist/i18n/es.js.map +1 -1
- package/dist/i18n/types.d.ts +43 -1
- package/dist/i18n/types.d.ts.map +1 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/litellm.d.ts +98 -0
- package/dist/litellm.d.ts.map +1 -0
- package/dist/litellm.js +207 -0
- package/dist/litellm.js.map +1 -0
- package/dist/measured-profile.d.ts +4 -0
- package/dist/measured-profile.d.ts.map +1 -1
- package/dist/measured-profile.js +22 -1
- package/dist/measured-profile.js.map +1 -1
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +36 -0
- package/dist/schemas.js.map +1 -1
- package/dist/tokenizer.d.ts +45 -49
- package/dist/tokenizer.d.ts.map +1 -1
- package/dist/tokenizer.js +100 -51
- package/dist/tokenizer.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/verdict-bridge.d.ts +113 -0
- package/dist/verdict-bridge.d.ts.map +1 -0
- package/dist/verdict-bridge.js +127 -0
- package/dist/verdict-bridge.js.map +1 -0
- package/package.json +1 -1
- package/src/advisories.ts +132 -15
- package/src/band.ts +213 -0
- package/src/claude-code.ts +53 -1
- package/src/conform.ts +100 -1
- package/src/helicone.ts +227 -0
- package/src/i18n/en.ts +27 -20
- package/src/i18n/es.ts +10 -3
- package/src/i18n/types.ts +45 -1
- package/src/index.ts +17 -1
- package/src/litellm.ts +243 -0
- package/src/measured-profile.ts +26 -2
- package/src/schemas.ts +36 -0
- package/src/tokenizer.ts +101 -51
- package/src/types.ts +1 -0
- package/src/verdict-bridge.ts +200 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading a dropped `route --json` verdict, so quality can stand beside cost.
|
|
3
|
+
*
|
|
4
|
+
* The bill is arithmetic: every figure in it comes from records already on
|
|
5
|
+
* disk, and it can say exactly what a cheaper model would have saved. It can
|
|
6
|
+
* say nothing at all about whether that model still does the job. That answer
|
|
7
|
+
* costs provider calls and a credential, which is why `trazum route` exists
|
|
8
|
+
* and why no surface that only reads a log will ever produce it.
|
|
9
|
+
*
|
|
10
|
+
* This is the bridge between the two, and it is deliberately one-directional:
|
|
11
|
+
* the measurement is made where the credential is, written out as the
|
|
12
|
+
* `routing-measurement` contract, and read back here by a pure function that
|
|
13
|
+
* touches no network and holds no key. A reader who has run `route` once can
|
|
14
|
+
* carry the verdict to any surface that prices the same workload.
|
|
15
|
+
*
|
|
16
|
+
* It was not possible to write until the document was worth reading. Before
|
|
17
|
+
* the JSON-text sweep, `route --json` printed the whole `EvalReport` — every
|
|
18
|
+
* case input and three model answers per case — so a bridge would have been a
|
|
19
|
+
* feature for carrying prompt text into a browser. The document carries the
|
|
20
|
+
* measurement and nothing else now, and `conform` is what says so rather than
|
|
21
|
+
* this file's own reading: the contract validates, the bridge only maps.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { conform } from './conform.js';
|
|
25
|
+
import type { EvalVerdict } from './evaluate.js';
|
|
26
|
+
import { UNLABELLED } from './usage.js';
|
|
27
|
+
|
|
28
|
+
/** What a dropped routing measurement says, mapped onto what a bill can match. */
|
|
29
|
+
export interface DroppedVerdict {
|
|
30
|
+
/** The workload the measurement was made on, or null when it was unlabelled. */
|
|
31
|
+
label: string | null;
|
|
32
|
+
/**
|
|
33
|
+
* The model those calls go to today, from the bill's own slice.
|
|
34
|
+
*
|
|
35
|
+
* Not the model that answered. `route` builds its baseline provider from
|
|
36
|
+
* the environment, so the model in the measurement is whatever
|
|
37
|
+
* `TRAZUM_LLM_MODEL` names, and it is only the log's model when the reader
|
|
38
|
+
* configured it that way. Pairing on it matched nothing on a real run
|
|
39
|
+
* against an OpenAI-compatible endpoint — `evaluation.model` said
|
|
40
|
+
* `stub-strong` while the log said `claude-opus-5` — and the bill would
|
|
41
|
+
* have shown no verdict at all with no explanation.
|
|
42
|
+
*/
|
|
43
|
+
model: string;
|
|
44
|
+
/**
|
|
45
|
+
* The model that actually answered, when it is not the one the log records.
|
|
46
|
+
*
|
|
47
|
+
* Null when they agree, which is the ordinary case. Not null is worth
|
|
48
|
+
* saying out loud rather than smoothing over: a verdict measured on a
|
|
49
|
+
* stand-in is a weaker claim about this workload than one measured on the
|
|
50
|
+
* model the workload actually uses, and only this field can tell the
|
|
51
|
+
* difference.
|
|
52
|
+
*/
|
|
53
|
+
measuredOn: string | null;
|
|
54
|
+
/** The model measured against it. */
|
|
55
|
+
candidateModel: string;
|
|
56
|
+
verdict: EvalVerdict;
|
|
57
|
+
/** The model's agreement with itself. The yardstick the other rate is read against. */
|
|
58
|
+
selfAgreement: number;
|
|
59
|
+
/** Agreement between the two models, same prompt on both sides. */
|
|
60
|
+
crossAgreement: number;
|
|
61
|
+
/** Cases the measurement covered, so a two-case verdict cannot pose as a hundred. */
|
|
62
|
+
cases: number;
|
|
63
|
+
/** Calls it cost to reach. */
|
|
64
|
+
callsMade: number;
|
|
65
|
+
/**
|
|
66
|
+
* What the measurement's own bill priced the route at, when the document
|
|
67
|
+
* carried it. Null rather than zero when it did not: a saving nobody
|
|
68
|
+
* measured is not a saving of nothing.
|
|
69
|
+
*/
|
|
70
|
+
savingUsd: number | null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* What the bridge did with a file.
|
|
75
|
+
*
|
|
76
|
+
* `null` is not a failure: it means the file is not a routing measurement at
|
|
77
|
+
* all, and the caller should go on treating it as whatever else it was. A
|
|
78
|
+
* refusal is the other case — it looked like one and could not be read — and
|
|
79
|
+
* it names what is missing, because a refusal with nothing after it is
|
|
80
|
+
* indistinguishable from a bug.
|
|
81
|
+
*/
|
|
82
|
+
export type BridgeReading =
|
|
83
|
+
| { kind: 'verdict'; verdict: DroppedVerdict }
|
|
84
|
+
| { kind: 'refusal'; because: string };
|
|
85
|
+
|
|
86
|
+
const isObject = (value: unknown): value is Record<string, unknown> =>
|
|
87
|
+
typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Whether this file is even claiming to be a routing measurement.
|
|
91
|
+
*
|
|
92
|
+
* Shape only, and deliberately narrow: an `evaluation` object carrying a
|
|
93
|
+
* `verdict`. Anything looser would swallow a usage log or a profile and
|
|
94
|
+
* refuse it in this file's words instead of letting the caller price it.
|
|
95
|
+
*/
|
|
96
|
+
function claimsToBeOne(parsed: unknown): boolean {
|
|
97
|
+
if (!isObject(parsed)) return false;
|
|
98
|
+
const evaluation = parsed.evaluation;
|
|
99
|
+
return isObject(evaluation) && typeof evaluation.verdict === 'string';
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Reads a dropped `trazum route --json` document.
|
|
104
|
+
*
|
|
105
|
+
* Returns null when the text is something else entirely. Validation is
|
|
106
|
+
* `conform`'s, against the published `routing-measurement` contract, so this
|
|
107
|
+
* function cannot drift from the schema a connector author builds against —
|
|
108
|
+
* and a document that fails carries the contract's own sentences back to the
|
|
109
|
+
* reader rather than a second opinion written here.
|
|
110
|
+
*/
|
|
111
|
+
export function readDroppedVerdict(text: string): BridgeReading | null {
|
|
112
|
+
let parsed: unknown;
|
|
113
|
+
try {
|
|
114
|
+
parsed = JSON.parse(text);
|
|
115
|
+
} catch {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
if (!claimsToBeOne(parsed)) return null;
|
|
119
|
+
|
|
120
|
+
const report = conform(text, { contract: 'routing-measurement' });
|
|
121
|
+
if (!report.conforms) {
|
|
122
|
+
const detail =
|
|
123
|
+
report.problems.length > 0
|
|
124
|
+
? report.problems.map((problem) => `${problem.at}: ${problem.detail}`).join('; ')
|
|
125
|
+
: (report.because ?? 'it does not match the routing-measurement contract');
|
|
126
|
+
return { kind: 'refusal', because: detail };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const document = parsed as { slice?: unknown; evaluation?: unknown };
|
|
130
|
+
const slice = isObject(document.slice) ? document.slice : {};
|
|
131
|
+
const evaluation = document.evaluation as Record<string, unknown>;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Every field below is read defensively even though `conform` has passed,
|
|
135
|
+
* because the contract requires `slice` and `evaluation` to be objects and
|
|
136
|
+
* says nothing about what is inside them. A document from a future version
|
|
137
|
+
* with a thinner slice is a document this reads what it can from, not one
|
|
138
|
+
* it crashes on.
|
|
139
|
+
*/
|
|
140
|
+
const answered = typeof evaluation.model === 'string' ? evaluation.model : null;
|
|
141
|
+
// The slice's model first: that is the fact the bill can be matched against.
|
|
142
|
+
const model = typeof slice.model === 'string' ? slice.model : answered;
|
|
143
|
+
const candidateModel =
|
|
144
|
+
typeof evaluation.candidateModel === 'string' ? evaluation.candidateModel : null;
|
|
145
|
+
const verdict = typeof evaluation.verdict === 'string' ? (evaluation.verdict as EvalVerdict) : null;
|
|
146
|
+
if (model === null || candidateModel === null || verdict === null) {
|
|
147
|
+
return {
|
|
148
|
+
kind: 'refusal',
|
|
149
|
+
because:
|
|
150
|
+
'the evaluation names no model, candidate model or verdict, so there is nothing to set beside a bill',
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const number = (value: unknown): number => (typeof value === 'number' ? value : 0);
|
|
155
|
+
const route = isObject(slice.route) ? slice.route : null;
|
|
156
|
+
const rawLabel = typeof slice.label === 'string' ? slice.label : null;
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
kind: 'verdict',
|
|
160
|
+
verdict: {
|
|
161
|
+
// `UNLABELLED` is the profile's own sentinel for calls that carry no
|
|
162
|
+
// label. It travels through the document as that sentinel and comes
|
|
163
|
+
// back as null here, so the bridge speaks the bill's language rather
|
|
164
|
+
// than leaking an internal marker into a caption.
|
|
165
|
+
label: rawLabel === null || rawLabel === UNLABELLED ? null : rawLabel,
|
|
166
|
+
model,
|
|
167
|
+
measuredOn: answered !== null && answered !== model ? answered : null,
|
|
168
|
+
candidateModel,
|
|
169
|
+
verdict,
|
|
170
|
+
selfAgreement: number(evaluation.selfAgreement),
|
|
171
|
+
crossAgreement: number(evaluation.crossAgreement),
|
|
172
|
+
cases: Array.isArray(evaluation.cases) ? evaluation.cases.length : 0,
|
|
173
|
+
callsMade: number(evaluation.callsMade),
|
|
174
|
+
savingUsd: route !== null && typeof route.savingUsd === 'number' ? route.savingUsd : null,
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Whether a verdict describes the route a given slice of the bill is offering.
|
|
181
|
+
*
|
|
182
|
+
* Both halves must agree or the pairing is a lie: the same workload, the same
|
|
183
|
+
* model it goes to now, and the same candidate. A verdict measured on `chat`
|
|
184
|
+
* shown against `summarise`'s saving would be the exact fault this repository
|
|
185
|
+
* keeps finding in itself — a number describing something other than what was
|
|
186
|
+
* measured — and matching on the model alone would produce it on any log with
|
|
187
|
+
* two workloads on one model.
|
|
188
|
+
*/
|
|
189
|
+
export function verdictMatchesSlice(
|
|
190
|
+
verdict: DroppedVerdict,
|
|
191
|
+
slice: { label: string; model: string; route: { candidate: { id: string } } | null },
|
|
192
|
+
): boolean {
|
|
193
|
+
if (slice.route === null) return false;
|
|
194
|
+
const sliceLabel = slice.label === UNLABELLED ? null : slice.label;
|
|
195
|
+
return (
|
|
196
|
+
sliceLabel === verdict.label &&
|
|
197
|
+
slice.model === verdict.model &&
|
|
198
|
+
slice.route.candidate.id === verdict.candidateModel
|
|
199
|
+
);
|
|
200
|
+
}
|