@rulvar/openai 1.127.0 → 1.128.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.
Files changed (2) hide show
  1. package/dist/index.js +37 -35
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -1137,6 +1137,7 @@ function reconcileStatement(invoice, statement, options) {
1137
1137
  let statementTotalUsd;
1138
1138
  let statementComponents;
1139
1139
  let tokenMismatches = 0;
1140
+ let partialOverlap = false;
1140
1141
  const tokenMismatchSample = [];
1141
1142
  const rowsWithResponseId = billable.filter((row) => row.responseId !== void 0).length;
1142
1143
  if (statement.kind === "requests") {
@@ -1166,50 +1167,51 @@ function reconcileStatement(invoice, statement, options) {
1166
1167
  if (!carriesAnything) throw new ConfigError("statement reconciliation refused: no export row carries dollars, components, or usage; there is nothing to reconcile against");
1167
1168
  const matched = [];
1168
1169
  const matchedStatement = /* @__PURE__ */ new Set();
1170
+ const partialSegmentIds = /* @__PURE__ */ new Set();
1169
1171
  for (const row of billable) {
1170
- const hit = row.responseId === void 0 ? void 0 : byId.get(row.responseId);
1171
- if (hit === void 0) {
1172
+ const rowIds = row.wireResponseIds !== void 0 && row.wireResponseIds.length > 0 ? row.wireResponseIds : row.responseId === void 0 ? [] : [row.responseId];
1173
+ const hits = rowIds.map((id) => byId.get(id)).filter((hit) => hit !== void 0);
1174
+ if (rowIds.length === 0 || hits.length !== rowIds.length) {
1172
1175
  unmatchedRows += 1;
1173
1176
  if (row.responseId !== void 0 && unmatchedIdSample.length < SAMPLE_CAP) unmatchedIdSample.push(row.responseId);
1177
+ for (const hit of hits) {
1178
+ partialSegmentIds.add(hit.responseId);
1179
+ partialOverlap = true;
1180
+ }
1174
1181
  continue;
1175
1182
  }
1176
- matchedStatement.add(hit.responseId);
1183
+ for (const hit of hits) matchedStatement.add(hit.responseId);
1177
1184
  matched.push(row);
1178
- if (hit.usage !== void 0) {
1179
- const pairs = [
1180
- [
1181
- "inputTokens",
1182
- hit.usage.inputTokens,
1183
- row.usage.inputTokens
1184
- ],
1185
- [
1186
- "cachedInputTokens",
1187
- hit.usage.cachedInputTokens,
1188
- row.usage.cacheReadTokens
1189
- ],
1190
- [
1191
- "cacheWriteTokens",
1192
- hit.usage.cacheWriteTokens,
1193
- row.usage.cacheWriteTokens
1194
- ],
1195
- [
1196
- "outputTokens",
1197
- hit.usage.outputTokens,
1198
- row.usage.outputTokens
1199
- ]
1185
+ if (hits.length > 0 && hits.every((hit) => hit.usage !== void 0)) {
1186
+ const fields = [
1187
+ ["inputTokens", row.usage.inputTokens],
1188
+ ["cachedInputTokens", row.usage.cacheReadTokens],
1189
+ ["cacheWriteTokens", row.usage.cacheWriteTokens],
1190
+ ["outputTokens", row.usage.outputTokens]
1200
1191
  ];
1201
- for (const [field, statementValue, ours] of pairs) if (statementValue !== void 0 && statementValue !== ours) {
1202
- tokenMismatches += 1;
1203
- if (tokenMismatchSample.length < SAMPLE_CAP) tokenMismatchSample.push({
1204
- responseId: hit.responseId,
1205
- field,
1206
- ours,
1207
- statement: statementValue
1208
- });
1192
+ for (const [field, ours] of fields) {
1193
+ let sum = 0;
1194
+ let present = 0;
1195
+ for (const hit of hits) {
1196
+ const value = hit.usage?.[field];
1197
+ if (value !== void 0) {
1198
+ sum += value;
1199
+ present += 1;
1200
+ }
1201
+ }
1202
+ if (present === hits.length && sum !== ours) {
1203
+ tokenMismatches += 1;
1204
+ if (tokenMismatchSample.length < SAMPLE_CAP) tokenMismatchSample.push({
1205
+ responseId: row.responseId ?? rowIds[0] ?? "",
1206
+ field,
1207
+ ours,
1208
+ statement: sum
1209
+ });
1210
+ }
1209
1211
  }
1210
1212
  }
1211
1213
  }
1212
- for (const row of statement.rows) if (!matchedStatement.has(row.responseId)) {
1214
+ for (const row of statement.rows) if (!matchedStatement.has(row.responseId) && !partialSegmentIds.has(row.responseId)) {
1213
1215
  statementOnlyRows += 1;
1214
1216
  if (statementOnlyIdSample.length < SAMPLE_CAP) statementOnlyIdSample.push(row.responseId);
1215
1217
  }
@@ -1321,7 +1323,7 @@ function reconcileStatement(invoice, statement, options) {
1321
1323
  const tokensDivergent = tokenComparison === "verdict" && tokenMismatches > 0;
1322
1324
  let verdict;
1323
1325
  if (divergent.length > 0 || totalsDivergent || tokensDivergent) verdict = "divergence";
1324
- else if (matchedRows === 0) verdict = "no-overlap";
1326
+ else if (matchedRows === 0 && !partialOverlap) verdict = "no-overlap";
1325
1327
  else if (!coverageComplete) verdict = "partial-coverage";
1326
1328
  else verdict = "match";
1327
1329
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulvar/openai",
3
- "version": "1.127.0",
3
+ "version": "1.128.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.127.0"
26
+ "@rulvar/core": "1.128.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.127.0"
32
+ "@rulvar/testing": "1.128.0"
33
33
  },
34
34
  "repository": {
35
35
  "type": "git",