@rulvar/openai 1.19.0 → 1.20.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 +14 -8
- package/dist/index.js +41 -16
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -150,14 +150,20 @@ type ResponsesStreamEvent = Record<string, unknown> & {
|
|
|
150
150
|
};
|
|
151
151
|
/**
|
|
152
152
|
* Normalizes Responses usage into the canonical Usage invariant, where
|
|
153
|
-
* `inputTokens` is the FULL prompt
|
|
154
|
-
*
|
|
155
|
-
* `input_tokens_details.cache_write_tokens` (GPT-5.6 and later
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
153
|
+
* `inputTokens` is the FULL prompt. On the OpenAI wire `input_tokens`
|
|
154
|
+
* is ALREADY that full count: `input_tokens_details.cached_tokens` and
|
|
155
|
+
* `input_tokens_details.cache_write_tokens` (GPT-5.6 and later
|
|
156
|
+
* families) are priced SUBSETS of it, never additional tokens, so both
|
|
157
|
+
* pass through untouched and nothing is added. Verified on the live
|
|
158
|
+
* wire 2026-07-18: two identical long prompts report the SAME
|
|
159
|
+
* `input_tokens` while the details flip from write to read, and
|
|
160
|
+
* `total_tokens` equals `input_tokens + output_tokens` on both calls.
|
|
161
|
+
* Adding writes on top (the v1.19.0 reading of the field) double-billed
|
|
162
|
+
* every written token at 1x + 1.25x and inflated budget debits
|
|
163
|
+
* (v1.19.0 review P1-1). Contrast with the Anthropic adapter, whose
|
|
164
|
+
* wire genuinely EXCLUDES both cache counts from `input_tokens`, so
|
|
165
|
+
* that adapter adds them; the two wires differ, the canonical Usage
|
|
166
|
+
* invariant does not.
|
|
161
167
|
*/
|
|
162
168
|
declare function normalizeOpenAiUsage(raw: Record<string, unknown> | undefined): Usage;
|
|
163
169
|
/**
|
package/dist/index.js
CHANGED
|
@@ -298,25 +298,49 @@ function buildResponsesParams(req, ids, options) {
|
|
|
298
298
|
};
|
|
299
299
|
}
|
|
300
300
|
/**
|
|
301
|
+
* Clamps the cache detail counts into the subset domain the pricing
|
|
302
|
+
* fold relies on (`reads + writes <= input`, everything nonnegative).
|
|
303
|
+
* The provider contract already guarantees it; impossible telemetry is
|
|
304
|
+
* clamped rather than rejected because a rejection would discard PAID
|
|
305
|
+
* evidence, and clamping is the conservative direction for the budget:
|
|
306
|
+
* dropped detail tokens price at the FULL input rate instead of the
|
|
307
|
+
* cache-read discount. Reads keep priority over writes, so a violation
|
|
308
|
+
* shrinks the write premium, never the base charge.
|
|
309
|
+
*/
|
|
310
|
+
function clampCacheSubsets(inputTokens, rawRead, rawWrite) {
|
|
311
|
+
const cacheReadTokens = Math.min(Math.max(0, rawRead), Math.max(0, inputTokens));
|
|
312
|
+
return {
|
|
313
|
+
cacheReadTokens,
|
|
314
|
+
cacheWriteTokens: Math.min(Math.max(0, rawWrite), Math.max(0, inputTokens) - cacheReadTokens)
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
301
318
|
* Normalizes Responses usage into the canonical Usage invariant, where
|
|
302
|
-
* `inputTokens` is the FULL prompt
|
|
303
|
-
*
|
|
304
|
-
* `input_tokens_details.cache_write_tokens` (GPT-5.6 and later
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
309
|
-
*
|
|
319
|
+
* `inputTokens` is the FULL prompt. On the OpenAI wire `input_tokens`
|
|
320
|
+
* is ALREADY that full count: `input_tokens_details.cached_tokens` and
|
|
321
|
+
* `input_tokens_details.cache_write_tokens` (GPT-5.6 and later
|
|
322
|
+
* families) are priced SUBSETS of it, never additional tokens, so both
|
|
323
|
+
* pass through untouched and nothing is added. Verified on the live
|
|
324
|
+
* wire 2026-07-18: two identical long prompts report the SAME
|
|
325
|
+
* `input_tokens` while the details flip from write to read, and
|
|
326
|
+
* `total_tokens` equals `input_tokens + output_tokens` on both calls.
|
|
327
|
+
* Adding writes on top (the v1.19.0 reading of the field) double-billed
|
|
328
|
+
* every written token at 1x + 1.25x and inflated budget debits
|
|
329
|
+
* (v1.19.0 review P1-1). Contrast with the Anthropic adapter, whose
|
|
330
|
+
* wire genuinely EXCLUDES both cache counts from `input_tokens`, so
|
|
331
|
+
* that adapter adds them; the two wires differ, the canonical Usage
|
|
332
|
+
* invariant does not.
|
|
310
333
|
*/
|
|
311
334
|
function normalizeOpenAiUsage(raw) {
|
|
312
335
|
const inputDetails = raw?.input_tokens_details;
|
|
313
336
|
const outputDetails = raw?.output_tokens_details;
|
|
314
|
-
const
|
|
337
|
+
const inputTokens = typeof raw?.input_tokens === "number" ? raw.input_tokens : 0;
|
|
338
|
+
const clamped = clampCacheSubsets(inputTokens, typeof inputDetails?.cached_tokens === "number" ? inputDetails.cached_tokens : 0, typeof inputDetails?.cache_write_tokens === "number" ? inputDetails.cache_write_tokens : 0);
|
|
315
339
|
const usage = {
|
|
316
|
-
inputTokens
|
|
340
|
+
inputTokens,
|
|
317
341
|
outputTokens: typeof raw?.output_tokens === "number" ? raw.output_tokens : 0,
|
|
318
|
-
cacheReadTokens:
|
|
319
|
-
cacheWriteTokens:
|
|
342
|
+
cacheReadTokens: clamped.cacheReadTokens,
|
|
343
|
+
cacheWriteTokens: clamped.cacheWriteTokens
|
|
320
344
|
};
|
|
321
345
|
const reasoning = outputDetails?.reasoning_tokens;
|
|
322
346
|
if (typeof reasoning === "number" && reasoning > 0) usage.reasoningTokens = reasoning;
|
|
@@ -644,12 +668,13 @@ async function* mapChatCompletionsStream(stream, ids) {
|
|
|
644
668
|
const chunkUsage = chunk.usage;
|
|
645
669
|
if (chunkUsage !== void 0 && chunkUsage !== null) {
|
|
646
670
|
const promptDetails = chunkUsage.prompt_tokens_details;
|
|
647
|
-
const
|
|
671
|
+
const promptTokens = typeof chunkUsage.prompt_tokens === "number" ? chunkUsage.prompt_tokens : 0;
|
|
672
|
+
const clamped = clampCacheSubsets(promptTokens, typeof promptDetails?.cached_tokens === "number" ? promptDetails.cached_tokens : 0, typeof promptDetails?.cache_write_tokens === "number" ? promptDetails.cache_write_tokens : 0);
|
|
648
673
|
usage = {
|
|
649
|
-
inputTokens:
|
|
674
|
+
inputTokens: promptTokens,
|
|
650
675
|
outputTokens: typeof chunkUsage.completion_tokens === "number" ? chunkUsage.completion_tokens : 0,
|
|
651
|
-
cacheReadTokens:
|
|
652
|
-
cacheWriteTokens:
|
|
676
|
+
cacheReadTokens: clamped.cacheReadTokens,
|
|
677
|
+
cacheWriteTokens: clamped.cacheWriteTokens
|
|
653
678
|
};
|
|
654
679
|
}
|
|
655
680
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rulvar/openai",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.20.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.45.0",
|
|
26
|
-
"@rulvar/core": "1.
|
|
26
|
+
"@rulvar/core": "1.20.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/node": "^22.20.0",
|
|
30
30
|
"tsdown": "^0.22.3",
|
|
31
31
|
"typescript": "~6.0.3",
|
|
32
|
-
"@rulvar/testing": "1.
|
|
32
|
+
"@rulvar/testing": "1.20.0"
|
|
33
33
|
},
|
|
34
34
|
"repository": {
|
|
35
35
|
"type": "git",
|