@rulvar/openai 1.18.0 → 1.19.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 +11 -1
- package/dist/index.js +70 -31
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -148,7 +148,17 @@ declare function buildResponsesParams(req: ChatRequest, ids: OpenAiIdMap, option
|
|
|
148
148
|
type ResponsesStreamEvent = Record<string, unknown> & {
|
|
149
149
|
type: string;
|
|
150
150
|
};
|
|
151
|
-
/**
|
|
151
|
+
/**
|
|
152
|
+
* Normalizes Responses usage into the canonical Usage invariant, where
|
|
153
|
+
* `inputTokens` is the FULL prompt: wire `input_tokens` already includes
|
|
154
|
+
* cached READS, while cache WRITE tokens arrive SEPARATELY in
|
|
155
|
+
* `input_tokens_details.cache_write_tokens` (GPT-5.6 and later families;
|
|
156
|
+
* they bill at the 1.25x write premium, and earlier families report no
|
|
157
|
+
* field and pay no premium). Dropping the field lost the whole write
|
|
158
|
+
* charge and weakened the budget guard (v1.18.0 review P1-2), so writes
|
|
159
|
+
* are added into `inputTokens` and surfaced as `cacheWriteTokens` for
|
|
160
|
+
* the premium rate, exactly mirroring the Anthropic adapter's mapping.
|
|
161
|
+
*/
|
|
152
162
|
declare function normalizeOpenAiUsage(raw: Record<string, unknown> | undefined): Usage;
|
|
153
163
|
/**
|
|
154
164
|
* Maps the typed Responses SSE stream to ChatEvents, yielding each
|
package/dist/index.js
CHANGED
|
@@ -62,24 +62,23 @@ const OPENAI_MODELS = {
|
|
|
62
62
|
}),
|
|
63
63
|
"gpt-5.6": GPT_56_SOL,
|
|
64
64
|
"gpt-5.5": responses(4e5, 128e3, {
|
|
65
|
-
inputUsdPerMTok:
|
|
66
|
-
outputUsdPerMTok:
|
|
67
|
-
cacheReadUsdPerMTok:
|
|
65
|
+
inputUsdPerMTok: 5,
|
|
66
|
+
outputUsdPerMTok: 30,
|
|
67
|
+
cacheReadUsdPerMTok: .5
|
|
68
68
|
}),
|
|
69
69
|
"gpt-5.5-pro": responses(4e5, 128e3, {
|
|
70
|
-
inputUsdPerMTok:
|
|
71
|
-
outputUsdPerMTok:
|
|
72
|
-
cacheReadUsdPerMTok: 4
|
|
70
|
+
inputUsdPerMTok: 30,
|
|
71
|
+
outputUsdPerMTok: 180
|
|
73
72
|
}),
|
|
74
73
|
"gpt-5.4": responses(272e3, 1e5, {
|
|
75
|
-
inputUsdPerMTok:
|
|
76
|
-
outputUsdPerMTok:
|
|
77
|
-
cacheReadUsdPerMTok: .
|
|
74
|
+
inputUsdPerMTok: 2.5,
|
|
75
|
+
outputUsdPerMTok: 15,
|
|
76
|
+
cacheReadUsdPerMTok: .25
|
|
78
77
|
}),
|
|
79
78
|
"gpt-5.4-mini": responses(272e3, 1e5, {
|
|
80
|
-
inputUsdPerMTok:
|
|
81
|
-
outputUsdPerMTok: 4.
|
|
82
|
-
cacheReadUsdPerMTok: .
|
|
79
|
+
inputUsdPerMTok: .75,
|
|
80
|
+
outputUsdPerMTok: 4.5,
|
|
81
|
+
cacheReadUsdPerMTok: .075
|
|
83
82
|
})
|
|
84
83
|
};
|
|
85
84
|
/**
|
|
@@ -102,7 +101,7 @@ const OPENAI_MODELS = {
|
|
|
102
101
|
* silent reinterpretation.
|
|
103
102
|
*/
|
|
104
103
|
const OPENAI_PRICING = {
|
|
105
|
-
pricingVersion: "openai-2026-07-18",
|
|
104
|
+
pricingVersion: "openai-2026-07-18-r2",
|
|
106
105
|
models: (() => {
|
|
107
106
|
const models = {};
|
|
108
107
|
for (const [name, info] of Object.entries(OPENAI_MODELS)) if (info.caps.pricing !== void 0) models[`openai:${name}`] = info.caps.pricing;
|
|
@@ -298,15 +297,26 @@ function buildResponsesParams(req, ids, options) {
|
|
|
298
297
|
effortDownmapped
|
|
299
298
|
};
|
|
300
299
|
}
|
|
301
|
-
/**
|
|
300
|
+
/**
|
|
301
|
+
* Normalizes Responses usage into the canonical Usage invariant, where
|
|
302
|
+
* `inputTokens` is the FULL prompt: wire `input_tokens` already includes
|
|
303
|
+
* cached READS, while cache WRITE tokens arrive SEPARATELY in
|
|
304
|
+
* `input_tokens_details.cache_write_tokens` (GPT-5.6 and later families;
|
|
305
|
+
* they bill at the 1.25x write premium, and earlier families report no
|
|
306
|
+
* field and pay no premium). Dropping the field lost the whole write
|
|
307
|
+
* charge and weakened the budget guard (v1.18.0 review P1-2), so writes
|
|
308
|
+
* are added into `inputTokens` and surfaced as `cacheWriteTokens` for
|
|
309
|
+
* the premium rate, exactly mirroring the Anthropic adapter's mapping.
|
|
310
|
+
*/
|
|
302
311
|
function normalizeOpenAiUsage(raw) {
|
|
303
312
|
const inputDetails = raw?.input_tokens_details;
|
|
304
313
|
const outputDetails = raw?.output_tokens_details;
|
|
314
|
+
const cacheWrite = typeof inputDetails?.cache_write_tokens === "number" ? inputDetails.cache_write_tokens : 0;
|
|
305
315
|
const usage = {
|
|
306
|
-
inputTokens: typeof raw?.input_tokens === "number" ? raw.input_tokens : 0,
|
|
316
|
+
inputTokens: (typeof raw?.input_tokens === "number" ? raw.input_tokens : 0) + cacheWrite,
|
|
307
317
|
outputTokens: typeof raw?.output_tokens === "number" ? raw.output_tokens : 0,
|
|
308
318
|
cacheReadTokens: typeof inputDetails?.cached_tokens === "number" ? inputDetails.cached_tokens : 0,
|
|
309
|
-
cacheWriteTokens:
|
|
319
|
+
cacheWriteTokens: cacheWrite
|
|
310
320
|
};
|
|
311
321
|
const reasoning = outputDetails?.reasoning_tokens;
|
|
312
322
|
if (typeof reasoning === "number" && reasoning > 0) usage.reasoningTokens = reasoning;
|
|
@@ -415,31 +425,59 @@ async function* mapResponsesStream(stream, ids, options) {
|
|
|
415
425
|
};
|
|
416
426
|
return;
|
|
417
427
|
}
|
|
418
|
-
case "response.failed":
|
|
428
|
+
case "response.failed": {
|
|
429
|
+
const response = event.response;
|
|
430
|
+
const rawUsage = response?.usage;
|
|
431
|
+
if (rawUsage !== void 0) yield {
|
|
432
|
+
type: "usage",
|
|
433
|
+
usage: normalizeOpenAiUsage(rawUsage)
|
|
434
|
+
};
|
|
435
|
+
const error = response?.error;
|
|
419
436
|
yield {
|
|
420
437
|
type: "error",
|
|
421
|
-
error:
|
|
422
|
-
code: "agent",
|
|
423
|
-
message: (event.response?.error)?.message ?? "response.failed",
|
|
424
|
-
retryable: false,
|
|
425
|
-
data: { kind: "transport" }
|
|
426
|
-
}
|
|
438
|
+
error: failedResponseError(typeof error?.code === "string" ? error.code : void 0, error?.message ?? "response.failed")
|
|
427
439
|
};
|
|
428
440
|
return;
|
|
441
|
+
}
|
|
429
442
|
case "error":
|
|
430
443
|
yield {
|
|
431
444
|
type: "error",
|
|
432
|
-
error:
|
|
433
|
-
code: "agent",
|
|
434
|
-
message: event.message ?? "stream error",
|
|
435
|
-
retryable: false,
|
|
436
|
-
data: { kind: "transport" }
|
|
437
|
-
}
|
|
445
|
+
error: failedResponseError(typeof event.code === "string" ? event.code : void 0, event.message ?? "stream error")
|
|
438
446
|
};
|
|
439
447
|
return;
|
|
440
448
|
default: break;
|
|
441
449
|
}
|
|
442
450
|
}
|
|
451
|
+
/**
|
|
452
|
+
* Classifies a terminal stream failure (`response.failed` /
|
|
453
|
+
* SSE `error`) into the retryable WireError vocabulary by the
|
|
454
|
+
* documented `response.error.code` enum (Responses API reference):
|
|
455
|
+
* `rate_limit_exceeded` retries as a rate limit, `server_error` and
|
|
456
|
+
* timeout-class codes retry as transport faults, and everything else
|
|
457
|
+
* (validation such as `invalid_prompt`, policy, auth) stays
|
|
458
|
+
* non-retryable. Unknown codes fail closed as non-retryable: retrying a
|
|
459
|
+
* permanent failure would loop-bill it (v1.18.0 review P1-3).
|
|
460
|
+
*/
|
|
461
|
+
function failedResponseError(code, message) {
|
|
462
|
+
if (code === "rate_limit_exceeded") return {
|
|
463
|
+
code: "agent",
|
|
464
|
+
message,
|
|
465
|
+
retryable: true,
|
|
466
|
+
data: {
|
|
467
|
+
kind: "rate-limit",
|
|
468
|
+
providerCode: code
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
return {
|
|
472
|
+
code: "agent",
|
|
473
|
+
message,
|
|
474
|
+
retryable: code === "server_error" || code !== void 0 && code.endsWith("_timeout"),
|
|
475
|
+
data: {
|
|
476
|
+
kind: "transport",
|
|
477
|
+
...code === void 0 ? {} : { providerCode: code }
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
}
|
|
443
481
|
/** Projects SDK/API errors into the retryable WireError vocabulary. */
|
|
444
482
|
function openAiErrorToWire(error) {
|
|
445
483
|
const record = error;
|
|
@@ -606,11 +644,12 @@ async function* mapChatCompletionsStream(stream, ids) {
|
|
|
606
644
|
const chunkUsage = chunk.usage;
|
|
607
645
|
if (chunkUsage !== void 0 && chunkUsage !== null) {
|
|
608
646
|
const promptDetails = chunkUsage.prompt_tokens_details;
|
|
647
|
+
const cacheWrite = typeof promptDetails?.cache_write_tokens === "number" ? promptDetails.cache_write_tokens : 0;
|
|
609
648
|
usage = {
|
|
610
|
-
inputTokens: typeof chunkUsage.prompt_tokens === "number" ? chunkUsage.prompt_tokens : 0,
|
|
649
|
+
inputTokens: (typeof chunkUsage.prompt_tokens === "number" ? chunkUsage.prompt_tokens : 0) + cacheWrite,
|
|
611
650
|
outputTokens: typeof chunkUsage.completion_tokens === "number" ? chunkUsage.completion_tokens : 0,
|
|
612
651
|
cacheReadTokens: typeof promptDetails?.cached_tokens === "number" ? promptDetails.cached_tokens : 0,
|
|
613
|
-
cacheWriteTokens:
|
|
652
|
+
cacheWriteTokens: cacheWrite
|
|
614
653
|
};
|
|
615
654
|
}
|
|
616
655
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rulvar/openai",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.19.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.19.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.19.0"
|
|
33
33
|
},
|
|
34
34
|
"repository": {
|
|
35
35
|
"type": "git",
|