@vincemakes/kiso-provider-openai 0.39.0 → 0.39.2

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 +33 -6
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -16,7 +16,7 @@
16
16
  import { mkdirSync, writeFileSync } from "node:fs";
17
17
  import { join } from "node:path";
18
18
  import OpenAI from "openai";
19
- import { mapApiError, parseRetryAfter } from "@vincemakes/kiso-core";
19
+ import { mapApiError, parseRetryAfter, streamFailure } from "@vincemakes/kiso-core";
20
20
  /**
21
21
  * High-level factory (round 7): builds the adapter FROM CONFIG, owning the SDK
22
22
  * inside this package. Consumers (and the runtime's lazy provider path)
@@ -258,6 +258,33 @@ export function createOpenAICompatAdapter(client, adapterOpts = {}) {
258
258
  catch (err) {
259
259
  throw toOpenAIError(err, options.model);
260
260
  }
261
+ // 0.39.1 — the verdict is read BEFORE anything is emitted. A
262
+ // stream that ended with NO finish_reason produced none: the
263
+ // protocol mandates one, so its absence is a failure somewhere
264
+ // in the path (a gateway ending a response its upstream
265
+ // abandoned is how a long session dies), never the provider's
266
+ // answer. It used to become `stop { reason: "error" }` — which
267
+ // the kernel reads as `unknown / provider stopped with an
268
+ // error`, non-retryable — so the run ended and a human had to
269
+ // restart it. `streamFailure` states the rule and the kernel's
270
+ // mid-stream recovery takes it from there.
271
+ //
272
+ // Placed ABOVE the close-out on purpose: the pending calls at
273
+ // this point are TRUNCATED. Emitting them launches them (the
274
+ // kernel starts an execution on `tool_call_end`), which would
275
+ // both run a tool on a half-arrived argument list and, once
276
+ // something has started, block the very retry this throw exists
277
+ // to reach (`unsafeStartedInDraft`).
278
+ //
279
+ // An abort keeps its existing path below: COMPAT-F1's early end
280
+ // is the caller's act, classified by the kernel from the throw
281
+ // with the signal set, and nothing here changes it. An
282
+ // UNRECOGNIZED finish_reason is the other non-case — the
283
+ // provider answered, we just do not know the word, and it still
284
+ // stops.
285
+ if (finishReason === null && !options.signal?.aborted) {
286
+ throw streamFailure(`[${vendorOf(options.model)}] request failed: the stream ended with no finish reason`);
287
+ }
261
288
  // Close out any streamed tool calls.
262
289
  for (const call of [...pending.values()].sort((a, b) => a.index - b.index)) {
263
290
  let input = null;
@@ -286,8 +313,8 @@ export function createOpenAICompatAdapter(client, adapterOpts = {}) {
286
313
  yield { seq: 0, type: "usage", inputTokens: null, outputTokens: null, cacheRead: null, cacheWrite: null, known: false, ...(served !== null ? { servedModel: served } : {}) };
287
314
  }
288
315
  // Area 6 hardening (review finding 4): a stream that ended with
289
- // NO finish_reason is a TRUNCATED turn — the stop is an explicit
290
- // error, never a default end_turn/completed.
316
+ // NO finish_reason is a TRUNCATED turn — never a default
317
+ // end_turn/completed.
291
318
  // COMPAT-F1 (2026-09-09, the GLM leg): the SDK closes an ABORTED
292
319
  // stream quietly — the iterator just ends, no finish_reason — and
293
320
  // that used to read as the provider's failure below, so esc on a
@@ -295,13 +322,13 @@ export function createOpenAICompatAdapter(client, adapterOpts = {}) {
295
322
  // durable void. The kernel classifies an abort only when the
296
323
  // adapter THROWS while the signal is set (its catch asks
297
324
  // `aborted()` first), which is what the Responses adapter does.
298
- // An early end WITHOUT an abort stays the provider's error.
325
+ // An early end WITHOUT an abort is the transport's failure.
299
326
  if (options.signal?.aborted) {
300
327
  const err = new Error("stream aborted by the caller");
301
328
  err.name = "AbortError";
302
329
  throw err;
303
330
  }
304
- yield { seq: 0, type: "stop", reason: finishReason === null ? "error" : stopReason };
331
+ yield { seq: 0, type: "stop", reason: stopReason };
305
332
  },
306
333
  };
307
334
  }
@@ -518,7 +545,7 @@ function toOpenAIError(err, model) {
518
545
  // The name is fetch's, not the vendor's, so it is a transport failure by
519
546
  // construction — the same class as APIConnectionError above.
520
547
  if (err instanceof TypeError && (err.message === "terminated" || err.message === "fetch failed")) {
521
- return { code: "network", retryable: true, message: label + err.message };
548
+ return streamFailure(label + err.message);
522
549
  }
523
550
  return err;
524
551
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-provider-openai",
3
- "version": "0.39.0",
3
+ "version": "0.39.2",
4
4
  "description": "kiso OpenAI-compatible adapter \u2014 OpenAI + the compat family (GLM, Kimi, DeepSeek, OpenRouter) via base_url swap, reasoning dialects digested.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -21,7 +21,7 @@
21
21
  "test": "vitest run"
22
22
  },
23
23
  "dependencies": {
24
- "@vincemakes/kiso-core": "0.39.0",
24
+ "@vincemakes/kiso-core": "0.39.2",
25
25
  "openai": "^7.3.0"
26
26
  },
27
27
  "devDependencies": {