@rulvar/openai 1.117.0 → 1.119.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 +128 -2
- package/dist/index.js +257 -2
- 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, Effort, JournalEntry, ModelCaps, ModelRef, PriceTable, ProviderAdapter, Usage, WireError } from "@rulvar/core";
|
|
2
|
+
import { CanonicalId, ChatEvent, ChatRequest, Effort, InvoiceRow, JournalEntry, ModelCaps, ModelRef, PriceTable, Pricing, ProviderAdapter, Usage, WireError } from "@rulvar/core";
|
|
3
3
|
|
|
4
4
|
//#region src/caps.d.ts
|
|
5
5
|
interface OpenAiModelInfo {
|
|
@@ -145,6 +145,132 @@ 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
|
+
/** One (model, component) line of the reconciliation. */
|
|
205
|
+
interface ComponentDelta {
|
|
206
|
+
model: string;
|
|
207
|
+
component: BillingComponent;
|
|
208
|
+
/** Our token base for the component, from the invoice rows' usage. */
|
|
209
|
+
ourTokens: number;
|
|
210
|
+
/** Our dollars, from the shared price decomposition (priceComponentsOf). */
|
|
211
|
+
ourUsd: number;
|
|
212
|
+
/** The statement's dollars; absent when the export does not carry this line. */
|
|
213
|
+
statementUsd?: number;
|
|
214
|
+
deltaUsd?: number;
|
|
215
|
+
/** statementUsd over ourTokens, per MTok: the rate the provider ACTUALLY applied. */
|
|
216
|
+
impliedUsdPerMTok?: number;
|
|
217
|
+
/** ourUsd over ourTokens, per MTok: our effective rate over the same base, tier mix included. */
|
|
218
|
+
effectiveUsdPerMTok?: number;
|
|
219
|
+
divergent: boolean;
|
|
220
|
+
}
|
|
221
|
+
interface StatementCoverage {
|
|
222
|
+
/** Invoice rows carrying usage or dollars: the billable set. */
|
|
223
|
+
billableRows: number;
|
|
224
|
+
rowsWithResponseId: number;
|
|
225
|
+
/** Requests mode: rows the export covered. Categories mode: equals billableRows (totals claim the set). */
|
|
226
|
+
matchedRows: number;
|
|
227
|
+
unmatchedRows: number;
|
|
228
|
+
/** First unmatched response ids (at most 20), requests mode. */
|
|
229
|
+
unmatchedIdSample: string[];
|
|
230
|
+
/** Statement rows matching nothing of ours: ids (requests) or model names (categories). */
|
|
231
|
+
statementOnlyRows: number;
|
|
232
|
+
statementOnlyIdSample: string[];
|
|
233
|
+
complete: boolean;
|
|
234
|
+
}
|
|
235
|
+
interface StatementReconciliation {
|
|
236
|
+
mode: "requests" | "categories";
|
|
237
|
+
coverage: StatementCoverage;
|
|
238
|
+
totals: {
|
|
239
|
+
ourUsd: number;
|
|
240
|
+
statementUsd?: number;
|
|
241
|
+
deltaUsd?: number;
|
|
242
|
+
};
|
|
243
|
+
/** Every (model, component) line, models sorted, components in canonical order. */
|
|
244
|
+
components: ComponentDelta[];
|
|
245
|
+
/** The lines beyond tolerance, largest |delta| first: the named divergences. */
|
|
246
|
+
divergent: ComponentDelta[];
|
|
247
|
+
/** Sample of token disagreements between the export and our recorded usage (requests mode). */
|
|
248
|
+
tokenMismatches: number;
|
|
249
|
+
tokenMismatchSample: Array<{
|
|
250
|
+
responseId: string;
|
|
251
|
+
field: string;
|
|
252
|
+
ours: number;
|
|
253
|
+
statement: number;
|
|
254
|
+
}>;
|
|
255
|
+
/** Models the rate card does not cover: declared, excluded from divergence. */
|
|
256
|
+
unpricedModels: string[];
|
|
257
|
+
/** Rows whose usage the ledger never saw (usageUnknown): counted apart, never folded. */
|
|
258
|
+
usageUnknownRows: number;
|
|
259
|
+
componentToleranceUsd: number;
|
|
260
|
+
verdict: "match" | "divergence" | "partial-coverage" | "no-overlap";
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Reconciles the invoice against a normalized provider export. Pure and
|
|
264
|
+
* journal-free; see the module doc for the contract. Throws a typed
|
|
265
|
+
* ConfigError on inputs that cannot be evidence: an empty statement (a
|
|
266
|
+
* headline total with no rows), a request row without a response id, a
|
|
267
|
+
* duplicate response id (an ambiguous join), or a request export whose
|
|
268
|
+
* rows carry neither dollars, components, nor usage.
|
|
269
|
+
*/
|
|
270
|
+
declare function reconcileStatement(invoice: {
|
|
271
|
+
rows: readonly InvoiceRow[];
|
|
272
|
+
}, statement: ProviderStatement, options: ReconcileStatementOptions): StatementReconciliation;
|
|
273
|
+
//#endregion
|
|
148
274
|
//#region src/wire.d.ts
|
|
149
275
|
/** Bijective canonical-to-wire (call_*) id map. */
|
|
150
276
|
declare class OpenAiIdMap {
|
|
@@ -260,4 +386,4 @@ declare function mapChatCompletionsStream(stream: AsyncIterable<Record<string, u
|
|
|
260
386
|
signal?: AbortSignal;
|
|
261
387
|
}): AsyncGenerator<ChatEvent, void>;
|
|
262
388
|
//#endregion
|
|
263
|
-
export { CONSERVATIVE_COMPATIBLE_CAPS, OPENAI_MODELS, OPENAI_PRICING, type OpenAiAdapterOptions, type OpenAiClientLike, type OpenAiCompatibleConfig, OpenAiIdMap, type OpenAiModelInfo, type OpenAiSdkOptions, type ResponsesStreamEvent, type V1190CacheAudit, auditV1190CacheJournal, buildChatCompletionsParams, buildResponsesParams, mapChatCompletionsStream, mapOpenAiEffort, mapResponsesStream, normalizeOpenAiUsage, openAiErrorToWire, openAiModelInfo, openai, openaiCompatible, undoV1190CacheDoubleCount };
|
|
389
|
+
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 };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import OpenAI from "openai";
|
|
2
|
-
import { ConfigError, createCanonicalIdMinter, entryUsageSlices, isStrictCompatibleSchema } from "@rulvar/core";
|
|
2
|
+
import { ConfigError, createCanonicalIdMinter, entryUsageSlices, isStrictCompatibleSchema, priceComponentsOf } from "@rulvar/core";
|
|
3
3
|
//#region src/caps.ts
|
|
4
4
|
const REASONING_EFFORTS = [
|
|
5
5
|
"low",
|
|
@@ -1038,4 +1038,259 @@ function auditV1190CacheJournal(entries, priceUsd) {
|
|
|
1038
1038
|
};
|
|
1039
1039
|
}
|
|
1040
1040
|
//#endregion
|
|
1041
|
-
|
|
1041
|
+
//#region src/reconcile.ts
|
|
1042
|
+
const COMPONENTS = [
|
|
1043
|
+
"input",
|
|
1044
|
+
"cached-input",
|
|
1045
|
+
"cache-write",
|
|
1046
|
+
"output"
|
|
1047
|
+
];
|
|
1048
|
+
const SAMPLE_CAP = 20;
|
|
1049
|
+
const emptySums = () => ({
|
|
1050
|
+
tokens: {
|
|
1051
|
+
input: 0,
|
|
1052
|
+
"cached-input": 0,
|
|
1053
|
+
"cache-write": 0,
|
|
1054
|
+
output: 0
|
|
1055
|
+
},
|
|
1056
|
+
usd: {
|
|
1057
|
+
input: 0,
|
|
1058
|
+
"cached-input": 0,
|
|
1059
|
+
"cache-write": 0,
|
|
1060
|
+
output: 0
|
|
1061
|
+
}
|
|
1062
|
+
});
|
|
1063
|
+
const defaultModelOf = (servedBy) => {
|
|
1064
|
+
const colon = servedBy.indexOf(":");
|
|
1065
|
+
return colon === -1 ? servedBy : servedBy.slice(colon + 1);
|
|
1066
|
+
};
|
|
1067
|
+
/**
|
|
1068
|
+
* Reconciles the invoice against a normalized provider export. Pure and
|
|
1069
|
+
* journal-free; see the module doc for the contract. Throws a typed
|
|
1070
|
+
* ConfigError on inputs that cannot be evidence: an empty statement (a
|
|
1071
|
+
* headline total with no rows), a request row without a response id, a
|
|
1072
|
+
* duplicate response id (an ambiguous join), or a request export whose
|
|
1073
|
+
* rows carry neither dollars, components, nor usage.
|
|
1074
|
+
*/
|
|
1075
|
+
function reconcileStatement(invoice, statement, options) {
|
|
1076
|
+
const componentToleranceUsd = options.componentToleranceUsd ?? .005;
|
|
1077
|
+
const totalToleranceUsd = options.totalToleranceUsd ?? .01;
|
|
1078
|
+
const modelOf = options.modelOf ?? defaultModelOf;
|
|
1079
|
+
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");
|
|
1080
|
+
const billable = [];
|
|
1081
|
+
let usageUnknownRows = 0;
|
|
1082
|
+
for (const row of invoice.rows) {
|
|
1083
|
+
if (row.usageUnknown === true) {
|
|
1084
|
+
usageUnknownRows += 1;
|
|
1085
|
+
continue;
|
|
1086
|
+
}
|
|
1087
|
+
billable.push(row);
|
|
1088
|
+
}
|
|
1089
|
+
let covered = billable;
|
|
1090
|
+
let matchedRows;
|
|
1091
|
+
let unmatchedRows = 0;
|
|
1092
|
+
const unmatchedIdSample = [];
|
|
1093
|
+
let statementOnlyRows = 0;
|
|
1094
|
+
const statementOnlyIdSample = [];
|
|
1095
|
+
let statementTotalUsd;
|
|
1096
|
+
let statementComponents;
|
|
1097
|
+
let tokenMismatches = 0;
|
|
1098
|
+
const tokenMismatchSample = [];
|
|
1099
|
+
const rowsWithResponseId = billable.filter((row) => row.responseId !== void 0).length;
|
|
1100
|
+
if (statement.kind === "requests") {
|
|
1101
|
+
const byId = /* @__PURE__ */ new Map();
|
|
1102
|
+
let carriesAnything = false;
|
|
1103
|
+
for (const row of statement.rows) {
|
|
1104
|
+
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");
|
|
1105
|
+
if (byId.has(row.responseId)) throw new ConfigError(`statement reconciliation refused: duplicate response id '${row.responseId}' in the export makes the join ambiguous`);
|
|
1106
|
+
byId.set(row.responseId, row);
|
|
1107
|
+
if (row.usd !== void 0 || row.componentsUsd !== void 0 || row.usage !== void 0) carriesAnything = true;
|
|
1108
|
+
}
|
|
1109
|
+
if (!carriesAnything) throw new ConfigError("statement reconciliation refused: no export row carries dollars, components, or usage; there is nothing to reconcile against");
|
|
1110
|
+
const matched = [];
|
|
1111
|
+
const matchedStatement = /* @__PURE__ */ new Set();
|
|
1112
|
+
for (const row of billable) {
|
|
1113
|
+
const hit = row.responseId === void 0 ? void 0 : byId.get(row.responseId);
|
|
1114
|
+
if (hit === void 0) {
|
|
1115
|
+
unmatchedRows += 1;
|
|
1116
|
+
if (row.responseId !== void 0 && unmatchedIdSample.length < SAMPLE_CAP) unmatchedIdSample.push(row.responseId);
|
|
1117
|
+
continue;
|
|
1118
|
+
}
|
|
1119
|
+
matchedStatement.add(hit.responseId);
|
|
1120
|
+
matched.push(row);
|
|
1121
|
+
if (hit.usage !== void 0) {
|
|
1122
|
+
const pairs = [
|
|
1123
|
+
[
|
|
1124
|
+
"inputTokens",
|
|
1125
|
+
hit.usage.inputTokens,
|
|
1126
|
+
row.usage.inputTokens
|
|
1127
|
+
],
|
|
1128
|
+
[
|
|
1129
|
+
"cachedInputTokens",
|
|
1130
|
+
hit.usage.cachedInputTokens,
|
|
1131
|
+
row.usage.cacheReadTokens
|
|
1132
|
+
],
|
|
1133
|
+
[
|
|
1134
|
+
"cacheWriteTokens",
|
|
1135
|
+
hit.usage.cacheWriteTokens,
|
|
1136
|
+
row.usage.cacheWriteTokens
|
|
1137
|
+
],
|
|
1138
|
+
[
|
|
1139
|
+
"outputTokens",
|
|
1140
|
+
hit.usage.outputTokens,
|
|
1141
|
+
row.usage.outputTokens
|
|
1142
|
+
]
|
|
1143
|
+
];
|
|
1144
|
+
for (const [field, statementValue, ours] of pairs) if (statementValue !== void 0 && statementValue !== ours) {
|
|
1145
|
+
tokenMismatches += 1;
|
|
1146
|
+
if (tokenMismatchSample.length < SAMPLE_CAP) tokenMismatchSample.push({
|
|
1147
|
+
responseId: hit.responseId,
|
|
1148
|
+
field,
|
|
1149
|
+
ours,
|
|
1150
|
+
statement: statementValue
|
|
1151
|
+
});
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
for (const row of statement.rows) if (!matchedStatement.has(row.responseId)) {
|
|
1156
|
+
statementOnlyRows += 1;
|
|
1157
|
+
if (statementOnlyIdSample.length < SAMPLE_CAP) statementOnlyIdSample.push(row.responseId);
|
|
1158
|
+
}
|
|
1159
|
+
covered = matched;
|
|
1160
|
+
matchedRows = matched.length;
|
|
1161
|
+
let totalSeen = false;
|
|
1162
|
+
let total = 0;
|
|
1163
|
+
statementComponents = /* @__PURE__ */ new Map();
|
|
1164
|
+
for (const row of statement.rows) {
|
|
1165
|
+
if (!matchedStatement.has(row.responseId)) continue;
|
|
1166
|
+
if (row.usd !== void 0) {
|
|
1167
|
+
totalSeen = true;
|
|
1168
|
+
total += row.usd;
|
|
1169
|
+
}
|
|
1170
|
+
if (row.componentsUsd !== void 0) {
|
|
1171
|
+
const model = row.model ?? "";
|
|
1172
|
+
const sums = statementComponents.get(model) ?? {};
|
|
1173
|
+
for (const component of COMPONENTS) {
|
|
1174
|
+
const usd = row.componentsUsd[component];
|
|
1175
|
+
if (usd !== void 0) sums[component] = (sums[component] ?? 0) + usd;
|
|
1176
|
+
}
|
|
1177
|
+
statementComponents.set(model, sums);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
if (totalSeen) statementTotalUsd = total;
|
|
1181
|
+
if (statementComponents.size === 0) statementComponents = void 0;
|
|
1182
|
+
} else {
|
|
1183
|
+
statementComponents = /* @__PURE__ */ new Map();
|
|
1184
|
+
let total = 0;
|
|
1185
|
+
for (const row of statement.rows) {
|
|
1186
|
+
const sums = statementComponents.get(row.model) ?? {};
|
|
1187
|
+
sums[row.component] = (sums[row.component] ?? 0) + row.usd;
|
|
1188
|
+
statementComponents.set(row.model, sums);
|
|
1189
|
+
total += row.usd;
|
|
1190
|
+
}
|
|
1191
|
+
statementTotalUsd = total;
|
|
1192
|
+
matchedRows = billable.length;
|
|
1193
|
+
}
|
|
1194
|
+
const ourByModel = /* @__PURE__ */ new Map();
|
|
1195
|
+
const unpricedModels = /* @__PURE__ */ new Set();
|
|
1196
|
+
let ourUsd = 0;
|
|
1197
|
+
for (const row of covered) {
|
|
1198
|
+
const model = modelOf(row.servedBy);
|
|
1199
|
+
const pricing = options.pricingOf(row.servedBy);
|
|
1200
|
+
if (pricing === void 0) {
|
|
1201
|
+
unpricedModels.add(model);
|
|
1202
|
+
continue;
|
|
1203
|
+
}
|
|
1204
|
+
const parts = priceComponentsOf(pricing, row.usage);
|
|
1205
|
+
const sums = ourByModel.get(model) ?? emptySums();
|
|
1206
|
+
const byName = [
|
|
1207
|
+
["input", parts.input],
|
|
1208
|
+
["cached-input", parts.cachedInput],
|
|
1209
|
+
["cache-write", parts.cacheWrite],
|
|
1210
|
+
["output", parts.output]
|
|
1211
|
+
];
|
|
1212
|
+
for (const [component, part] of byName) {
|
|
1213
|
+
sums.tokens[component] += part.tokens;
|
|
1214
|
+
sums.usd[component] += part.usd;
|
|
1215
|
+
}
|
|
1216
|
+
ourByModel.set(model, sums);
|
|
1217
|
+
}
|
|
1218
|
+
for (const sums of ourByModel.values()) for (const component of COMPONENTS) ourUsd += sums.usd[component];
|
|
1219
|
+
if (statement.kind === "categories") {
|
|
1220
|
+
for (const model of statementComponents?.keys() ?? []) if (!ourByModel.has(model) && !unpricedModels.has(model)) {
|
|
1221
|
+
statementOnlyRows += 1;
|
|
1222
|
+
if (statementOnlyIdSample.length < SAMPLE_CAP) statementOnlyIdSample.push(model);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
const components = [];
|
|
1226
|
+
const ourLines = statement.kind === "requests" && statementComponents?.has("") === true ? /* @__PURE__ */ new Map([["", [...ourByModel.values()].reduce((acc, sums) => {
|
|
1227
|
+
for (const component of COMPONENTS) {
|
|
1228
|
+
acc.tokens[component] += sums.tokens[component];
|
|
1229
|
+
acc.usd[component] += sums.usd[component];
|
|
1230
|
+
}
|
|
1231
|
+
return acc;
|
|
1232
|
+
}, emptySums())]]) : ourByModel;
|
|
1233
|
+
for (const model of [...ourLines.keys()].sort()) {
|
|
1234
|
+
const sums = ourLines.get(model);
|
|
1235
|
+
if (sums === void 0) continue;
|
|
1236
|
+
for (const component of COMPONENTS) {
|
|
1237
|
+
const ourTokens = sums.tokens[component];
|
|
1238
|
+
const ours = sums.usd[component];
|
|
1239
|
+
const statementUsd = statementComponents?.get(model)?.[component];
|
|
1240
|
+
const line = {
|
|
1241
|
+
model,
|
|
1242
|
+
component,
|
|
1243
|
+
ourTokens,
|
|
1244
|
+
ourUsd: ours,
|
|
1245
|
+
divergent: false
|
|
1246
|
+
};
|
|
1247
|
+
if (statementUsd !== void 0) {
|
|
1248
|
+
line.statementUsd = statementUsd;
|
|
1249
|
+
line.deltaUsd = statementUsd - ours;
|
|
1250
|
+
line.divergent = Math.abs(line.deltaUsd) > componentToleranceUsd;
|
|
1251
|
+
}
|
|
1252
|
+
if (ourTokens > 0) {
|
|
1253
|
+
line.effectiveUsdPerMTok = ours / (ourTokens / 1e6);
|
|
1254
|
+
if (statementUsd !== void 0) line.impliedUsdPerMTok = statementUsd / (ourTokens / 1e6);
|
|
1255
|
+
}
|
|
1256
|
+
components.push(line);
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
const divergent = components.filter((line) => line.divergent).sort((a, b) => Math.abs(b.deltaUsd ?? 0) - Math.abs(a.deltaUsd ?? 0));
|
|
1260
|
+
const totalsDelta = statementTotalUsd === void 0 ? void 0 : statementTotalUsd - ourUsd;
|
|
1261
|
+
const totalsDivergent = statementComponents === void 0 && totalsDelta !== void 0 && Math.abs(totalsDelta) > totalToleranceUsd;
|
|
1262
|
+
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));
|
|
1263
|
+
let verdict;
|
|
1264
|
+
if (divergent.length > 0 || totalsDivergent) verdict = "divergence";
|
|
1265
|
+
else if (matchedRows === 0) verdict = "no-overlap";
|
|
1266
|
+
else if (!coverageComplete) verdict = "partial-coverage";
|
|
1267
|
+
else verdict = "match";
|
|
1268
|
+
return {
|
|
1269
|
+
mode: statement.kind,
|
|
1270
|
+
coverage: {
|
|
1271
|
+
billableRows: billable.length,
|
|
1272
|
+
rowsWithResponseId,
|
|
1273
|
+
matchedRows,
|
|
1274
|
+
unmatchedRows,
|
|
1275
|
+
unmatchedIdSample,
|
|
1276
|
+
statementOnlyRows,
|
|
1277
|
+
statementOnlyIdSample,
|
|
1278
|
+
complete: coverageComplete
|
|
1279
|
+
},
|
|
1280
|
+
totals: {
|
|
1281
|
+
ourUsd,
|
|
1282
|
+
...statementTotalUsd === void 0 ? {} : { statementUsd: statementTotalUsd },
|
|
1283
|
+
...totalsDelta === void 0 ? {} : { deltaUsd: totalsDelta }
|
|
1284
|
+
},
|
|
1285
|
+
components,
|
|
1286
|
+
divergent,
|
|
1287
|
+
tokenMismatches,
|
|
1288
|
+
tokenMismatchSample,
|
|
1289
|
+
unpricedModels: [...unpricedModels].sort(),
|
|
1290
|
+
usageUnknownRows,
|
|
1291
|
+
componentToleranceUsd,
|
|
1292
|
+
verdict
|
|
1293
|
+
};
|
|
1294
|
+
}
|
|
1295
|
+
//#endregion
|
|
1296
|
+
export { CONSERVATIVE_COMPATIBLE_CAPS, OPENAI_MODELS, OPENAI_PRICING, OpenAiIdMap, auditV1190CacheJournal, buildChatCompletionsParams, buildResponsesParams, mapChatCompletionsStream, mapOpenAiEffort, mapResponsesStream, normalizeOpenAiUsage, openAiErrorToWire, openAiModelInfo, openai, openaiCompatible, reconcileStatement, undoV1190CacheDoubleCount };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rulvar/openai",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.119.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.119.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.119.0"
|
|
33
33
|
},
|
|
34
34
|
"repository": {
|
|
35
35
|
"type": "git",
|