@rulvar/openai 1.20.0 → 1.22.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 +57 -10
- package/dist/index.js +91 -8
- 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, ModelCaps, PriceTable, ProviderAdapter, Usage, WireError } from "@rulvar/core";
|
|
2
|
+
import { CanonicalId, ChatEvent, ChatRequest, Effort, JournalEntry, ModelCaps, ModelRef, PriceTable, ProviderAdapter, Usage, WireError } from "@rulvar/core";
|
|
3
3
|
|
|
4
4
|
//#region src/caps.d.ts
|
|
5
5
|
interface OpenAiModelInfo {
|
|
@@ -8,11 +8,13 @@ interface OpenAiModelInfo {
|
|
|
8
8
|
/** Reasoning models reject non-default sampling parameters. */
|
|
9
9
|
reasoning: boolean;
|
|
10
10
|
/**
|
|
11
|
-
* The model accepts wire `reasoning.effort: "max"` (GPT-5.6
|
|
12
|
-
* the official model
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* either way.
|
|
11
|
+
* The model accepts wire `reasoning.effort: "max"` (the whole GPT-5.6
|
|
12
|
+
* family per the official model guidance, each sibling verified live
|
|
13
|
+
* 2026-07-18). When false, canonical max downmaps to wire xhigh; the
|
|
14
|
+
* downmap is recorded in providerMetadata and the journal identity
|
|
15
|
+
* keeps max, so caps accept the full canonical set either way. Flip
|
|
16
|
+
* this to true ONLY on a per-model live verification, never from the
|
|
17
|
+
* family page alone.
|
|
16
18
|
*/
|
|
17
19
|
wireMaxEffort: boolean;
|
|
18
20
|
}
|
|
@@ -108,6 +110,41 @@ interface OpenAiCompatibleConfig {
|
|
|
108
110
|
/** Creates a Chat Completions dialect adapter for a compatible endpoint. */
|
|
109
111
|
declare function openaiCompatible(cfg: OpenAiCompatibleConfig): ProviderAdapter;
|
|
110
112
|
//#endregion
|
|
113
|
+
//#region src/audit.d.ts
|
|
114
|
+
/**
|
|
115
|
+
* The exact inverse of the v1.19.0 double count for one usage:
|
|
116
|
+
* subtracts `cacheWriteTokens` back out of `inputTokens` and leaves
|
|
117
|
+
* every other field untouched. A usage without cache writes is returned
|
|
118
|
+
* unchanged (v1.19.0 recorded those correctly). Throws a typed
|
|
119
|
+
* ConfigError when the arithmetic cannot be the v1.19.0 shape (the
|
|
120
|
+
* recorded input has no room for the subtraction), which means the
|
|
121
|
+
* usage was NOT recorded by the affected adapter; do not guess.
|
|
122
|
+
*/
|
|
123
|
+
declare function undoV1190CacheDoubleCount(usage: Usage): Usage;
|
|
124
|
+
/** One journal's sidecar reconciliation; see auditV1190CacheJournal. */
|
|
125
|
+
interface V1190CacheAudit {
|
|
126
|
+
/** Entries whose usage carried the affected shape and were inverted. */
|
|
127
|
+
affectedEntries: number;
|
|
128
|
+
/** The fold as recorded (what reports and budgets saw). */
|
|
129
|
+
recordedUsd: number;
|
|
130
|
+
/** The fold with every affected usage inverted to the true wire shape. */
|
|
131
|
+
correctedUsd: number;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Folds a journal twice with the SAME price function: once as recorded
|
|
135
|
+
* and once with every affected OpenAI usage passed through
|
|
136
|
+
* `undoV1190CacheDoubleCount`, returning both totals and the affected
|
|
137
|
+
* entry count. An entry (or per-model slice) counts as affected when it
|
|
138
|
+
* was served by the `openai` adapter, carries cache writes, and has no
|
|
139
|
+
* `usageSemantics` stamp; stamped entries are already correct and fold
|
|
140
|
+
* identically in both totals. The journal itself is never touched.
|
|
141
|
+
* `recordedUsd - correctedUsd` is the exact overcharge IF the journal
|
|
142
|
+
* was recorded by v1.19.0; for a v1.20.0 journal the same shape folds
|
|
143
|
+
* to a smaller `correctedUsd` that does NOT correspond to any real
|
|
144
|
+
* charge, so version provenance stays the caller's responsibility.
|
|
145
|
+
*/
|
|
146
|
+
declare function auditV1190CacheJournal(entries: readonly JournalEntry[], priceUsd: (servedBy: ModelRef, usage: Usage) => number | undefined): V1190CacheAudit;
|
|
147
|
+
//#endregion
|
|
111
148
|
//#region src/wire.d.ts
|
|
112
149
|
/** Bijective canonical-to-wire (call_*) id map. */
|
|
113
150
|
declare class OpenAiIdMap {
|
|
@@ -121,9 +158,10 @@ declare class OpenAiIdMap {
|
|
|
121
158
|
/**
|
|
122
159
|
* Canonical-to-wire effort: low through xhigh pass through. Canonical
|
|
123
160
|
* max passes through unchanged on models whose caps declare wire max
|
|
124
|
-
* support (GPT-5.6
|
|
125
|
-
*
|
|
126
|
-
*
|
|
161
|
+
* support (the whole GPT-5.6 family, each sibling verified live
|
|
162
|
+
* 2026-07-18; v1.20.0 review P2-3); elsewhere it downmaps to xhigh
|
|
163
|
+
* (documented lossy; recorded in providerMetadata). Provider 'none' is
|
|
164
|
+
* reachable only via providerOptions.openai.reasoningEffort.
|
|
127
165
|
*/
|
|
128
166
|
declare function mapOpenAiEffort(effort: Effort, options?: {
|
|
129
167
|
wireMaxEffort?: boolean;
|
|
@@ -164,6 +202,15 @@ type ResponsesStreamEvent = Record<string, unknown> & {
|
|
|
164
202
|
* wire genuinely EXCLUDES both cache counts from `input_tokens`, so
|
|
165
203
|
* that adapter adds them; the two wires differ, the canonical Usage
|
|
166
204
|
* invariant does not.
|
|
205
|
+
*
|
|
206
|
+
* Numeric hygiene is deliberately NOT this function's job: any `number`
|
|
207
|
+
* the wire (or an injected client) reports passes through, and the core
|
|
208
|
+
* enforces the full telemetry invariant at the adapter boundary for
|
|
209
|
+
* every adapter uniformly, failing the call loud on non-finite,
|
|
210
|
+
* negative, or fractional counts while accounting only sanitized values
|
|
211
|
+
* (`usageViolations`/`sanitizeUsage` in @rulvar/core; v1.20.0 review
|
|
212
|
+
* P1-1). Real wires report whole nonnegative integers; a violation here
|
|
213
|
+
* means a broken transport, never plausible provider data.
|
|
167
214
|
*/
|
|
168
215
|
declare function normalizeOpenAiUsage(raw: Record<string, unknown> | undefined): Usage;
|
|
169
216
|
/**
|
|
@@ -195,4 +242,4 @@ declare function buildChatCompletionsParams(req: ChatRequest, ids: OpenAiIdMap):
|
|
|
195
242
|
*/
|
|
196
243
|
declare function mapChatCompletionsStream(stream: AsyncIterable<Record<string, unknown>>, ids: OpenAiIdMap): AsyncGenerator<ChatEvent, void>;
|
|
197
244
|
//#endregion
|
|
198
|
-
export { CONSERVATIVE_COMPATIBLE_CAPS, OPENAI_MODELS, OPENAI_PRICING, type OpenAiAdapterOptions, type OpenAiClientLike, type OpenAiCompatibleConfig, OpenAiIdMap, type OpenAiModelInfo, type OpenAiSdkOptions, type ResponsesStreamEvent, buildChatCompletionsParams, buildResponsesParams, mapChatCompletionsStream, mapOpenAiEffort, mapResponsesStream, normalizeOpenAiUsage, openAiErrorToWire, openAiModelInfo, openai, openaiCompatible };
|
|
245
|
+
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 };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import OpenAI from "openai";
|
|
2
|
-
import { ConfigError, createCanonicalIdMinter, isStrictCompatibleSchema } from "@rulvar/core";
|
|
2
|
+
import { ConfigError, createCanonicalIdMinter, entryUsageSlices, isStrictCompatibleSchema } from "@rulvar/core";
|
|
3
3
|
//#region src/caps.ts
|
|
4
4
|
const REASONING_EFFORTS = [
|
|
5
5
|
"low",
|
|
@@ -34,7 +34,13 @@ const GPT_56_TIERS = [{
|
|
|
34
34
|
* .../gpt-5.6-terra, .../gpt-5.6-luna; rates verified 2026-07-18). All
|
|
35
35
|
* three: prompts strictly above 272K input tokens price the FULL
|
|
36
36
|
* request at 2x input and 1.5x output; cache writes bill at 1.25x
|
|
37
|
-
* uncached input.
|
|
37
|
+
* uncached input. All three accept wire reasoning effort `max`
|
|
38
|
+
* (v1.20.0 review P2-3; verified live per sibling 2026-07-18: a
|
|
39
|
+
* Responses call with `reasoning.effort: "max"` returns HTTP 200 with
|
|
40
|
+
* the effort echoed on Terra and Luna alike, and the API's own 400
|
|
41
|
+
* validator for an invalid effort enumerates `max` among the supported
|
|
42
|
+
* values, so acceptance is enforcement, not silence). Earlier families
|
|
43
|
+
* keep the conservative downmap until verified the same way.
|
|
38
44
|
*/
|
|
39
45
|
const GPT_56_SOL = responses(105e4, 128e3, {
|
|
40
46
|
inputUsdPerMTok: 5,
|
|
@@ -52,14 +58,14 @@ const OPENAI_MODELS = {
|
|
|
52
58
|
cacheReadUsdPerMTok: .25,
|
|
53
59
|
cacheWriteUsdPerMTok: 3.125,
|
|
54
60
|
tiers: GPT_56_TIERS
|
|
55
|
-
}),
|
|
61
|
+
}, { wireMaxEffort: true }),
|
|
56
62
|
"gpt-5.6-luna": responses(105e4, 128e3, {
|
|
57
63
|
inputUsdPerMTok: 1,
|
|
58
64
|
outputUsdPerMTok: 6,
|
|
59
65
|
cacheReadUsdPerMTok: .1,
|
|
60
66
|
cacheWriteUsdPerMTok: 1.25,
|
|
61
67
|
tiers: GPT_56_TIERS
|
|
62
|
-
}),
|
|
68
|
+
}, { wireMaxEffort: true }),
|
|
63
69
|
"gpt-5.6": GPT_56_SOL,
|
|
64
70
|
"gpt-5.5": responses(4e5, 128e3, {
|
|
65
71
|
inputUsdPerMTok: 5,
|
|
@@ -168,9 +174,10 @@ var OpenAiIdMap = class {
|
|
|
168
174
|
/**
|
|
169
175
|
* Canonical-to-wire effort: low through xhigh pass through. Canonical
|
|
170
176
|
* max passes through unchanged on models whose caps declare wire max
|
|
171
|
-
* support (GPT-5.6
|
|
172
|
-
*
|
|
173
|
-
*
|
|
177
|
+
* support (the whole GPT-5.6 family, each sibling verified live
|
|
178
|
+
* 2026-07-18; v1.20.0 review P2-3); elsewhere it downmaps to xhigh
|
|
179
|
+
* (documented lossy; recorded in providerMetadata). Provider 'none' is
|
|
180
|
+
* reachable only via providerOptions.openai.reasoningEffort.
|
|
174
181
|
*/
|
|
175
182
|
function mapOpenAiEffort(effort, options) {
|
|
176
183
|
if (effort === "max" && options?.wireMaxEffort !== true) return {
|
|
@@ -330,6 +337,15 @@ function clampCacheSubsets(inputTokens, rawRead, rawWrite) {
|
|
|
330
337
|
* wire genuinely EXCLUDES both cache counts from `input_tokens`, so
|
|
331
338
|
* that adapter adds them; the two wires differ, the canonical Usage
|
|
332
339
|
* invariant does not.
|
|
340
|
+
*
|
|
341
|
+
* Numeric hygiene is deliberately NOT this function's job: any `number`
|
|
342
|
+
* the wire (or an injected client) reports passes through, and the core
|
|
343
|
+
* enforces the full telemetry invariant at the adapter boundary for
|
|
344
|
+
* every adapter uniformly, failing the call loud on non-finite,
|
|
345
|
+
* negative, or fractional counts while accounting only sanitized values
|
|
346
|
+
* (`usageViolations`/`sanitizeUsage` in @rulvar/core; v1.20.0 review
|
|
347
|
+
* P1-1). Real wires report whole nonnegative integers; a violation here
|
|
348
|
+
* means a broken transport, never plausible provider data.
|
|
333
349
|
*/
|
|
334
350
|
function normalizeOpenAiUsage(raw) {
|
|
335
351
|
const inputDetails = raw?.input_tokens_details;
|
|
@@ -740,6 +756,7 @@ function openai(options = {}) {
|
|
|
740
756
|
return {
|
|
741
757
|
id: "openai",
|
|
742
758
|
provider: "openai",
|
|
759
|
+
usageSemantics: "openai-cache-subsets-v2",
|
|
743
760
|
caps(model) {
|
|
744
761
|
return openAiModelInfo(model).caps;
|
|
745
762
|
},
|
|
@@ -810,6 +827,7 @@ function openaiCompatible(cfg) {
|
|
|
810
827
|
return {
|
|
811
828
|
id: cfg.id,
|
|
812
829
|
provider: "openai",
|
|
830
|
+
usageSemantics: "openai-cache-subsets-v2",
|
|
813
831
|
caps(model) {
|
|
814
832
|
const overrides = cfg.caps?.(model);
|
|
815
833
|
return overrides === void 0 ? CONSERVATIVE_COMPATIBLE_CAPS : {
|
|
@@ -835,4 +853,69 @@ function openaiCompatible(cfg) {
|
|
|
835
853
|
};
|
|
836
854
|
}
|
|
837
855
|
//#endregion
|
|
838
|
-
|
|
856
|
+
//#region src/audit.ts
|
|
857
|
+
/**
|
|
858
|
+
* The exact inverse of the v1.19.0 double count for one usage:
|
|
859
|
+
* subtracts `cacheWriteTokens` back out of `inputTokens` and leaves
|
|
860
|
+
* every other field untouched. A usage without cache writes is returned
|
|
861
|
+
* unchanged (v1.19.0 recorded those correctly). Throws a typed
|
|
862
|
+
* ConfigError when the arithmetic cannot be the v1.19.0 shape (the
|
|
863
|
+
* recorded input has no room for the subtraction), which means the
|
|
864
|
+
* usage was NOT recorded by the affected adapter; do not guess.
|
|
865
|
+
*/
|
|
866
|
+
function undoV1190CacheDoubleCount(usage) {
|
|
867
|
+
if (usage.cacheWriteTokens <= 0) return usage;
|
|
868
|
+
const trueInput = usage.inputTokens - usage.cacheWriteTokens;
|
|
869
|
+
if (trueInput < usage.cacheReadTokens + usage.cacheWriteTokens) throw new ConfigError(`not a v1.19.0-inflated usage: subtracting cacheWriteTokens (${String(usage.cacheWriteTokens)}) out of inputTokens (${String(usage.inputTokens)}) leaves less than the cache subsets; this usage was not recorded by the affected adapter`);
|
|
870
|
+
return {
|
|
871
|
+
...usage,
|
|
872
|
+
inputTokens: trueInput
|
|
873
|
+
};
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* Folds a journal twice with the SAME price function: once as recorded
|
|
877
|
+
* and once with every affected OpenAI usage passed through
|
|
878
|
+
* `undoV1190CacheDoubleCount`, returning both totals and the affected
|
|
879
|
+
* entry count. An entry (or per-model slice) counts as affected when it
|
|
880
|
+
* was served by the `openai` adapter, carries cache writes, and has no
|
|
881
|
+
* `usageSemantics` stamp; stamped entries are already correct and fold
|
|
882
|
+
* identically in both totals. The journal itself is never touched.
|
|
883
|
+
* `recordedUsd - correctedUsd` is the exact overcharge IF the journal
|
|
884
|
+
* was recorded by v1.19.0; for a v1.20.0 journal the same shape folds
|
|
885
|
+
* to a smaller `correctedUsd` that does NOT correspond to any real
|
|
886
|
+
* charge, so version provenance stays the caller's responsibility.
|
|
887
|
+
*/
|
|
888
|
+
function auditV1190CacheJournal(entries, priceUsd) {
|
|
889
|
+
let affectedEntries = 0;
|
|
890
|
+
let recordedUsd = 0;
|
|
891
|
+
let correctedUsd = 0;
|
|
892
|
+
for (const entry of entries) {
|
|
893
|
+
let entryAffected = false;
|
|
894
|
+
for (const slice of entryUsageSlices(entry)) {
|
|
895
|
+
const recorded = priceUsd(slice.servedBy, slice.usage);
|
|
896
|
+
if (recorded === void 0) continue;
|
|
897
|
+
recordedUsd += recorded;
|
|
898
|
+
if (entry.usageSemantics === void 0 && slice.servedBy.startsWith("openai:") && slice.usage.cacheWriteTokens > 0) {
|
|
899
|
+
let corrected;
|
|
900
|
+
try {
|
|
901
|
+
corrected = undoV1190CacheDoubleCount(slice.usage);
|
|
902
|
+
} catch {
|
|
903
|
+
corrected = void 0;
|
|
904
|
+
}
|
|
905
|
+
if (corrected === void 0) correctedUsd += recorded;
|
|
906
|
+
else {
|
|
907
|
+
entryAffected = true;
|
|
908
|
+
correctedUsd += priceUsd(slice.servedBy, corrected) ?? 0;
|
|
909
|
+
}
|
|
910
|
+
} else correctedUsd += recorded;
|
|
911
|
+
}
|
|
912
|
+
if (entryAffected) affectedEntries += 1;
|
|
913
|
+
}
|
|
914
|
+
return {
|
|
915
|
+
affectedEntries,
|
|
916
|
+
recordedUsd,
|
|
917
|
+
correctedUsd
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
//#endregion
|
|
921
|
+
export { CONSERVATIVE_COMPATIBLE_CAPS, OPENAI_MODELS, OPENAI_PRICING, OpenAiIdMap, auditV1190CacheJournal, buildChatCompletionsParams, buildResponsesParams, mapChatCompletionsStream, mapOpenAiEffort, mapResponsesStream, normalizeOpenAiUsage, openAiErrorToWire, openAiModelInfo, openai, openaiCompatible, undoV1190CacheDoubleCount };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rulvar/openai",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.22.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.22.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.22.0"
|
|
33
33
|
},
|
|
34
34
|
"repository": {
|
|
35
35
|
"type": "git",
|