@rulvar/openai 1.26.0 → 1.28.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 +19 -1
- package/dist/index.js +51 -4
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -221,9 +221,17 @@ declare function normalizeOpenAiUsage(raw: Record<string, unknown> | undefined):
|
|
|
221
221
|
* output array, never the output_text aggregate. Raw output items ride
|
|
222
222
|
* finish.providerMetadata.openai.outputItems so the runtime can retain
|
|
223
223
|
* reasoning items as provider-raw parts.
|
|
224
|
+
*
|
|
225
|
+
* A stream that drains without any response terminal event
|
|
226
|
+
* (`response.completed`, `response.incomplete`, `response.failed`, or
|
|
227
|
+
* `error`) is a truncated wire read: the mapper fails closed with one
|
|
228
|
+
* retryable transport error instead of ending silently, unless
|
|
229
|
+
* `options.signal` shows the caller requested the abort (the documented
|
|
230
|
+
* exception that ends a stream without a terminal event).
|
|
224
231
|
*/
|
|
225
232
|
declare function mapResponsesStream(stream: AsyncIterable<ResponsesStreamEvent>, ids: OpenAiIdMap, options?: {
|
|
226
233
|
effortDownmapped?: boolean;
|
|
234
|
+
signal?: AbortSignal;
|
|
227
235
|
}): AsyncGenerator<ChatEvent, void>;
|
|
228
236
|
/** Projects SDK/API errors into the retryable WireError vocabulary. */
|
|
229
237
|
declare function openAiErrorToWire(error: unknown): WireError;
|
|
@@ -239,7 +247,17 @@ declare function buildChatCompletionsParams(req: ChatRequest, ids: OpenAiIdMap):
|
|
|
239
247
|
* Delta-patched chunk assembly for the degraded path; yields each
|
|
240
248
|
* canonical event as its chunk is consumed (same live-streaming contract
|
|
241
249
|
* as mapResponsesStream).
|
|
250
|
+
*
|
|
251
|
+
* The chat dialect has no explicit terminal frame at this layer: the
|
|
252
|
+
* only completion signal is a `finish_reason` on the last choice chunk.
|
|
253
|
+
* A stream that drains without one is a truncated wire read, so the
|
|
254
|
+
* mapper fails closed with one retryable transport error (after
|
|
255
|
+
* forwarding any usage the provider did report, which was still paid
|
|
256
|
+
* for) instead of synthesizing a `stop` finish, unless `options.signal`
|
|
257
|
+
* shows the caller requested the abort.
|
|
242
258
|
*/
|
|
243
|
-
declare function mapChatCompletionsStream(stream: AsyncIterable<Record<string, unknown>>, ids: OpenAiIdMap
|
|
259
|
+
declare function mapChatCompletionsStream(stream: AsyncIterable<Record<string, unknown>>, ids: OpenAiIdMap, options?: {
|
|
260
|
+
signal?: AbortSignal;
|
|
261
|
+
}): AsyncGenerator<ChatEvent, void>;
|
|
244
262
|
//#endregion
|
|
245
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 };
|
package/dist/index.js
CHANGED
|
@@ -370,6 +370,13 @@ function normalizeOpenAiUsage(raw) {
|
|
|
370
370
|
* output array, never the output_text aggregate. Raw output items ride
|
|
371
371
|
* finish.providerMetadata.openai.outputItems so the runtime can retain
|
|
372
372
|
* reasoning items as provider-raw parts.
|
|
373
|
+
*
|
|
374
|
+
* A stream that drains without any response terminal event
|
|
375
|
+
* (`response.completed`, `response.incomplete`, `response.failed`, or
|
|
376
|
+
* `error`) is a truncated wire read: the mapper fails closed with one
|
|
377
|
+
* retryable transport error instead of ending silently, unless
|
|
378
|
+
* `options.signal` shows the caller requested the abort (the documented
|
|
379
|
+
* exception that ends a stream without a terminal event).
|
|
373
380
|
*/
|
|
374
381
|
async function* mapResponsesStream(stream, ids, options) {
|
|
375
382
|
const callIdByItemId = /* @__PURE__ */ new Map();
|
|
@@ -487,6 +494,16 @@ async function* mapResponsesStream(stream, ids, options) {
|
|
|
487
494
|
return;
|
|
488
495
|
default: break;
|
|
489
496
|
}
|
|
497
|
+
if (options?.signal?.aborted === true) return;
|
|
498
|
+
yield {
|
|
499
|
+
type: "error",
|
|
500
|
+
error: {
|
|
501
|
+
code: "agent",
|
|
502
|
+
message: "Responses stream ended without a response terminal event (response.completed, response.incomplete, response.failed, or error); the read was truncated",
|
|
503
|
+
retryable: true,
|
|
504
|
+
data: { kind: "transport" }
|
|
505
|
+
}
|
|
506
|
+
};
|
|
490
507
|
}
|
|
491
508
|
/**
|
|
492
509
|
* Classifies a terminal stream failure (`response.failed` /
|
|
@@ -635,10 +652,19 @@ function buildChatCompletionsParams(req, ids) {
|
|
|
635
652
|
* Delta-patched chunk assembly for the degraded path; yields each
|
|
636
653
|
* canonical event as its chunk is consumed (same live-streaming contract
|
|
637
654
|
* as mapResponsesStream).
|
|
655
|
+
*
|
|
656
|
+
* The chat dialect has no explicit terminal frame at this layer: the
|
|
657
|
+
* only completion signal is a `finish_reason` on the last choice chunk.
|
|
658
|
+
* A stream that drains without one is a truncated wire read, so the
|
|
659
|
+
* mapper fails closed with one retryable transport error (after
|
|
660
|
+
* forwarding any usage the provider did report, which was still paid
|
|
661
|
+
* for) instead of synthesizing a `stop` finish, unless `options.signal`
|
|
662
|
+
* shows the caller requested the abort.
|
|
638
663
|
*/
|
|
639
|
-
async function* mapChatCompletionsStream(stream, ids) {
|
|
664
|
+
async function* mapChatCompletionsStream(stream, ids, options) {
|
|
640
665
|
const pendingCalls = /* @__PURE__ */ new Map();
|
|
641
666
|
let finishReason;
|
|
667
|
+
let sawUsageChunk = false;
|
|
642
668
|
let usage = {
|
|
643
669
|
inputTokens: 0,
|
|
644
670
|
outputTokens: 0,
|
|
@@ -683,6 +709,7 @@ async function* mapChatCompletionsStream(stream, ids) {
|
|
|
683
709
|
if (typeof choice?.finish_reason === "string") finishReason = choice.finish_reason;
|
|
684
710
|
const chunkUsage = chunk.usage;
|
|
685
711
|
if (chunkUsage !== void 0 && chunkUsage !== null) {
|
|
712
|
+
sawUsageChunk = true;
|
|
686
713
|
const promptDetails = chunkUsage.prompt_tokens_details;
|
|
687
714
|
const promptTokens = typeof chunkUsage.prompt_tokens === "number" ? chunkUsage.prompt_tokens : 0;
|
|
688
715
|
const clamped = clampCacheSubsets(promptTokens, typeof promptDetails?.cached_tokens === "number" ? promptDetails.cached_tokens : 0, typeof promptDetails?.cache_write_tokens === "number" ? promptDetails.cache_write_tokens : 0);
|
|
@@ -694,6 +721,23 @@ async function* mapChatCompletionsStream(stream, ids) {
|
|
|
694
721
|
};
|
|
695
722
|
}
|
|
696
723
|
}
|
|
724
|
+
if (finishReason === void 0) {
|
|
725
|
+
if (options?.signal?.aborted === true) return;
|
|
726
|
+
if (sawUsageChunk) yield {
|
|
727
|
+
type: "usage",
|
|
728
|
+
usage
|
|
729
|
+
};
|
|
730
|
+
yield {
|
|
731
|
+
type: "error",
|
|
732
|
+
error: {
|
|
733
|
+
code: "agent",
|
|
734
|
+
message: "chat completions stream ended without a finish_reason; the read was truncated",
|
|
735
|
+
retryable: true,
|
|
736
|
+
data: { kind: "transport" }
|
|
737
|
+
}
|
|
738
|
+
};
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
697
741
|
for (const [, pending] of pendingCalls) {
|
|
698
742
|
let args = {};
|
|
699
743
|
try {
|
|
@@ -768,14 +812,17 @@ function openai(options = {}) {
|
|
|
768
812
|
yield* mapResponsesStream(await client.responses.create({
|
|
769
813
|
...params,
|
|
770
814
|
stream: true
|
|
771
|
-
}, signal === void 0 ? void 0 : { signal }), ids, {
|
|
815
|
+
}, signal === void 0 ? void 0 : { signal }), ids, {
|
|
816
|
+
effortDownmapped,
|
|
817
|
+
...signal === void 0 ? {} : { signal }
|
|
818
|
+
});
|
|
772
819
|
} else {
|
|
773
820
|
const params = buildChatCompletionsParams(req, ids);
|
|
774
821
|
yield* mapChatCompletionsStream(await client.chat.completions.create({
|
|
775
822
|
...params,
|
|
776
823
|
stream: true,
|
|
777
824
|
stream_options: { include_usage: true }
|
|
778
|
-
}, signal === void 0 ? void 0 : { signal }), ids);
|
|
825
|
+
}, signal === void 0 ? void 0 : { signal }), ids, signal === void 0 ? void 0 : { signal });
|
|
779
826
|
}
|
|
780
827
|
} catch (thrown) {
|
|
781
828
|
if (signal?.aborted !== true) yield {
|
|
@@ -842,7 +889,7 @@ function openaiCompatible(cfg) {
|
|
|
842
889
|
...params,
|
|
843
890
|
stream: true,
|
|
844
891
|
stream_options: { include_usage: true }
|
|
845
|
-
}, signal === void 0 ? void 0 : { signal }), ids);
|
|
892
|
+
}, signal === void 0 ? void 0 : { signal }), ids, signal === void 0 ? void 0 : { signal });
|
|
846
893
|
} catch (thrown) {
|
|
847
894
|
if (signal?.aborted !== true) yield {
|
|
848
895
|
type: "error",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rulvar/openai",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.28.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.28.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.28.0"
|
|
33
33
|
},
|
|
34
34
|
"repository": {
|
|
35
35
|
"type": "git",
|