@rulvar/openai 1.124.0 → 1.126.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +25 -3
- package/dist/index.js +68 -22
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -200,6 +200,20 @@ interface ReconcileStatementOptions {
|
|
|
200
200
|
totalToleranceUsd?: number;
|
|
201
201
|
/** Provider-side model name of a served ref; default strips the adapter prefix. */
|
|
202
202
|
modelOf?: (servedBy: ModelRef) => string;
|
|
203
|
+
/**
|
|
204
|
+
* How provider-reported token counts weigh on the verdict (RV903).
|
|
205
|
+
* 'verdict' (default): any token disagreement between the export and
|
|
206
|
+
* our recorded usage is a divergence, because our counts ARE the
|
|
207
|
+
* provider's own wire-reported numbers, so an export that disagrees
|
|
208
|
+
* with them describes a different request than the wire served, and
|
|
209
|
+
* dollars derived from either cannot be trusted to mean the same
|
|
210
|
+
* thing. 'informational' preserves the pre-v1.126 dollar-only
|
|
211
|
+
* verdict for exports whose token semantics legitimately differ from
|
|
212
|
+
* the wire's (a different cache accounting, rounded aggregates):
|
|
213
|
+
* mismatches are still counted and sampled, but only dollar deltas
|
|
214
|
+
* decide.
|
|
215
|
+
*/
|
|
216
|
+
tokenComparison?: "verdict" | "informational";
|
|
203
217
|
}
|
|
204
218
|
/** One (model, component) line of the reconciliation. */
|
|
205
219
|
interface ComponentDelta {
|
|
@@ -244,7 +258,12 @@ interface StatementReconciliation {
|
|
|
244
258
|
components: ComponentDelta[];
|
|
245
259
|
/** The lines beyond tolerance, largest |delta| first: the named divergences. */
|
|
246
260
|
divergent: ComponentDelta[];
|
|
247
|
-
/**
|
|
261
|
+
/**
|
|
262
|
+
* Token disagreements between the export and our recorded usage
|
|
263
|
+
* (requests mode). Under the default tokenComparison 'verdict' any
|
|
264
|
+
* mismatch makes the verdict 'divergence'; under 'informational' the
|
|
265
|
+
* count and sample still report, advisory only (RV903).
|
|
266
|
+
*/
|
|
248
267
|
tokenMismatches: number;
|
|
249
268
|
tokenMismatchSample: Array<{
|
|
250
269
|
responseId: string;
|
|
@@ -264,8 +283,11 @@ interface StatementReconciliation {
|
|
|
264
283
|
* journal-free; see the module doc for the contract. Throws a typed
|
|
265
284
|
* ConfigError on inputs that cannot be evidence: an empty statement (a
|
|
266
285
|
* headline total with no rows), a request row without a response id, a
|
|
267
|
-
* duplicate response id (an ambiguous join),
|
|
268
|
-
* rows carry neither dollars, components, nor usage
|
|
286
|
+
* duplicate response id (an ambiguous join), a request export whose
|
|
287
|
+
* rows carry neither dollars, components, nor usage, any non-finite or
|
|
288
|
+
* negative dollar amount, any non-integer or negative token count, or
|
|
289
|
+
* a non-finite or negative tolerance (RV903: a statement that cannot
|
|
290
|
+
* be summed must refuse loudly, never verdict 'match' on NaN totals).
|
|
269
291
|
*/
|
|
270
292
|
declare function reconcileStatement(invoice: {
|
|
271
293
|
rows: readonly InvoiceRow[];
|
package/dist/index.js
CHANGED
|
@@ -32,13 +32,19 @@ const GPT_56_TIERS = [{
|
|
|
32
32
|
/**
|
|
33
33
|
* GPT-5.6 Sol, Terra, and Luna are three sibling models, not snapshots
|
|
34
34
|
* of one model (developers.openai.com/api/docs/models/gpt-5.6-sol,
|
|
35
|
-
* .../gpt-5.6-terra, .../gpt-5.6-luna; rates
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
35
|
+
* .../gpt-5.6-terra, .../gpt-5.6-luna; the weekly rates audit
|
|
36
|
+
* re-verifies the three pages). The family rows carry ratesVerifiedAt
|
|
37
|
+
* '2026-07-31' (RV814): on that date every row was re-verified against
|
|
38
|
+
* its documented model page, picking up the provider's 2026-07-30
|
|
39
|
+
* price cut on Terra (0.8x across input, cached input, cache write,
|
|
40
|
+
* and output) and Luna (0.2x across the same four) that the
|
|
41
|
+
* thirteenth-experiment live audit caught as drift (RV911). Sol's
|
|
42
|
+
* rates are unchanged and additionally remain billing-CONFIRMED by the
|
|
43
|
+
* 2026-07-30 twelfth-experiment statement reconciliation, which
|
|
44
|
+
* matched all eight per-model per-component dashboard categories to
|
|
45
|
+
* the cent; the new Terra and Luna rates are docs-verified only, and
|
|
46
|
+
* their billing truth is established by the next statement
|
|
47
|
+
* reconciliation over saved exports, never assumed. All
|
|
42
48
|
* three: prompts strictly above 272K input tokens price the FULL
|
|
43
49
|
* request at 2x input and 1.5x output; cache writes bill at 1.25x
|
|
44
50
|
* uncached input. All three accept wire reasoning effort `max`
|
|
@@ -55,26 +61,26 @@ const GPT_56_SOL = responses(105e4, 128e3, {
|
|
|
55
61
|
cacheReadUsdPerMTok: .5,
|
|
56
62
|
cacheWriteUsdPerMTok: 6.25,
|
|
57
63
|
tiers: GPT_56_TIERS,
|
|
58
|
-
ratesVerifiedAt: "2026-07-
|
|
64
|
+
ratesVerifiedAt: "2026-07-31"
|
|
59
65
|
}, { wireMaxEffort: true });
|
|
60
66
|
/** Static seed table of the current model set. */
|
|
61
67
|
const OPENAI_MODELS = {
|
|
62
68
|
"gpt-5.6-sol": GPT_56_SOL,
|
|
63
69
|
"gpt-5.6-terra": responses(105e4, 128e3, {
|
|
64
|
-
inputUsdPerMTok: 2
|
|
65
|
-
outputUsdPerMTok:
|
|
66
|
-
cacheReadUsdPerMTok: .
|
|
67
|
-
cacheWriteUsdPerMTok:
|
|
70
|
+
inputUsdPerMTok: 2,
|
|
71
|
+
outputUsdPerMTok: 12,
|
|
72
|
+
cacheReadUsdPerMTok: .2,
|
|
73
|
+
cacheWriteUsdPerMTok: 2.5,
|
|
68
74
|
tiers: GPT_56_TIERS,
|
|
69
|
-
ratesVerifiedAt: "2026-07-
|
|
75
|
+
ratesVerifiedAt: "2026-07-31"
|
|
70
76
|
}, { wireMaxEffort: true }),
|
|
71
77
|
"gpt-5.6-luna": responses(105e4, 128e3, {
|
|
72
|
-
inputUsdPerMTok:
|
|
73
|
-
outputUsdPerMTok:
|
|
74
|
-
cacheReadUsdPerMTok: .
|
|
75
|
-
cacheWriteUsdPerMTok:
|
|
78
|
+
inputUsdPerMTok: .2,
|
|
79
|
+
outputUsdPerMTok: 1.2,
|
|
80
|
+
cacheReadUsdPerMTok: .02,
|
|
81
|
+
cacheWriteUsdPerMTok: .25,
|
|
76
82
|
tiers: GPT_56_TIERS,
|
|
77
|
-
ratesVerifiedAt: "2026-07-
|
|
83
|
+
ratesVerifiedAt: "2026-07-31"
|
|
78
84
|
}, { wireMaxEffort: true }),
|
|
79
85
|
"gpt-5.6": GPT_56_SOL,
|
|
80
86
|
"gpt-5.5": responses(4e5, 128e3, {
|
|
@@ -121,7 +127,7 @@ const OPENAI_MODELS = {
|
|
|
121
127
|
* silent reinterpretation.
|
|
122
128
|
*/
|
|
123
129
|
const OPENAI_PRICING = {
|
|
124
|
-
pricingVersion: "openai-2026-07-
|
|
130
|
+
pricingVersion: "openai-2026-07-31",
|
|
125
131
|
models: (() => {
|
|
126
132
|
const models = {};
|
|
127
133
|
for (const [name, info] of Object.entries(OPENAI_MODELS)) if (info.caps.pricing !== void 0) models[`openai:${name}`] = info.caps.pricing;
|
|
@@ -1078,17 +1084,40 @@ const defaultModelOf = (servedBy) => {
|
|
|
1078
1084
|
return colon === -1 ? servedBy : servedBy.slice(colon + 1);
|
|
1079
1085
|
};
|
|
1080
1086
|
/**
|
|
1087
|
+
* A statement dollar amount must be a finite nonnegative number
|
|
1088
|
+
* (RV903). The thirteenth experiment's probe fed `usd: NaN` and got
|
|
1089
|
+
* verdict 'match' with NaN totals: NaN flowed through the sums and
|
|
1090
|
+
* `Math.abs(NaN) > tolerance` is false, so the divergence check
|
|
1091
|
+
* silently disarmed. Negative amounts are refused too: provider
|
|
1092
|
+
* credits and adjustments are real, but they are not per-request or
|
|
1093
|
+
* per-component BILLING evidence, and folding them into the join would
|
|
1094
|
+
* let an adjustment mask a rate divergence of the same size.
|
|
1095
|
+
*/
|
|
1096
|
+
function assertStatementUsd(where, field, value) {
|
|
1097
|
+
if (!Number.isFinite(value)) throw new ConfigError(`statement reconciliation refused: ${where} carries ${field} ${String(value)}, which cannot be summed; a statement whose dollars are not finite is not evidence`);
|
|
1098
|
+
if (value < 0) throw new ConfigError(`statement reconciliation refused: ${where} carries negative ${field} ${String(value)}; credits and adjustments reconcile separately, never as negative statement rows`);
|
|
1099
|
+
}
|
|
1100
|
+
/** A provider-reported token count must be a nonnegative integer (RV903). */
|
|
1101
|
+
function assertTokenCount(where, field, value) {
|
|
1102
|
+
if (!Number.isInteger(value) || value < 0) throw new ConfigError(`statement reconciliation refused: ${where} carries ${field} ${String(value)}; provider-reported token counts are nonnegative integers`);
|
|
1103
|
+
}
|
|
1104
|
+
/**
|
|
1081
1105
|
* Reconciles the invoice against a normalized provider export. Pure and
|
|
1082
1106
|
* journal-free; see the module doc for the contract. Throws a typed
|
|
1083
1107
|
* ConfigError on inputs that cannot be evidence: an empty statement (a
|
|
1084
1108
|
* headline total with no rows), a request row without a response id, a
|
|
1085
|
-
* duplicate response id (an ambiguous join),
|
|
1086
|
-
* rows carry neither dollars, components, nor usage
|
|
1109
|
+
* duplicate response id (an ambiguous join), a request export whose
|
|
1110
|
+
* rows carry neither dollars, components, nor usage, any non-finite or
|
|
1111
|
+
* negative dollar amount, any non-integer or negative token count, or
|
|
1112
|
+
* a non-finite or negative tolerance (RV903: a statement that cannot
|
|
1113
|
+
* be summed must refuse loudly, never verdict 'match' on NaN totals).
|
|
1087
1114
|
*/
|
|
1088
1115
|
function reconcileStatement(invoice, statement, options) {
|
|
1116
|
+
for (const [name, value] of [["componentToleranceUsd", options.componentToleranceUsd], ["totalToleranceUsd", options.totalToleranceUsd]]) if (value !== void 0 && (!Number.isFinite(value) || value < 0)) throw new ConfigError(`statement reconciliation refused: ${name} ${String(value)} is not a finite nonnegative dollar tolerance`);
|
|
1089
1117
|
const componentToleranceUsd = options.componentToleranceUsd ?? .005;
|
|
1090
1118
|
const totalToleranceUsd = options.totalToleranceUsd ?? .01;
|
|
1091
1119
|
const modelOf = options.modelOf ?? defaultModelOf;
|
|
1120
|
+
const tokenComparison = options.tokenComparison ?? "verdict";
|
|
1092
1121
|
if (statement.rows.length === 0) throw new ConfigError("statement reconciliation refused: the statement carries no rows. A headline total is not evidence (dashboard aggregates are eventually consistent); export per-request rows or per-component categories and reconcile those");
|
|
1093
1122
|
const billable = [];
|
|
1094
1123
|
let usageUnknownRows = 0;
|
|
@@ -1116,6 +1145,21 @@ function reconcileStatement(invoice, statement, options) {
|
|
|
1116
1145
|
for (const row of statement.rows) {
|
|
1117
1146
|
if (row.responseId === "") throw new ConfigError("statement reconciliation refused: a per-request export row has no response id, the join key; normalize the export or reconcile per-component categories instead");
|
|
1118
1147
|
if (byId.has(row.responseId)) throw new ConfigError(`statement reconciliation refused: duplicate response id '${row.responseId}' in the export makes the join ambiguous`);
|
|
1148
|
+
const where = `row '${row.responseId}'`;
|
|
1149
|
+
if (row.usd !== void 0) assertStatementUsd(where, "usd", row.usd);
|
|
1150
|
+
if (row.componentsUsd !== void 0) for (const component of COMPONENTS) {
|
|
1151
|
+
const usd = row.componentsUsd[component];
|
|
1152
|
+
if (usd !== void 0) assertStatementUsd(where, `componentsUsd.${component}`, usd);
|
|
1153
|
+
}
|
|
1154
|
+
if (row.usage !== void 0) for (const field of [
|
|
1155
|
+
"inputTokens",
|
|
1156
|
+
"cachedInputTokens",
|
|
1157
|
+
"cacheWriteTokens",
|
|
1158
|
+
"outputTokens"
|
|
1159
|
+
]) {
|
|
1160
|
+
const count = row.usage[field];
|
|
1161
|
+
if (count !== void 0) assertTokenCount(where, `usage.${field}`, count);
|
|
1162
|
+
}
|
|
1119
1163
|
byId.set(row.responseId, row);
|
|
1120
1164
|
if (row.usd !== void 0 || row.componentsUsd !== void 0 || row.usage !== void 0) carriesAnything = true;
|
|
1121
1165
|
}
|
|
@@ -1196,6 +1240,7 @@ function reconcileStatement(invoice, statement, options) {
|
|
|
1196
1240
|
statementComponents = /* @__PURE__ */ new Map();
|
|
1197
1241
|
let total = 0;
|
|
1198
1242
|
for (const row of statement.rows) {
|
|
1243
|
+
assertStatementUsd(`category row '${row.model}' ${row.component}`, "usd", row.usd);
|
|
1199
1244
|
const sums = statementComponents.get(row.model) ?? {};
|
|
1200
1245
|
sums[row.component] = (sums[row.component] ?? 0) + row.usd;
|
|
1201
1246
|
statementComponents.set(row.model, sums);
|
|
@@ -1273,8 +1318,9 @@ function reconcileStatement(invoice, statement, options) {
|
|
|
1273
1318
|
const totalsDelta = statementTotalUsd === void 0 ? void 0 : statementTotalUsd - ourUsd;
|
|
1274
1319
|
const totalsDivergent = statementComponents === void 0 && totalsDelta !== void 0 && Math.abs(totalsDelta) > totalToleranceUsd;
|
|
1275
1320
|
const coverageComplete = unmatchedRows === 0 && statementOnlyRows === 0 && unpricedModels.size === 0 && (statement.kind === "categories" || matchedRows === rowsWithResponseId && rowsWithResponseId === billable.length) && (statement.kind === "requests" || components.every((line) => line.statementUsd !== void 0));
|
|
1321
|
+
const tokensDivergent = tokenComparison === "verdict" && tokenMismatches > 0;
|
|
1276
1322
|
let verdict;
|
|
1277
|
-
if (divergent.length > 0 || totalsDivergent) verdict = "divergence";
|
|
1323
|
+
if (divergent.length > 0 || totalsDivergent || tokensDivergent) verdict = "divergence";
|
|
1278
1324
|
else if (matchedRows === 0) verdict = "no-overlap";
|
|
1279
1325
|
else if (!coverageComplete) verdict = "partial-coverage";
|
|
1280
1326
|
else verdict = "match";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rulvar/openai",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.126.0",
|
|
4
4
|
"description": "Rulvar first-class provider adapter for the OpenAI Responses API, plus the openaiCompatible factory.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -23,13 +23,13 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"openai": "^6.49.0",
|
|
26
|
-
"@rulvar/core": "1.
|
|
26
|
+
"@rulvar/core": "1.126.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/node": "^22.20.1",
|
|
30
30
|
"tsdown": "^0.22.14",
|
|
31
31
|
"typescript": "~6.0.3",
|
|
32
|
-
"@rulvar/testing": "1.
|
|
32
|
+
"@rulvar/testing": "1.126.0"
|
|
33
33
|
},
|
|
34
34
|
"repository": {
|
|
35
35
|
"type": "git",
|