@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.
- package/README.md +12 -5
- package/dist/advisories.d.ts.map +1 -1
- package/dist/advisories.js +94 -4
- package/dist/advisories.js.map +1 -1
- package/dist/against.d.ts +32 -0
- package/dist/against.d.ts.map +1 -0
- package/dist/against.js +34 -0
- package/dist/against.js.map +1 -0
- package/dist/config-schema.d.ts +42 -1
- package/dist/config-schema.d.ts.map +1 -1
- package/dist/config-schema.js +61 -0
- package/dist/config-schema.js.map +1 -1
- package/dist/conversation.d.ts +121 -0
- package/dist/conversation.d.ts.map +1 -0
- package/dist/conversation.js +157 -0
- package/dist/conversation.js.map +1 -0
- package/dist/csv.d.ts +61 -0
- package/dist/csv.d.ts.map +1 -0
- package/dist/csv.js +149 -0
- package/dist/csv.js.map +1 -0
- package/dist/evaluate.d.ts +24 -0
- package/dist/evaluate.d.ts.map +1 -1
- package/dist/evaluate.js +5 -2
- package/dist/evaluate.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +18 -7
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/es.d.ts.map +1 -1
- package/dist/i18n/es.js +18 -7
- package/dist/i18n/es.js.map +1 -1
- package/dist/i18n/types.d.ts +28 -1
- package/dist/i18n/types.d.ts.map +1 -1
- package/dist/index.d.ts +25 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -1
- package/dist/input-shape.d.ts +104 -0
- package/dist/input-shape.d.ts.map +1 -0
- package/dist/input-shape.js +132 -0
- package/dist/input-shape.js.map +1 -0
- package/dist/levers.d.ts +151 -0
- package/dist/levers.d.ts.map +1 -0
- package/dist/levers.js +160 -0
- package/dist/levers.js.map +1 -0
- package/dist/node.d.ts +1 -1
- package/dist/node.d.ts.map +1 -1
- package/dist/output-shape.d.ts +96 -0
- package/dist/output-shape.d.ts.map +1 -0
- package/dist/output-shape.js +145 -0
- package/dist/output-shape.js.map +1 -0
- package/dist/pricing-overlay.d.ts +1 -1
- package/dist/pricing-overlay.d.ts.map +1 -1
- package/dist/pricing-overlay.js +46 -0
- package/dist/pricing-overlay.js.map +1 -1
- package/dist/repeats.d.ts +75 -0
- package/dist/repeats.d.ts.map +1 -0
- package/dist/repeats.js +82 -0
- package/dist/repeats.js.map +1 -0
- package/dist/reprice.d.ts +143 -0
- package/dist/reprice.d.ts.map +1 -0
- package/dist/reprice.js +82 -0
- package/dist/reprice.js.map +1 -0
- package/dist/session-cost.d.ts +70 -0
- package/dist/session-cost.d.ts.map +1 -0
- package/dist/session-cost.js +90 -0
- package/dist/session-cost.js.map +1 -0
- package/dist/session-ledger.d.ts +77 -0
- package/dist/session-ledger.d.ts.map +1 -0
- package/dist/session-ledger.js +99 -0
- package/dist/session-ledger.js.map +1 -0
- package/dist/tokenizer.d.ts +33 -26
- package/dist/tokenizer.d.ts.map +1 -1
- package/dist/tokenizer.js +95 -34
- package/dist/tokenizer.js.map +1 -1
- package/dist/ttl-fit.d.ts +103 -0
- package/dist/ttl-fit.d.ts.map +1 -0
- package/dist/ttl-fit.js +184 -0
- package/dist/ttl-fit.js.map +1 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/usage.d.ts +637 -0
- package/dist/usage.d.ts.map +1 -0
- package/dist/usage.js +634 -0
- package/dist/usage.js.map +1 -0
- package/package.json +1 -1
- package/src/advisories.ts +96 -4
- package/src/against.ts +48 -0
- package/src/config-schema.ts +106 -0
- package/src/conversation.ts +305 -0
- package/src/csv.ts +184 -0
- package/src/evaluate.ts +33 -3
- package/src/i18n/en.ts +19 -6
- package/src/i18n/es.ts +19 -6
- package/src/i18n/types.ts +29 -1
- package/src/index.ts +65 -1
- package/src/input-shape.ts +259 -0
- package/src/levers.ts +331 -0
- package/src/node.ts +1 -1
- package/src/output-shape.ts +254 -0
- package/src/pricing-overlay.ts +52 -1
- package/src/repeats.ts +166 -0
- package/src/reprice.ts +227 -0
- package/src/session-cost.ts +170 -0
- package/src/session-ledger.ts +189 -0
- package/src/tokenizer.ts +93 -30
- package/src/ttl-fit.ts +251 -0
- package/src/types.ts +1 -0
- package/src/usage.ts +1267 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { PricingCatalogue } from './pricing.js';
|
|
2
|
+
import type { UsageRecord } from './usage.js';
|
|
3
|
+
/**
|
|
4
|
+
* What re-sending the conversation costs.
|
|
5
|
+
*
|
|
6
|
+
* ## The line nothing was watching
|
|
7
|
+
*
|
|
8
|
+
* A chat or agent workload sends the whole conversation back on every turn. Turn
|
|
9
|
+
* one is a system prompt and a question; turn twenty is a system prompt and
|
|
10
|
+
* nineteen previous exchanges and a question. The input grows linearly with the
|
|
11
|
+
* turn count, and on an agent bill that growth is routinely the largest single
|
|
12
|
+
* line — larger than the prompt, larger than the answers.
|
|
13
|
+
*
|
|
14
|
+
* Nothing in this package could see it. A prompt file shows the system prompt and
|
|
15
|
+
* not the history. A total shows the sum and not the shape. Even `profile` reported
|
|
16
|
+
* "input is 71% of this bill" without being able to say that most of that input was
|
|
17
|
+
* the same sentences, sent again.
|
|
18
|
+
*
|
|
19
|
+
* ## What it will and will not claim
|
|
20
|
+
*
|
|
21
|
+
* The honest figure is a **ceiling**, and the token half of it is exact: the
|
|
22
|
+
* input tokens beyond every turn being the size of the session's smallest turn.
|
|
23
|
+
* That quantity is order-independent — the first-seen anchor made the identical
|
|
24
|
+
* workload vanish when exported newest-first — and it is immune to billing
|
|
25
|
+
* rates: a cost-based anchor charged an ordinary 5-minute-TTL agent 77.5%
|
|
26
|
+
* "growth" on a conversation that never grew, because a cache-miss turn costs
|
|
27
|
+
* 12.5x a cache-hit turn of the same size. The dollars are that token share of
|
|
28
|
+
* what the session actually spent, at its own blended rate.
|
|
29
|
+
*
|
|
30
|
+
* It is a ceiling and not a saving because part of that growth is the user's own
|
|
31
|
+
* new messages, which nobody can truncate away, and this module cannot tell those
|
|
32
|
+
* apart from re-sent history — it sees counts, not content. Reporting the ceiling
|
|
33
|
+
* as an opportunity would be the flattering direction; reporting nothing because
|
|
34
|
+
* the exact split is unknowable would be worse. So it reports the bound and says
|
|
35
|
+
* what it is.
|
|
36
|
+
*
|
|
37
|
+
* ## The session key never leaves this module
|
|
38
|
+
*
|
|
39
|
+
* A session identifier is somebody's conversation, and in a real log it is often an
|
|
40
|
+
* account id, a ticket number or an email. It is used to group calls and count
|
|
41
|
+
* turns; **no figure reported anywhere carries it**, and every result is aggregated
|
|
42
|
+
* per label. The promise that a usage log handed to Trazum contains no content is
|
|
43
|
+
* only worth something if nothing identifying comes back out either.
|
|
44
|
+
*/
|
|
45
|
+
/** How one label-and-model slice grows across a conversation. */
|
|
46
|
+
export interface ConversationGrowth {
|
|
47
|
+
label: string;
|
|
48
|
+
model: string;
|
|
49
|
+
modelName: string;
|
|
50
|
+
/** How many distinct conversations were seen. Never which ones. */
|
|
51
|
+
sessions: number;
|
|
52
|
+
calls: number;
|
|
53
|
+
/** Mean input tokens on the smallest turn of a conversation. */
|
|
54
|
+
minTurnTokens: number;
|
|
55
|
+
/** Mean input tokens on the largest turn. */
|
|
56
|
+
maxTurnTokens: number;
|
|
57
|
+
/** Turns in the longest conversation seen. */
|
|
58
|
+
longestSession: number;
|
|
59
|
+
/** Input-side spend: plain input, cache reads and cache writes. */
|
|
60
|
+
inputUsd: number;
|
|
61
|
+
/** What that would have been if every turn had cost what its cheapest turn did. */
|
|
62
|
+
flatUsd: number;
|
|
63
|
+
/**
|
|
64
|
+
* `inputUsd - flatUsd`. **A ceiling on what removing conversation growth could
|
|
65
|
+
* be worth, not a saving** — part of it is the user's own new messages.
|
|
66
|
+
*/
|
|
67
|
+
growthUsd: number;
|
|
68
|
+
/** `growthUsd` as a fraction of the whole bill in the log. */
|
|
69
|
+
shareOfBill: number;
|
|
70
|
+
}
|
|
71
|
+
export interface ConversationOptions {
|
|
72
|
+
catalogue: PricingCatalogue;
|
|
73
|
+
on?: Date;
|
|
74
|
+
/**
|
|
75
|
+
* Slices whose growth is below this share of the bill are dropped, and slices
|
|
76
|
+
* shorter than `minTurns` never count as conversations at all.
|
|
77
|
+
*/
|
|
78
|
+
minShare?: number;
|
|
79
|
+
/**
|
|
80
|
+
* Conversations shorter than this are ignored.
|
|
81
|
+
*
|
|
82
|
+
* Two turns is not a conversation, it is a retry — and a workload that never
|
|
83
|
+
* exceeds two turns has no growth to measure, so including it would put a row on
|
|
84
|
+
* screen whose figure is arithmetic noise. Default 3.
|
|
85
|
+
*/
|
|
86
|
+
minTurns?: number;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Measures what conversation growth costs, from records that carry a session.
|
|
90
|
+
*
|
|
91
|
+
* Records without one are skipped rather than lumped together: calls from
|
|
92
|
+
* different conversations pushed into a single bucket would report a turn count
|
|
93
|
+
* that is really a call count, and a growth figure derived from it would be
|
|
94
|
+
* arithmetic performed on a fiction.
|
|
95
|
+
*
|
|
96
|
+
* Takes records rather than a report because turn order is the whole measurement,
|
|
97
|
+
* and a breakdown has already thrown it away.
|
|
98
|
+
*/
|
|
99
|
+
export interface ConversationTracker {
|
|
100
|
+
/** Feed one parsed record. Records without a session are ignored. */
|
|
101
|
+
add(record: UsageRecord): void;
|
|
102
|
+
/** The finished measurement, once the whole bill is known. */
|
|
103
|
+
finish(totalUsd: number): ConversationGrowth[];
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* An accumulator, so a profile can measure this in the pass it already makes.
|
|
107
|
+
*
|
|
108
|
+
* The alternative was holding every record to hand to a pure function afterwards,
|
|
109
|
+
* and a usage log is measured in megabytes — a profile that needs the whole file in
|
|
110
|
+
* memory to answer one question is a profile that stops working on the logs most
|
|
111
|
+
* worth reading. What this holds is bounded by the number of **conversations**, and
|
|
112
|
+
* only ever four numbers each.
|
|
113
|
+
*/
|
|
114
|
+
export declare function createConversationTracker(options: ConversationOptions): ConversationTracker;
|
|
115
|
+
/**
|
|
116
|
+
* The same measurement over a list of records, for a caller holding one already.
|
|
117
|
+
*
|
|
118
|
+
* `profileUsage` uses the tracker instead, so it never has to keep the log.
|
|
119
|
+
*/
|
|
120
|
+
export declare function conversationGrowth(records: readonly UsageRecord[], totalUsd: number, options: ConversationOptions): ConversationGrowth[];
|
|
121
|
+
//# sourceMappingURL=conversation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../src/conversation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,iEAAiE;AACjE,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,aAAa,EAAE,MAAM,CAAC;IACtB,6CAA6C;IAC7C,aAAa,EAAE,MAAM,CAAC;IACtB,8CAA8C;IAC9C,cAAc,EAAE,MAAM,CAAC;IACvB,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC;IACjB,mFAAmF;IACnF,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,gBAAgB,CAAC;IAC5B,EAAE,CAAC,EAAE,IAAI,CAAC;IACV;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AA6CD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,mBAAmB;IAClC,qEAAqE;IACrE,GAAG,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC;IAC/B,8DAA8D;IAC9D,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAAC;CAChD;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,mBAAmB,GAAG,mBAAmB,CA8H3F;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,SAAS,WAAW,EAAE,EAC/B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,mBAAmB,GAC3B,kBAAkB,EAAE,CAItB"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { effectivePricing, multipliersFor } from './pricing.js';
|
|
2
|
+
import { UNLABELLED } from './usage.js';
|
|
3
|
+
/** Input-side cost of one call at its own model's rates. */
|
|
4
|
+
function inputCostOf(record, catalogue, on) {
|
|
5
|
+
const model = catalogue.byId.get(record.model);
|
|
6
|
+
if (!model)
|
|
7
|
+
return null;
|
|
8
|
+
const { inputPerMTok } = effectivePricing(model, on);
|
|
9
|
+
const rates = multipliersFor(model);
|
|
10
|
+
const per = (tokens, rate) => (tokens / 1_000_000) * rate;
|
|
11
|
+
return (per(record.inputTokens, inputPerMTok) +
|
|
12
|
+
per(record.cacheReadTokens, inputPerMTok * rates.cacheRead) +
|
|
13
|
+
per(record.cacheWrite5mTokens, inputPerMTok * rates.cacheWrite5m) +
|
|
14
|
+
per(record.cacheWrite1hTokens, inputPerMTok * rates.cacheWrite1h));
|
|
15
|
+
}
|
|
16
|
+
/** Every input-side token of one call, whatever rate it was billed at. */
|
|
17
|
+
const inputTokensOf = (r) => r.inputTokens + r.cacheReadTokens + r.cacheWrite5mTokens + r.cacheWrite1hTokens;
|
|
18
|
+
/**
|
|
19
|
+
* An accumulator, so a profile can measure this in the pass it already makes.
|
|
20
|
+
*
|
|
21
|
+
* The alternative was holding every record to hand to a pure function afterwards,
|
|
22
|
+
* and a usage log is measured in megabytes — a profile that needs the whole file in
|
|
23
|
+
* memory to answer one question is a profile that stops working on the logs most
|
|
24
|
+
* worth reading. What this holds is bounded by the number of **conversations**, and
|
|
25
|
+
* only ever four numbers each.
|
|
26
|
+
*/
|
|
27
|
+
export function createConversationTracker(options) {
|
|
28
|
+
const { catalogue, on = new Date(), minShare = 0.01, minTurns = 3 } = options;
|
|
29
|
+
// Keyed on the pair, then on the session inside it. A newline cannot occur in a
|
|
30
|
+
// model id, and both halves are trimmed strings.
|
|
31
|
+
const slices = new Map();
|
|
32
|
+
const add = (record) => {
|
|
33
|
+
if (record.session === null)
|
|
34
|
+
return;
|
|
35
|
+
const cost = inputCostOf(record, catalogue, on);
|
|
36
|
+
// An unpriced model contributes no dollars anywhere else either; including it
|
|
37
|
+
// here would report growth of zero on a workload that grew.
|
|
38
|
+
if (cost === null)
|
|
39
|
+
return;
|
|
40
|
+
const sliceKey = `${record.label ?? UNLABELLED}\n${record.model}`;
|
|
41
|
+
let sessions = slices.get(sliceKey);
|
|
42
|
+
if (!sessions) {
|
|
43
|
+
sessions = new Map();
|
|
44
|
+
slices.set(sliceKey, sessions);
|
|
45
|
+
}
|
|
46
|
+
const tokens = inputTokensOf(record);
|
|
47
|
+
const existing = sessions.get(record.session);
|
|
48
|
+
if (!existing) {
|
|
49
|
+
sessions.set(record.session, {
|
|
50
|
+
turns: 1,
|
|
51
|
+
minTokens: tokens,
|
|
52
|
+
maxTokens: tokens,
|
|
53
|
+
totalTokens: tokens,
|
|
54
|
+
totalUsd: cost,
|
|
55
|
+
});
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
existing.turns += 1;
|
|
59
|
+
existing.minTokens = Math.min(existing.minTokens, tokens);
|
|
60
|
+
existing.maxTokens = Math.max(existing.maxTokens, tokens);
|
|
61
|
+
existing.totalTokens += tokens;
|
|
62
|
+
existing.totalUsd += cost;
|
|
63
|
+
};
|
|
64
|
+
const finish = (totalUsd) => {
|
|
65
|
+
const out = [];
|
|
66
|
+
for (const [sliceKey, sessions] of slices) {
|
|
67
|
+
const split = sliceKey.indexOf('\n');
|
|
68
|
+
const label = sliceKey.slice(0, split);
|
|
69
|
+
const modelId = sliceKey.slice(split + 1);
|
|
70
|
+
const model = catalogue.byId.get(modelId);
|
|
71
|
+
if (!model)
|
|
72
|
+
continue;
|
|
73
|
+
/**
|
|
74
|
+
* Only conversations long enough to have grown. A two-turn session has one
|
|
75
|
+
* step of growth and is as likely to be a retry, and averaging it in drags the
|
|
76
|
+
* measured shape towards flat — understating the real thing, which is the
|
|
77
|
+
* direction that flatters.
|
|
78
|
+
*/
|
|
79
|
+
const long = [...sessions.values()].filter((s) => s.turns >= minTurns);
|
|
80
|
+
if (long.length === 0)
|
|
81
|
+
continue;
|
|
82
|
+
let inputUsd = 0;
|
|
83
|
+
let flatUsd = 0;
|
|
84
|
+
let minTokens = 0;
|
|
85
|
+
let maxTokens = 0;
|
|
86
|
+
let calls = 0;
|
|
87
|
+
let longestSession = 0;
|
|
88
|
+
for (const session of long) {
|
|
89
|
+
inputUsd += session.totalUsd;
|
|
90
|
+
/**
|
|
91
|
+
* The growth is measured in tokens — `totalTokens - minTokens·turns`,
|
|
92
|
+
* which is exact, order-independent, and zero for a conversation whose
|
|
93
|
+
* turns never change size however each one was billed. The money is that
|
|
94
|
+
* token share of what the session actually spent: pricing the excess at
|
|
95
|
+
* any single rate would either overstate it (full input rate, when most
|
|
96
|
+
* re-sent history is cache-read cheap) or move with billing noise (the
|
|
97
|
+
* cheapest turn's rate, which is what mis-billed a flat cached agent).
|
|
98
|
+
*/
|
|
99
|
+
const flatTokens = session.minTokens * session.turns;
|
|
100
|
+
flatUsd +=
|
|
101
|
+
session.totalTokens > 0
|
|
102
|
+
? session.totalUsd * (flatTokens / session.totalTokens)
|
|
103
|
+
: session.totalUsd;
|
|
104
|
+
minTokens += session.minTokens;
|
|
105
|
+
maxTokens += session.maxTokens;
|
|
106
|
+
calls += session.turns;
|
|
107
|
+
longestSession = Math.max(longestSession, session.turns);
|
|
108
|
+
}
|
|
109
|
+
const growthUsd = inputUsd - flatUsd;
|
|
110
|
+
const shareOfBill = totalUsd > 0 ? growthUsd / totalUsd : 0;
|
|
111
|
+
/**
|
|
112
|
+
* Below the attention threshold — **and that covers shrinking conversations
|
|
113
|
+
* too**, because a negative share is below any threshold at or above zero.
|
|
114
|
+
*
|
|
115
|
+
* There was a separate `growthUsd <= 0` check here. No mutation could break
|
|
116
|
+
* it: every case it caught, this one caught first. A guard nothing can
|
|
117
|
+
* distinguish is not defence in depth, it is a second place for the intent to
|
|
118
|
+
* drift from the code, so the intent lives in this comment instead.
|
|
119
|
+
*
|
|
120
|
+
* The case it was written for is ordinary: an opening turn carrying an
|
|
121
|
+
* attachment or a retrieved document is bigger than everything after it, and
|
|
122
|
+
* reporting that as "conversation growth" would be a negative ceiling
|
|
123
|
+
* presented as an opportunity.
|
|
124
|
+
*/
|
|
125
|
+
if (shareOfBill < minShare)
|
|
126
|
+
continue;
|
|
127
|
+
out.push({
|
|
128
|
+
label,
|
|
129
|
+
model: modelId,
|
|
130
|
+
modelName: model.displayName,
|
|
131
|
+
sessions: long.length,
|
|
132
|
+
calls,
|
|
133
|
+
minTurnTokens: minTokens / long.length,
|
|
134
|
+
maxTurnTokens: maxTokens / long.length,
|
|
135
|
+
longestSession,
|
|
136
|
+
inputUsd,
|
|
137
|
+
flatUsd,
|
|
138
|
+
growthUsd,
|
|
139
|
+
shareOfBill,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
return out.sort((a, b) => b.growthUsd - a.growthUsd);
|
|
143
|
+
};
|
|
144
|
+
return { add, finish };
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* The same measurement over a list of records, for a caller holding one already.
|
|
148
|
+
*
|
|
149
|
+
* `profileUsage` uses the tracker instead, so it never has to keep the log.
|
|
150
|
+
*/
|
|
151
|
+
export function conversationGrowth(records, totalUsd, options) {
|
|
152
|
+
const tracker = createConversationTracker(options);
|
|
153
|
+
for (const record of records)
|
|
154
|
+
tracker.add(record);
|
|
155
|
+
return tracker.finish(totalUsd);
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=conversation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation.js","sourceRoot":"","sources":["../src/conversation.ts"],"names":[],"mappings":"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"}
|
package/dist/csv.d.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { UsageProfileReport } from './usage.js';
|
|
2
|
+
/**
|
|
3
|
+
* The profile as a spreadsheet.
|
|
4
|
+
*
|
|
5
|
+
* ## Why a file format is a feature
|
|
6
|
+
*
|
|
7
|
+
* The terminal report is read once and closed. The people who decide what a
|
|
8
|
+
* workload is allowed to cost live in spreadsheets, and handing them a
|
|
9
|
+
* screenshot of a terminal is how a finding stops at the person who ran the
|
|
10
|
+
* command. `--json` is for machines; this is for the pivot table that gets
|
|
11
|
+
* shown to whoever signs off the bill.
|
|
12
|
+
*
|
|
13
|
+
* ## One row per label and model, and no total row
|
|
14
|
+
*
|
|
15
|
+
* `byLabelAndModel` is the grouping a decision is actually made at — routing
|
|
16
|
+
* `classify` to a cheaper model is a question about one label's calls to one
|
|
17
|
+
* model — so it is the grain of the file.
|
|
18
|
+
*
|
|
19
|
+
* **There is deliberately no TOTAL row.** A total inside a data file is the
|
|
20
|
+
* oldest spreadsheet trap there is: somebody sums the column, the total row is
|
|
21
|
+
* included, and every figure downstream is exactly twice what it should be.
|
|
22
|
+
* The sum of this file is the bill, and a spreadsheet can compute it.
|
|
23
|
+
*
|
|
24
|
+
* ## Unpriced models get empty cells, never zeros
|
|
25
|
+
*
|
|
26
|
+
* A model the catalogue does not know has real tokens and unknown dollars.
|
|
27
|
+
* Writing `0` there would be a claim — that those calls were free — and it
|
|
28
|
+
* would survive into every chart built on the file. An empty cell is the
|
|
29
|
+
* absence it actually is, and spreadsheets already know how to skip one.
|
|
30
|
+
*/
|
|
31
|
+
/** Columns, in order. Exported so a test can pin the header rather than a string. */
|
|
32
|
+
export declare const PROFILE_CSV_COLUMNS: readonly ['label', 'model', 'calls', 'input_tokens', 'cache_read_tokens', 'cache_write_tokens', 'output_tokens', 'input_usd', 'cache_read_usd', 'cache_write_usd', 'output_usd', 'total_usd'];
|
|
33
|
+
export interface ProfileCsvOptions {
|
|
34
|
+
/** What to call the bucket for calls carrying no label. */
|
|
35
|
+
unlabelled: string;
|
|
36
|
+
/**
|
|
37
|
+
* Which table to write.
|
|
38
|
+
*
|
|
39
|
+
* `slice` is one row per label and model — the grain a routing or budget
|
|
40
|
+
* decision is made at. `day` and `hour` are the time series, which is what
|
|
41
|
+
* a spreadsheet gets asked to chart; keeping them behind a choice rather
|
|
42
|
+
* than in extra columns means every file has one row shape, and a
|
|
43
|
+
* spreadsheet that has to filter before it can sum is a spreadsheet
|
|
44
|
+
* somebody sums wrong.
|
|
45
|
+
*/
|
|
46
|
+
shape?: ProfileCsvShape;
|
|
47
|
+
}
|
|
48
|
+
export type ProfileCsvShape = 'slice' | 'day' | 'hour';
|
|
49
|
+
/** Columns for the per-day series. */
|
|
50
|
+
export declare const PROFILE_CSV_DAY_COLUMNS: readonly ['day', 'usd', 'calls', 'top_label', 'top_label_usd'];
|
|
51
|
+
/** Columns for the per-hour-of-UTC-day series. */
|
|
52
|
+
export declare const PROFILE_CSV_HOUR_COLUMNS: readonly ['hour_utc', 'usd', 'calls'];
|
|
53
|
+
/**
|
|
54
|
+
* The report as CSV text, one row per label and model.
|
|
55
|
+
*
|
|
56
|
+
* Rows arrive in the report's own order — largest bill first — because a
|
|
57
|
+
* spreadsheet can re-sort and a reader opening the file should see the
|
|
58
|
+
* expensive workload at the top either way.
|
|
59
|
+
*/
|
|
60
|
+
export declare function profileToCsv(report: UsageProfileReport, options: ProfileCsvOptions): string;
|
|
61
|
+
//# sourceMappingURL=csv.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csv.d.ts","sourceRoot":"","sources":["../src/csv.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,qFAAqF;AACrF,eAAO,MAAM,mBAAmB,YAC9B,OAAO,EACP,OAAO,EACP,OAAO,EACP,cAAc,EACd,mBAAmB,EACnB,oBAAoB,EACpB,eAAe,EACf,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,EACZ,WAAW,CACH,CAAC;AAwBX,MAAM,WAAW,iBAAiB;IAChC,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;;;;OASG;IACH,KAAK,CAAC,EAAE,eAAe,CAAC;CACzB;AAED,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;AAEvD,sCAAsC;AACtC,eAAO,MAAM,uBAAuB,YAAI,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,eAAe,CAAU,CAAC;AAEtG,kDAAkD;AAClD,eAAO,MAAM,wBAAwB,YAAI,UAAU,EAAE,KAAK,EAAE,OAAO,CAAU,CAAC;AAE9E;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,kBAAkB,EAAE,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAiF3F"}
|
package/dist/csv.js
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { UNLABELLED } from './usage.js';
|
|
2
|
+
/**
|
|
3
|
+
* The profile as a spreadsheet.
|
|
4
|
+
*
|
|
5
|
+
* ## Why a file format is a feature
|
|
6
|
+
*
|
|
7
|
+
* The terminal report is read once and closed. The people who decide what a
|
|
8
|
+
* workload is allowed to cost live in spreadsheets, and handing them a
|
|
9
|
+
* screenshot of a terminal is how a finding stops at the person who ran the
|
|
10
|
+
* command. `--json` is for machines; this is for the pivot table that gets
|
|
11
|
+
* shown to whoever signs off the bill.
|
|
12
|
+
*
|
|
13
|
+
* ## One row per label and model, and no total row
|
|
14
|
+
*
|
|
15
|
+
* `byLabelAndModel` is the grouping a decision is actually made at — routing
|
|
16
|
+
* `classify` to a cheaper model is a question about one label's calls to one
|
|
17
|
+
* model — so it is the grain of the file.
|
|
18
|
+
*
|
|
19
|
+
* **There is deliberately no TOTAL row.** A total inside a data file is the
|
|
20
|
+
* oldest spreadsheet trap there is: somebody sums the column, the total row is
|
|
21
|
+
* included, and every figure downstream is exactly twice what it should be.
|
|
22
|
+
* The sum of this file is the bill, and a spreadsheet can compute it.
|
|
23
|
+
*
|
|
24
|
+
* ## Unpriced models get empty cells, never zeros
|
|
25
|
+
*
|
|
26
|
+
* A model the catalogue does not know has real tokens and unknown dollars.
|
|
27
|
+
* Writing `0` there would be a claim — that those calls were free — and it
|
|
28
|
+
* would survive into every chart built on the file. An empty cell is the
|
|
29
|
+
* absence it actually is, and spreadsheets already know how to skip one.
|
|
30
|
+
*/
|
|
31
|
+
/** Columns, in order. Exported so a test can pin the header rather than a string. */
|
|
32
|
+
export const PROFILE_CSV_COLUMNS = [
|
|
33
|
+
'label',
|
|
34
|
+
'model',
|
|
35
|
+
'calls',
|
|
36
|
+
'input_tokens',
|
|
37
|
+
'cache_read_tokens',
|
|
38
|
+
'cache_write_tokens',
|
|
39
|
+
'output_tokens',
|
|
40
|
+
'input_usd',
|
|
41
|
+
'cache_read_usd',
|
|
42
|
+
'cache_write_usd',
|
|
43
|
+
'output_usd',
|
|
44
|
+
'total_usd',
|
|
45
|
+
];
|
|
46
|
+
/**
|
|
47
|
+
* One CSV field, RFC 4180.
|
|
48
|
+
*
|
|
49
|
+
* Labels are arbitrary strings out of somebody's log: a label containing a
|
|
50
|
+
* comma would shift every column after it, and one containing a quote would
|
|
51
|
+
* break the row it sits in. Both are quoted here rather than sanitised,
|
|
52
|
+
* because changing the label would make the file disagree with every other
|
|
53
|
+
* rendering about what the workload is called.
|
|
54
|
+
*
|
|
55
|
+
* A leading `=`, `+`, `-` or `@` is prefixed with an apostrophe: those are
|
|
56
|
+
* how a spreadsheet is told a cell is a formula, and a label out of a log is
|
|
57
|
+
* data. This is the one place a value is altered, and it is altered to stop
|
|
58
|
+
* a log from executing anything when the file is opened.
|
|
59
|
+
*/
|
|
60
|
+
function field(value) {
|
|
61
|
+
const guarded = /^[=+\-@\t\r]/.test(value) ? `'${value}` : value;
|
|
62
|
+
return /[",\n\r]/.test(guarded) ? `"${guarded.replace(/"/g, '""')}"` : guarded;
|
|
63
|
+
}
|
|
64
|
+
/** A dollar figure with enough places to survive being summed. */
|
|
65
|
+
const usd = (value) => value.toFixed(6);
|
|
66
|
+
/** Columns for the per-day series. */
|
|
67
|
+
export const PROFILE_CSV_DAY_COLUMNS = ['day', 'usd', 'calls', 'top_label', 'top_label_usd'];
|
|
68
|
+
/** Columns for the per-hour-of-UTC-day series. */
|
|
69
|
+
export const PROFILE_CSV_HOUR_COLUMNS = ['hour_utc', 'usd', 'calls'];
|
|
70
|
+
/**
|
|
71
|
+
* The report as CSV text, one row per label and model.
|
|
72
|
+
*
|
|
73
|
+
* Rows arrive in the report's own order — largest bill first — because a
|
|
74
|
+
* spreadsheet can re-sort and a reader opening the file should see the
|
|
75
|
+
* expensive workload at the top either way.
|
|
76
|
+
*/
|
|
77
|
+
export function profileToCsv(report, options) {
|
|
78
|
+
if (options.shape === 'day') {
|
|
79
|
+
const rows = [PROFILE_CSV_DAY_COLUMNS.join(',')];
|
|
80
|
+
for (const day of report.spendByDay) {
|
|
81
|
+
rows.push([
|
|
82
|
+
day.day,
|
|
83
|
+
usd(day.usd),
|
|
84
|
+
String(day.calls),
|
|
85
|
+
// A day whose calls carried no label at all has no top label, and an
|
|
86
|
+
// empty cell is that absence. Naming the unlabelled bucket here
|
|
87
|
+
// would claim a label the log never carried.
|
|
88
|
+
day.topLabel === null
|
|
89
|
+
? ''
|
|
90
|
+
: field(day.topLabel === UNLABELLED ? options.unlabelled : day.topLabel),
|
|
91
|
+
day.topLabel === null ? '' : usd(day.topLabelUsd),
|
|
92
|
+
].join(','));
|
|
93
|
+
}
|
|
94
|
+
return `${rows.join('\n')}\n`;
|
|
95
|
+
}
|
|
96
|
+
if (options.shape === 'hour') {
|
|
97
|
+
const rows = [PROFILE_CSV_HOUR_COLUMNS.join(',')];
|
|
98
|
+
for (const hour of report.spendByHour) {
|
|
99
|
+
rows.push([String(hour.hour), usd(hour.usd), String(hour.calls)].join(','));
|
|
100
|
+
}
|
|
101
|
+
return `${rows.join('\n')}\n`;
|
|
102
|
+
}
|
|
103
|
+
const rows = [PROFILE_CSV_COLUMNS.join(',')];
|
|
104
|
+
for (const { label, model, breakdown } of report.byLabelAndModel) {
|
|
105
|
+
rows.push([
|
|
106
|
+
field(label === UNLABELLED ? options.unlabelled : label),
|
|
107
|
+
field(model),
|
|
108
|
+
String(breakdown.calls),
|
|
109
|
+
String(breakdown.inputTokens),
|
|
110
|
+
String(breakdown.cacheReadTokens),
|
|
111
|
+
String(breakdown.cacheWriteTokens),
|
|
112
|
+
String(breakdown.outputTokens),
|
|
113
|
+
usd(breakdown.inputUsd),
|
|
114
|
+
usd(breakdown.cacheReadUsd),
|
|
115
|
+
usd(breakdown.cacheWriteUsd),
|
|
116
|
+
usd(breakdown.outputUsd),
|
|
117
|
+
usd(breakdown.totalUsd),
|
|
118
|
+
].join(','));
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* The unpriced calls, with their tokens and no dollars.
|
|
122
|
+
*
|
|
123
|
+
* They are absent from `byLabelAndModel` — which holds what could be priced
|
|
124
|
+
* — and leaving them out of the file entirely would make its token columns
|
|
125
|
+
* disagree with the log. `byModel` keeps them, so they are recovered from
|
|
126
|
+
* there, with empty dollar cells rather than zeros.
|
|
127
|
+
*/
|
|
128
|
+
for (const model of report.unpricedModels) {
|
|
129
|
+
const row = report.byModel.find((entry) => entry.model === model);
|
|
130
|
+
if (!row)
|
|
131
|
+
continue;
|
|
132
|
+
rows.push([
|
|
133
|
+
field(options.unlabelled),
|
|
134
|
+
field(model),
|
|
135
|
+
String(row.breakdown.calls),
|
|
136
|
+
String(row.breakdown.inputTokens),
|
|
137
|
+
String(row.breakdown.cacheReadTokens),
|
|
138
|
+
String(row.breakdown.cacheWriteTokens),
|
|
139
|
+
String(row.breakdown.outputTokens),
|
|
140
|
+
'',
|
|
141
|
+
'',
|
|
142
|
+
'',
|
|
143
|
+
'',
|
|
144
|
+
'',
|
|
145
|
+
].join(','));
|
|
146
|
+
}
|
|
147
|
+
return `${rows.join('\n')}\n`;
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=csv.js.map
|
package/dist/csv.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csv.js","sourceRoot":"","sources":["../src/csv.ts"],"names":[],"mappings":"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"}
|
package/dist/evaluate.d.ts
CHANGED
|
@@ -35,6 +35,16 @@ export type EvalVerdict = 'indistinguishable' | 'within-noise' | 'diverges' | 'i
|
|
|
35
35
|
export interface EvalReport {
|
|
36
36
|
provider: string;
|
|
37
37
|
model: string;
|
|
38
|
+
/**
|
|
39
|
+
* The model the candidate answer came from.
|
|
40
|
+
*
|
|
41
|
+
* Equal to `model` on the ordinary comparison — same model, two prompts. It
|
|
42
|
+
* differs when the question is the other one: **same prompt, two models**, which
|
|
43
|
+
* is what a routing decision is. `profile` prices that route exactly and can say
|
|
44
|
+
* nothing at all about whether the cheaper model still does the job; this is the
|
|
45
|
+
* measurement that can.
|
|
46
|
+
*/
|
|
47
|
+
candidateModel: string;
|
|
38
48
|
cases: EvalCase[];
|
|
39
49
|
/** Mean agreement of the original prompt with itself. The yardstick. */
|
|
40
50
|
selfAgreement: number;
|
|
@@ -51,6 +61,20 @@ export interface EvaluateOptions {
|
|
|
51
61
|
* call already paid for.
|
|
52
62
|
*/
|
|
53
63
|
concurrency?: number;
|
|
64
|
+
/**
|
|
65
|
+
* Where the candidate answer comes from. Defaults to `provider`.
|
|
66
|
+
*
|
|
67
|
+
* This is the whole routing axis, and it needed no new yardstick. The baseline
|
|
68
|
+
* prompt is still run **twice on the original model** to measure that model's own
|
|
69
|
+
* variance, and the candidate is still judged against it — so the question
|
|
70
|
+
* becomes "does the cheaper model agree with the expensive one more closely than
|
|
71
|
+
* the expensive one agrees with itself?", which is the honest form of "is this
|
|
72
|
+
* route safe".
|
|
73
|
+
*
|
|
74
|
+
* A verdict built any other way would be a threshold somebody picked. This one is
|
|
75
|
+
* the model's own noise floor, measured on the same cases in the same run.
|
|
76
|
+
*/
|
|
77
|
+
candidateProvider?: LlmProvider;
|
|
54
78
|
}
|
|
55
79
|
/**
|
|
56
80
|
* Builds the prompt for one case.
|
package/dist/evaluate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evaluate.d.ts","sourceRoot":"","sources":["../src/evaluate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,WAAW,QAAQ;IACvB,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3B,qCAAqC;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,cAAc,EAAE,MAAM,CAAC;IACvB,2EAA2E;IAC3E,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,WAAW,GAAG,mBAAmB,GAAG,cAAc,GAAG,UAAU,GAAG,cAAc,CAAC;AAE7F,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,wEAAwE;IACxE,aAAa,EAAE,MAAM,CAAC;IACtB,oEAAoE;IACpE,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,WAAW,CAAC;IACrB,kEAAkE;IAClE,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"evaluate.d.ts","sourceRoot":"","sources":["../src/evaluate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,WAAW,QAAQ;IACvB,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3B,qCAAqC;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,cAAc,EAAE,MAAM,CAAC;IACvB,2EAA2E;IAC3E,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,WAAW,GAAG,mBAAmB,GAAG,cAAc,GAAG,UAAU,GAAG,cAAc,CAAC;AAE7F,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;;OAQG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,wEAAwE;IACxE,aAAa,EAAE,MAAM,CAAC;IACtB,oEAAoE;IACpE,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,WAAW,CAAC;IACrB,kEAAkE;IAClE,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;;;;;;OAYG;IACH,iBAAiB,CAAC,EAAE,WAAW,CAAC;CACjC;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAMhE;AAED,0CAA0C;AAC1C;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAKtD;AAKD;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,WAAW,CAOrF;AAED,sEAAsE;AACtE,wBAAsB,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAc3F;AAED;;;;;;;GAOG;AACH,wBAAsB,QAAQ,CAC5B,cAAc,EAAE,MAAM,EACtB,eAAe,EAAE,MAAM,EACvB,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,QAAQ,EAAE,WAAW,EACrB,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,UAAU,CAAC,CA4CrB"}
|
package/dist/evaluate.js
CHANGED
|
@@ -79,14 +79,16 @@ export async function pooled(tasks, limit) {
|
|
|
79
79
|
*/
|
|
80
80
|
export async function evaluate(originalPrompt, optimizedPrompt, inputs, provider, options = {}) {
|
|
81
81
|
const concurrency = Math.max(1, options.concurrency ?? 3);
|
|
82
|
-
const
|
|
82
|
+
const candidate = options.candidateProvider ?? provider;
|
|
83
|
+
const run = (prompt, input, on = provider) => on.complete({ system: fillPrompt(prompt, input), user: input });
|
|
83
84
|
const cases = await pooled(inputs.map((input) => async () => {
|
|
84
85
|
// Sequential within a case: the two baseline runs exist to measure the
|
|
85
86
|
// model's variance, and issuing them together invites a provider to
|
|
86
87
|
// serve one from a cache and report a variance of zero.
|
|
87
88
|
const baselineA = await run(originalPrompt, input);
|
|
88
89
|
const baselineB = await run(originalPrompt, input);
|
|
89
|
-
|
|
90
|
+
// On `candidate`, which is `provider` unless a route is being measured.
|
|
91
|
+
const optimized = await run(optimizedPrompt, input, candidate);
|
|
90
92
|
return {
|
|
91
93
|
input,
|
|
92
94
|
baseline: [baselineA, baselineB],
|
|
@@ -100,6 +102,7 @@ export async function evaluate(originalPrompt, optimizedPrompt, inputs, provider
|
|
|
100
102
|
return {
|
|
101
103
|
provider: provider.name,
|
|
102
104
|
model: provider.model,
|
|
105
|
+
candidateModel: candidate.model,
|
|
103
106
|
cases,
|
|
104
107
|
selfAgreement,
|
|
105
108
|
crossAgreement,
|
package/dist/evaluate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evaluate.js","sourceRoot":"","sources":["../src/evaluate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"evaluate.js","sourceRoot":"","sources":["../src/evaluate.ts"],"names":[],"mappings":"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"}
|
package/dist/i18n/en.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../src/i18n/en.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAI/C,iFAAiF;AACjF,eAAO,MAAM,EAAE,EAAE,
|
|
1
|
+
{"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../src/i18n/en.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAI/C,iFAAiF;AACjF,eAAO,MAAM,EAAE,EAAE,YAuPhB,CAAC"}
|