@rulvar/openai 1.179.0 → 1.180.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 CHANGED
@@ -1,5 +1,5 @@
1
1
  import OpenAI, { ClientOptions } from "openai";
2
- import { CanonicalId, ChatEvent, ChatRequest, Effort, InvoiceRow, JournalEntry, ModelCaps, ModelRef, PriceTable, Pricing, ProviderAdapter, Usage, WireError } from "@rulvar/core";
2
+ import { BillingComponent, CanonicalId, ChatEvent, ChatRequest, ComponentDelta, Effort, JournalEntry, ModelCaps, ModelRef, PriceTable, ProviderAdapter, ProviderStatement, ReconcileStatementOptions, StatementCategoryRow, StatementColumnMap, StatementCoverage, StatementReconciliation, StatementRequestRow, Usage, WireError, reconcileStatement, statementFromRows } from "@rulvar/core";
3
3
 
4
4
  //#region src/caps.d.ts
5
5
  interface OpenAiModelInfo {
@@ -145,169 +145,6 @@ interface V1190CacheAudit {
145
145
  */
146
146
  declare function auditV1190CacheJournal(entries: readonly JournalEntry[], priceUsd: (servedBy: ModelRef, usage: Usage) => number | undefined): V1190CacheAudit;
147
147
  //#endregion
148
- //#region src/reconcile.d.ts
149
- /** The four billing components a provider statement itemizes. */
150
- type BillingComponent = "input" | "cached-input" | "cache-write" | "output";
151
- /**
152
- * One normalized per-request row of a usage/billing export. `usd` is
153
- * the row's billed dollars where the export carries amounts;
154
- * `componentsUsd` its per-component split where it carries one; `usage`
155
- * the provider-reported token counts where it carries those. A row must
156
- * carry at least one of the three, and every row needs the provider's
157
- * response id, the join key.
158
- */
159
- interface StatementRequestRow {
160
- responseId: string;
161
- /** Provider-side model name (without the adapter prefix); optional. */
162
- model?: string;
163
- usd?: number;
164
- componentsUsd?: Partial<Record<BillingComponent, number>>;
165
- usage?: {
166
- inputTokens?: number;
167
- cachedInputTokens?: number;
168
- cacheWriteTokens?: number;
169
- outputTokens?: number;
170
- };
171
- }
172
- /** One per-model per-component total: the Spend categories shape. */
173
- interface StatementCategoryRow {
174
- model: string;
175
- component: BillingComponent;
176
- usd: number;
177
- }
178
- /** A normalized provider export: never a headline total. */
179
- type ProviderStatement = {
180
- kind: "requests";
181
- rows: readonly StatementRequestRow[];
182
- } | {
183
- kind: "categories";
184
- rows: readonly StatementCategoryRow[];
185
- };
186
- interface ReconcileStatementOptions {
187
- /** Our rate card, the same resolution the engine prices with. */
188
- pricingOf: (servedBy: ModelRef) => Pricing | undefined;
189
- /**
190
- * Per-component divergence threshold in USD. The default 0.005
191
- * absorbs the dashboard's 3-decimal rounding (at most 0.0005 per
192
- * figure) with an order of margin, while any real rate-card
193
- * divergence on a run worth reconciling sits orders above it.
194
- */
195
- componentToleranceUsd?: number;
196
- /**
197
- * Totals threshold for a per-request export that carries row dollars
198
- * but no per-component split; default 0.01.
199
- */
200
- totalToleranceUsd?: number;
201
- /** Provider-side model name of a served ref; default strips the adapter prefix. */
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";
217
- }
218
- /** One (model, component) line of the reconciliation. */
219
- interface ComponentDelta {
220
- model: string;
221
- component: BillingComponent;
222
- /** Our token base for the component, from the invoice rows' usage. */
223
- ourTokens: number;
224
- /** Our dollars, from the shared price decomposition (priceComponentsOf). */
225
- ourUsd: number;
226
- /** The statement's dollars; absent when the export does not carry this line. */
227
- statementUsd?: number;
228
- deltaUsd?: number;
229
- /** statementUsd over ourTokens, per MTok: the rate the provider ACTUALLY applied. */
230
- impliedUsdPerMTok?: number;
231
- /** ourUsd over ourTokens, per MTok: our effective rate over the same base, tier mix included. */
232
- effectiveUsdPerMTok?: number;
233
- divergent: boolean;
234
- }
235
- interface StatementCoverage {
236
- /** Invoice rows carrying usage or dollars: the billable set. */
237
- billableRows: number;
238
- rowsWithResponseId: number;
239
- /** Requests mode: rows the export covered. Categories mode: equals billableRows (totals claim the set). */
240
- matchedRows: number;
241
- unmatchedRows: number;
242
- /** First unmatched response ids (at most 20), requests mode. */
243
- unmatchedIdSample: string[];
244
- /** Statement rows matching nothing of ours: ids (requests) or model names (categories). */
245
- statementOnlyRows: number;
246
- statementOnlyIdSample: string[];
247
- complete: boolean;
248
- }
249
- interface StatementReconciliation {
250
- mode: "requests" | "categories";
251
- coverage: StatementCoverage;
252
- totals: {
253
- ourUsd: number;
254
- statementUsd?: number;
255
- deltaUsd?: number;
256
- };
257
- /** Every (model, component) line, models sorted, components in canonical order. */
258
- components: ComponentDelta[];
259
- /** The lines beyond tolerance, largest |delta| first: the named divergences. */
260
- divergent: ComponentDelta[];
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
- */
267
- tokenMismatches: number;
268
- tokenMismatchSample: Array<{
269
- responseId: string;
270
- field: string;
271
- ours: number;
272
- statement: number;
273
- }>;
274
- /** Models the rate card does not cover: declared, excluded from divergence. */
275
- unpricedModels: string[];
276
- /** Rows whose usage the ledger never saw (usageUnknown): counted apart, never folded. */
277
- usageUnknownRows: number;
278
- componentToleranceUsd: number;
279
- verdict: "match" | "divergence" | "partial-coverage" | "no-overlap";
280
- /**
281
- * The settlement-grade composite, first class (RV1006): true exactly
282
- * when the verdict is 'match' AND coverage is complete AND no row's
283
- * usage is unknown AND no model went unpriced. A 'match' alone is
284
- * not enough: an export can cover every KNOWN row to the cent while
285
- * a usage-unknown attempt still holds unattributed money, and a safe
286
- * consumer must not assemble this predicate by hand. The last two
287
- * conditions overlap today's verdict semantics deliberately: the
288
- * predicate states the full contract so it cannot drift apart from
289
- * a future verdict refinement.
290
- */
291
- settleable: boolean;
292
- }
293
- /**
294
- * Reconciles the invoice against a normalized provider export. Pure and
295
- * journal-free; see the module doc for the contract. Throws a typed
296
- * ConfigError on inputs that cannot be evidence: an empty statement (a
297
- * headline total with no rows), a request row without a response id, a
298
- * duplicate response id (an ambiguous join), a request export whose
299
- * rows carry neither dollars, components, nor usage, any non-finite or
300
- * negative dollar amount, any non-integer or negative token count, a
301
- * non-finite or negative tolerance (RV903: a statement that cannot
302
- * be summed must refuse loudly, never verdict 'match' on NaN totals),
303
- * or a row whose usd and componentsUsd contradict each other beyond
304
- * totalToleranceUsd (RV1005: an internally contradictory export is
305
- * not evidence either).
306
- */
307
- declare function reconcileStatement(invoice: {
308
- rows: readonly InvoiceRow[];
309
- }, statement: ProviderStatement, options: ReconcileStatementOptions): StatementReconciliation;
310
- //#endregion
311
148
  //#region src/wire.d.ts
312
149
  /** Bijective canonical-to-wire (call_*) id map. */
313
150
  declare class OpenAiIdMap {
@@ -423,4 +260,4 @@ declare function mapChatCompletionsStream(stream: AsyncIterable<Record<string, u
423
260
  signal?: AbortSignal;
424
261
  }): AsyncGenerator<ChatEvent, void>;
425
262
  //#endregion
426
- export { type BillingComponent, CONSERVATIVE_COMPATIBLE_CAPS, type ComponentDelta, OPENAI_MODELS, OPENAI_PRICING, type OpenAiAdapterOptions, type OpenAiClientLike, type OpenAiCompatibleConfig, OpenAiIdMap, type OpenAiModelInfo, type OpenAiSdkOptions, type ProviderStatement, type ReconcileStatementOptions, type ResponsesStreamEvent, type StatementCategoryRow, type StatementCoverage, type StatementReconciliation, type StatementRequestRow, type V1190CacheAudit, auditV1190CacheJournal, buildChatCompletionsParams, buildResponsesParams, mapChatCompletionsStream, mapOpenAiEffort, mapResponsesStream, normalizeOpenAiUsage, openAiErrorToWire, openAiModelInfo, openai, openaiCompatible, reconcileStatement, undoV1190CacheDoubleCount };
263
+ export { type BillingComponent, CONSERVATIVE_COMPATIBLE_CAPS, type ComponentDelta, OPENAI_MODELS, OPENAI_PRICING, type OpenAiAdapterOptions, type OpenAiClientLike, type OpenAiCompatibleConfig, OpenAiIdMap, type OpenAiModelInfo, type OpenAiSdkOptions, type ProviderStatement, type ReconcileStatementOptions, type ResponsesStreamEvent, type StatementCategoryRow, type StatementColumnMap, type StatementCoverage, type StatementReconciliation, type StatementRequestRow, type V1190CacheAudit, auditV1190CacheJournal, buildChatCompletionsParams, buildResponsesParams, mapChatCompletionsStream, mapOpenAiEffort, mapResponsesStream, normalizeOpenAiUsage, openAiErrorToWire, openAiModelInfo, openai, openaiCompatible, reconcileStatement, statementFromRows, undoV1190CacheDoubleCount };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import OpenAI from "openai";
2
- import { ConfigError, createCanonicalIdMinter, entryUsageSlices, isStrictCompatibleSchema, priceComponentsOf } from "@rulvar/core";
2
+ import { ConfigError, createCanonicalIdMinter, entryUsageSlices, isStrictCompatibleSchema, reconcileStatement, statementFromRows } from "@rulvar/core";
3
3
  //#region src/caps.ts
4
4
  const REASONING_EFFORTS = [
5
5
  "low",
@@ -1057,326 +1057,4 @@ function auditV1190CacheJournal(entries, priceUsd) {
1057
1057
  };
1058
1058
  }
1059
1059
  //#endregion
1060
- //#region src/reconcile.ts
1061
- const COMPONENTS = [
1062
- "input",
1063
- "cached-input",
1064
- "cache-write",
1065
- "output"
1066
- ];
1067
- const SAMPLE_CAP = 20;
1068
- const emptySums = () => ({
1069
- tokens: {
1070
- input: 0,
1071
- "cached-input": 0,
1072
- "cache-write": 0,
1073
- output: 0
1074
- },
1075
- usd: {
1076
- input: 0,
1077
- "cached-input": 0,
1078
- "cache-write": 0,
1079
- output: 0
1080
- }
1081
- });
1082
- const defaultModelOf = (servedBy) => {
1083
- const colon = servedBy.indexOf(":");
1084
- return colon === -1 ? servedBy : servedBy.slice(colon + 1);
1085
- };
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
- /**
1105
- * Reconciles the invoice against a normalized provider export. Pure and
1106
- * journal-free; see the module doc for the contract. Throws a typed
1107
- * ConfigError on inputs that cannot be evidence: an empty statement (a
1108
- * headline total with no rows), a request row without a response id, a
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, a
1112
- * non-finite or negative tolerance (RV903: a statement that cannot
1113
- * be summed must refuse loudly, never verdict 'match' on NaN totals),
1114
- * or a row whose usd and componentsUsd contradict each other beyond
1115
- * totalToleranceUsd (RV1005: an internally contradictory export is
1116
- * not evidence either).
1117
- */
1118
- function reconcileStatement(invoice, statement, options) {
1119
- 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`);
1120
- const componentToleranceUsd = options.componentToleranceUsd ?? .005;
1121
- const totalToleranceUsd = options.totalToleranceUsd ?? .01;
1122
- const modelOf = options.modelOf ?? defaultModelOf;
1123
- const tokenComparison = options.tokenComparison ?? "verdict";
1124
- 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");
1125
- const billable = [];
1126
- let usageUnknownRows = 0;
1127
- for (const row of invoice.rows) {
1128
- if (row.usageUnknown === true) {
1129
- usageUnknownRows += 1;
1130
- continue;
1131
- }
1132
- billable.push(row);
1133
- }
1134
- let covered = billable;
1135
- let matchedRows;
1136
- let unmatchedRows = 0;
1137
- const unmatchedIdSample = [];
1138
- let statementOnlyRows = 0;
1139
- const statementOnlyIdSample = [];
1140
- let statementTotalUsd;
1141
- let statementComponents;
1142
- let matchedStatementRows = 0;
1143
- let matchedUsdRows = 0;
1144
- let tokenMismatches = 0;
1145
- let partialOverlap = false;
1146
- const tokenMismatchSample = [];
1147
- const rowsWithResponseId = billable.filter((row) => row.responseId !== void 0).length;
1148
- if (statement.kind === "requests") {
1149
- const byId = /* @__PURE__ */ new Map();
1150
- let carriesAnything = false;
1151
- for (const row of statement.rows) {
1152
- 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");
1153
- if (byId.has(row.responseId)) throw new ConfigError(`statement reconciliation refused: duplicate response id '${row.responseId}' in the export makes the join ambiguous`);
1154
- const where = `row '${row.responseId}'`;
1155
- if (row.usd !== void 0) assertStatementUsd(where, "usd", row.usd);
1156
- if (row.componentsUsd !== void 0) {
1157
- let componentsSum = 0;
1158
- let componentsSeen = 0;
1159
- for (const component of COMPONENTS) {
1160
- const usd = row.componentsUsd[component];
1161
- if (usd !== void 0) {
1162
- assertStatementUsd(where, `componentsUsd.${component}`, usd);
1163
- componentsSum += usd;
1164
- componentsSeen += 1;
1165
- }
1166
- }
1167
- if (componentsSeen === 0) throw new ConfigError(`statement reconciliation refused: ${where} declares componentsUsd with no component figures; an empty object is not evidence: drop the field or export the split`);
1168
- if (row.usd !== void 0 && componentsSeen > 0 && Math.abs(row.usd - componentsSum) > totalToleranceUsd) throw new ConfigError(`statement reconciliation refused: ${where} carries usd ${String(row.usd)} and a component split summing to ${String(componentsSum)}, claims that contradict each other beyond the ${String(totalToleranceUsd)} totals tolerance; an export whose own total disagrees with its own components is not evidence: normalize it to one dollar claim per row or fix the export`);
1169
- }
1170
- if (row.usage !== void 0) {
1171
- let usageSeen = 0;
1172
- for (const field of [
1173
- "inputTokens",
1174
- "cachedInputTokens",
1175
- "cacheWriteTokens",
1176
- "outputTokens"
1177
- ]) {
1178
- const count = row.usage[field];
1179
- if (count !== void 0) {
1180
- assertTokenCount(where, `usage.${field}`, count);
1181
- usageSeen += 1;
1182
- }
1183
- }
1184
- if (usageSeen === 0) throw new ConfigError(`statement reconciliation refused: ${where} declares usage with no token counts; an empty object is not evidence: drop the field or export the counts`);
1185
- }
1186
- byId.set(row.responseId, row);
1187
- if (row.usd !== void 0 || row.componentsUsd !== void 0 || row.usage !== void 0) carriesAnything = true;
1188
- }
1189
- if (!carriesAnything) throw new ConfigError("statement reconciliation refused: no export row carries dollars, components, or usage; there is nothing to reconcile against");
1190
- const matched = [];
1191
- const matchedStatement = /* @__PURE__ */ new Set();
1192
- const partialSegmentIds = /* @__PURE__ */ new Set();
1193
- for (const row of billable) {
1194
- const rowIds = row.wireResponseIds !== void 0 && row.wireResponseIds.length > 0 ? row.wireResponseIds : row.responseId === void 0 ? [] : [row.responseId];
1195
- const hits = rowIds.map((id) => byId.get(id)).filter((hit) => hit !== void 0);
1196
- if (rowIds.length === 0 || hits.length !== rowIds.length) {
1197
- unmatchedRows += 1;
1198
- if (row.responseId !== void 0 && unmatchedIdSample.length < SAMPLE_CAP) unmatchedIdSample.push(row.responseId);
1199
- for (const hit of hits) {
1200
- partialSegmentIds.add(hit.responseId);
1201
- partialOverlap = true;
1202
- }
1203
- continue;
1204
- }
1205
- for (const hit of hits) matchedStatement.add(hit.responseId);
1206
- matched.push(row);
1207
- if (hits.length > 0 && hits.every((hit) => hit.usage !== void 0)) {
1208
- const fields = [
1209
- ["inputTokens", row.usage.inputTokens],
1210
- ["cachedInputTokens", row.usage.cacheReadTokens],
1211
- ["cacheWriteTokens", row.usage.cacheWriteTokens],
1212
- ["outputTokens", row.usage.outputTokens]
1213
- ];
1214
- for (const [field, ours] of fields) {
1215
- let sum = 0;
1216
- let present = 0;
1217
- for (const hit of hits) {
1218
- const value = hit.usage?.[field];
1219
- if (value !== void 0) {
1220
- sum += value;
1221
- present += 1;
1222
- }
1223
- }
1224
- if (present === hits.length && sum !== ours) {
1225
- tokenMismatches += 1;
1226
- if (tokenMismatchSample.length < SAMPLE_CAP) tokenMismatchSample.push({
1227
- responseId: row.responseId ?? rowIds[0] ?? "",
1228
- field,
1229
- ours,
1230
- statement: sum
1231
- });
1232
- }
1233
- }
1234
- }
1235
- }
1236
- for (const row of statement.rows) if (!matchedStatement.has(row.responseId) && !partialSegmentIds.has(row.responseId)) {
1237
- statementOnlyRows += 1;
1238
- if (statementOnlyIdSample.length < SAMPLE_CAP) statementOnlyIdSample.push(row.responseId);
1239
- }
1240
- covered = matched;
1241
- matchedRows = matched.length;
1242
- let totalSeen = false;
1243
- let total = 0;
1244
- statementComponents = /* @__PURE__ */ new Map();
1245
- for (const row of statement.rows) {
1246
- if (!matchedStatement.has(row.responseId)) continue;
1247
- matchedStatementRows += 1;
1248
- if (row.usd !== void 0) {
1249
- matchedUsdRows += 1;
1250
- totalSeen = true;
1251
- total += row.usd;
1252
- }
1253
- if (row.componentsUsd !== void 0) {
1254
- const model = row.model ?? "";
1255
- const sums = statementComponents.get(model) ?? {};
1256
- for (const component of COMPONENTS) {
1257
- const usd = row.componentsUsd[component];
1258
- if (usd !== void 0) sums[component] = (sums[component] ?? 0) + usd;
1259
- }
1260
- statementComponents.set(model, sums);
1261
- }
1262
- }
1263
- if (totalSeen) statementTotalUsd = total;
1264
- if (statementComponents.size === 0) statementComponents = void 0;
1265
- } else {
1266
- statementComponents = /* @__PURE__ */ new Map();
1267
- let total = 0;
1268
- for (const row of statement.rows) {
1269
- assertStatementUsd(`category row '${row.model}' ${row.component}`, "usd", row.usd);
1270
- const sums = statementComponents.get(row.model) ?? {};
1271
- sums[row.component] = (sums[row.component] ?? 0) + row.usd;
1272
- statementComponents.set(row.model, sums);
1273
- total += row.usd;
1274
- }
1275
- statementTotalUsd = total;
1276
- matchedRows = billable.length;
1277
- }
1278
- const ourByModel = /* @__PURE__ */ new Map();
1279
- const unpricedModels = /* @__PURE__ */ new Set();
1280
- let ourUsd = 0;
1281
- for (const row of covered) {
1282
- const model = modelOf(row.servedBy);
1283
- const pricing = options.pricingOf(row.servedBy);
1284
- if (pricing === void 0) {
1285
- unpricedModels.add(model);
1286
- continue;
1287
- }
1288
- const parts = priceComponentsOf(pricing, row.usage);
1289
- const sums = ourByModel.get(model) ?? emptySums();
1290
- const byName = [
1291
- ["input", parts.input],
1292
- ["cached-input", parts.cachedInput],
1293
- ["cache-write", parts.cacheWrite],
1294
- ["output", parts.output]
1295
- ];
1296
- for (const [component, part] of byName) {
1297
- sums.tokens[component] += part.tokens;
1298
- sums.usd[component] += part.usd;
1299
- }
1300
- ourByModel.set(model, sums);
1301
- }
1302
- for (const sums of ourByModel.values()) for (const component of COMPONENTS) ourUsd += sums.usd[component];
1303
- if (statement.kind === "categories") {
1304
- for (const model of statementComponents?.keys() ?? []) if (!ourByModel.has(model) && !unpricedModels.has(model)) {
1305
- statementOnlyRows += 1;
1306
- if (statementOnlyIdSample.length < SAMPLE_CAP) statementOnlyIdSample.push(model);
1307
- }
1308
- }
1309
- const components = [];
1310
- const ourLines = statement.kind === "requests" && statementComponents?.has("") === true ? /* @__PURE__ */ new Map([["", [...ourByModel.values()].reduce((acc, sums) => {
1311
- for (const component of COMPONENTS) {
1312
- acc.tokens[component] += sums.tokens[component];
1313
- acc.usd[component] += sums.usd[component];
1314
- }
1315
- return acc;
1316
- }, emptySums())]]) : ourByModel;
1317
- for (const model of [...ourLines.keys()].sort()) {
1318
- const sums = ourLines.get(model);
1319
- if (sums === void 0) continue;
1320
- for (const component of COMPONENTS) {
1321
- const ourTokens = sums.tokens[component];
1322
- const ours = sums.usd[component];
1323
- const statementUsd = statementComponents?.get(model)?.[component];
1324
- const line = {
1325
- model,
1326
- component,
1327
- ourTokens,
1328
- ourUsd: ours,
1329
- divergent: false
1330
- };
1331
- if (statementUsd !== void 0) {
1332
- line.statementUsd = statementUsd;
1333
- line.deltaUsd = statementUsd - ours;
1334
- line.divergent = Math.abs(line.deltaUsd) > componentToleranceUsd;
1335
- }
1336
- if (ourTokens > 0) {
1337
- line.effectiveUsdPerMTok = ours / (ourTokens / 1e6);
1338
- if (statementUsd !== void 0) line.impliedUsdPerMTok = statementUsd / (ourTokens / 1e6);
1339
- }
1340
- components.push(line);
1341
- }
1342
- }
1343
- const divergent = components.filter((line) => line.divergent).sort((a, b) => Math.abs(b.deltaUsd ?? 0) - Math.abs(a.deltaUsd ?? 0));
1344
- const totalsDelta = statementTotalUsd === void 0 ? void 0 : statementTotalUsd - ourUsd;
1345
- const totalsDivergent = unpricedModels.size === 0 && (statement.kind === "requests" ? matchedUsdRows === matchedStatementRows : statementOnlyRows === 0 && components.every((line) => line.statementUsd !== void 0)) && totalsDelta !== void 0 && Math.abs(totalsDelta) > totalToleranceUsd;
1346
- 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));
1347
- const tokensDivergent = tokenComparison === "verdict" && tokenMismatches > 0;
1348
- let verdict;
1349
- if (divergent.length > 0 || totalsDivergent || tokensDivergent) verdict = "divergence";
1350
- else if (matchedRows === 0 && !partialOverlap) verdict = "no-overlap";
1351
- else if (!coverageComplete) verdict = "partial-coverage";
1352
- else verdict = "match";
1353
- return {
1354
- mode: statement.kind,
1355
- coverage: {
1356
- billableRows: billable.length,
1357
- rowsWithResponseId,
1358
- matchedRows,
1359
- unmatchedRows,
1360
- unmatchedIdSample,
1361
- statementOnlyRows,
1362
- statementOnlyIdSample,
1363
- complete: coverageComplete
1364
- },
1365
- totals: {
1366
- ourUsd,
1367
- ...statementTotalUsd === void 0 ? {} : { statementUsd: statementTotalUsd },
1368
- ...totalsDelta === void 0 ? {} : { deltaUsd: totalsDelta }
1369
- },
1370
- components,
1371
- divergent,
1372
- tokenMismatches,
1373
- tokenMismatchSample,
1374
- unpricedModels: [...unpricedModels].sort(),
1375
- usageUnknownRows,
1376
- componentToleranceUsd,
1377
- verdict,
1378
- settleable: verdict === "match" && coverageComplete && usageUnknownRows === 0 && unpricedModels.size === 0
1379
- };
1380
- }
1381
- //#endregion
1382
- export { CONSERVATIVE_COMPATIBLE_CAPS, OPENAI_MODELS, OPENAI_PRICING, OpenAiIdMap, auditV1190CacheJournal, buildChatCompletionsParams, buildResponsesParams, mapChatCompletionsStream, mapOpenAiEffort, mapResponsesStream, normalizeOpenAiUsage, openAiErrorToWire, openAiModelInfo, openai, openaiCompatible, reconcileStatement, undoV1190CacheDoubleCount };
1060
+ export { CONSERVATIVE_COMPATIBLE_CAPS, OPENAI_MODELS, OPENAI_PRICING, OpenAiIdMap, auditV1190CacheJournal, buildChatCompletionsParams, buildResponsesParams, mapChatCompletionsStream, mapOpenAiEffort, mapResponsesStream, normalizeOpenAiUsage, openAiErrorToWire, openAiModelInfo, openai, openaiCompatible, reconcileStatement, statementFromRows, undoV1190CacheDoubleCount };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulvar/openai",
3
- "version": "1.179.0",
3
+ "version": "1.180.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.179.0"
26
+ "@rulvar/core": "1.180.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.179.0"
32
+ "@rulvar/testing": "1.180.0"
33
33
  },
34
34
  "repository": {
35
35
  "type": "git",