@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 +2 -165
- package/dist/index.js +2 -324
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import OpenAI, { ClientOptions } from "openai";
|
|
2
|
-
import { CanonicalId, ChatEvent, ChatRequest,
|
|
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,
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
32
|
+
"@rulvar/testing": "1.180.0"
|
|
33
33
|
},
|
|
34
34
|
"repository": {
|
|
35
35
|
"type": "git",
|