@vincemakes/kiso-provider-openai 0.1.28 → 0.1.30
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/README.md +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +23 -23
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ overview.
|
|
|
12
12
|
|
|
13
13
|
`KISO_DUMP_REQUESTS=<dir>` writes every outgoing request body to
|
|
14
14
|
`<dir>/req-<pid>-<n>.json` before it is sent — the diagnosis instrument
|
|
15
|
-
for the request-prefix (D
|
|
15
|
+
for the request-prefix (D area) contract, kept as a permanent debug sink.
|
|
16
16
|
⚠ The bodies are REAL conversation data (the model may have seen repo
|
|
17
17
|
contents) — never share a dump dir; a dump failure never breaks the
|
|
18
18
|
request. `bench/dumpdiff.py` byte-diffs consecutive dumps and localizes
|
package/dist/index.d.ts
CHANGED
|
@@ -16,13 +16,13 @@
|
|
|
16
16
|
import OpenAI from "openai";
|
|
17
17
|
import type { ChatCompletionChunk } from "openai/resources/chat/completions";
|
|
18
18
|
import type { Adapter } from "@vincemakes/kiso-core";
|
|
19
|
-
/** Config accepted by the high-level factory (
|
|
19
|
+
/** Config accepted by the high-level factory (round 7: the provider owns its SDK). */
|
|
20
20
|
export interface OpenAICompatProviderConfig {
|
|
21
21
|
readonly apiKey?: string;
|
|
22
22
|
readonly baseUrl?: string;
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
|
-
* High-level factory (
|
|
25
|
+
* High-level factory (round 7): builds the adapter FROM CONFIG, owning the SDK
|
|
26
26
|
* inside this package. Consumers (and the runtime's lazy provider path)
|
|
27
27
|
* import ONLY @vincemakes/kiso-provider-openai — the SDK stays a private dependency
|
|
28
28
|
* of this package, so nested installs resolve it next to here, never
|
package/dist/index.js
CHANGED
|
@@ -18,7 +18,7 @@ import { join } from "node:path";
|
|
|
18
18
|
import OpenAI from "openai";
|
|
19
19
|
import { mapApiError } from "@vincemakes/kiso-core";
|
|
20
20
|
/**
|
|
21
|
-
* High-level factory (
|
|
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)
|
|
23
23
|
* import ONLY @vincemakes/kiso-provider-openai — the SDK stays a private dependency
|
|
24
24
|
* of this package, so nested installs resolve it next to here, never
|
|
@@ -104,7 +104,7 @@ export function createOpenAICompatAdapter(client) {
|
|
|
104
104
|
for (const tc of delta?.tool_calls ?? []) {
|
|
105
105
|
const buffered = pending.get(tc.index);
|
|
106
106
|
if (!buffered) {
|
|
107
|
-
//
|
|
107
|
+
// round 6: no fallback id is adopted yet — the id must
|
|
108
108
|
// arrive from the provider to become the identity.
|
|
109
109
|
pending.set(tc.index, {
|
|
110
110
|
index: tc.index,
|
|
@@ -122,7 +122,7 @@ export function createOpenAICompatAdapter(client) {
|
|
|
122
122
|
if (tc.function?.name)
|
|
123
123
|
call.name = tc.function.name;
|
|
124
124
|
if (tc.id) {
|
|
125
|
-
//
|
|
125
|
+
// round 9: the FIRST non-empty id is the call's identity,
|
|
126
126
|
// forever. A DIFFERENT id later is a protocol
|
|
127
127
|
// violation — a structured error, never a silent
|
|
128
128
|
// switch (start/delta/end must share one identity).
|
|
@@ -136,7 +136,7 @@ export function createOpenAICompatAdapter(client) {
|
|
|
136
136
|
call.id = tc.id;
|
|
137
137
|
// The identity is now known: emit the start, then
|
|
138
138
|
// flush the argument deltas that arrived before it
|
|
139
|
-
// under the SAME id (
|
|
139
|
+
// under the SAME id (round 6: start → delta → end all
|
|
140
140
|
// share one identity, never the fallback).
|
|
141
141
|
if (!call.emittedStart) {
|
|
142
142
|
call.emittedStart = true;
|
|
@@ -174,7 +174,7 @@ export function createOpenAICompatAdapter(client) {
|
|
|
174
174
|
}
|
|
175
175
|
if (chunk.usage) {
|
|
176
176
|
usageSent = true;
|
|
177
|
-
//
|
|
177
|
+
// round 6: REAL cached-token data is read from the provider's
|
|
178
178
|
// prompt_tokens_details — an absent value is null, NEVER
|
|
179
179
|
// faked as a zero-cache turn. OpenAI does not report a
|
|
180
180
|
// cache write; null is the honest answer.
|
|
@@ -210,7 +210,7 @@ export function createOpenAICompatAdapter(client) {
|
|
|
210
210
|
catch {
|
|
211
211
|
input = null; // never a silent repair
|
|
212
212
|
}
|
|
213
|
-
//
|
|
213
|
+
// round 6: a call whose id NEVER arrived adopts the index fallback
|
|
214
214
|
// here — no start/delta was emitted under any other identity,
|
|
215
215
|
// so this end is the call's first and only identity.
|
|
216
216
|
yield {
|
|
@@ -293,7 +293,7 @@ function toOpenAIContent(content) {
|
|
|
293
293
|
? { type: "text", text: block.text }
|
|
294
294
|
: {
|
|
295
295
|
type: "image_url",
|
|
296
|
-
//
|
|
296
|
+
// round 6: a base64 block becomes a REAL data URL —
|
|
297
297
|
// `data:<media>;base64,<data>` — never an empty string URL.
|
|
298
298
|
// URL-sourced blocks pass the provider URL through.
|
|
299
299
|
image_url: {
|
|
@@ -304,7 +304,7 @@ function toOpenAIContent(content) {
|
|
|
304
304
|
});
|
|
305
305
|
}
|
|
306
306
|
/**
|
|
307
|
-
*
|
|
307
|
+
* round 6: OpenAI tool results accept TEXT ONLY — an image block is converted to
|
|
308
308
|
* an explicit, honest text note (what kind of image was omitted and why),
|
|
309
309
|
* never silently dropped.
|
|
310
310
|
*/
|
|
@@ -325,20 +325,20 @@ function toOpenAIMessages(messages, systemPrompt) {
|
|
|
325
325
|
if (systemPrompt !== undefined) {
|
|
326
326
|
out.push({ role: "system", content: systemPrompt });
|
|
327
327
|
}
|
|
328
|
-
//
|
|
329
|
-
//
|
|
330
|
-
//
|
|
331
|
-
//
|
|
332
|
-
// assistant
|
|
333
|
-
//
|
|
334
|
-
//
|
|
335
|
-
//
|
|
336
|
-
// (
|
|
337
|
-
//
|
|
338
|
-
//
|
|
339
|
-
//
|
|
340
|
-
//
|
|
341
|
-
// ""
|
|
328
|
+
// the merge round (0.1.23) C7 revision: `reasoning_content`'s PRESENCE follows the whole projection's
|
|
329
|
+
// monotone state — any reasoning in the projection → every assistant message
|
|
330
|
+
// carries it (its own reasoning, or ""); otherwise none do. Rationale: the D-area request-level
|
|
331
|
+
// byte stability. The old implementation keyed on the "current round" (the ergonomics batch C7); once a round boundary passed,
|
|
332
|
+
// the old rounds' assistant-message fields were stripped and the serialization rewritten — requests N and N+1's common
|
|
333
|
+
// prefix broke at the old message; the provider's prefix cache broke at every round boundary (the fresh-mystery
|
|
334
|
+
// empirical proof: in a 14-request session both cache breaks sat at round boundaries; after the fix the same session had 0 breaks,
|
|
335
|
+
// per-request cached 82-98%). Monotonicity guarantees the field's presence flips at most once per session
|
|
336
|
+
// (when the first thinking appears, usually in the first round — before it there are almost no old assistant
|
|
337
|
+
// messages); after it, never flips for the session's life; real OpenAI never produces reasoning → the field
|
|
338
|
+
// never appears, and its request path is byte-for-byte the old behavior. Old-round messages carrying reasoning
|
|
339
|
+
// pass their reasoning back — DeepSeek's officially recommended cache-stable shape; old content hitting the prefix
|
|
340
|
+
// cache bills at 0.1× only — the "repassing wastes tokens" premise does not hold under cache economics.
|
|
341
|
+
// the "" field is accepted on old rounds too (real API verification: 200 + 2560 cached tokens).
|
|
342
342
|
const hasReasoning = messages.some((m) => m.role === "assistant" && m.reasoning !== undefined);
|
|
343
343
|
for (const msg of messages) {
|
|
344
344
|
if (msg.role === "user") {
|
|
@@ -372,7 +372,7 @@ function toOpenAIMessages(messages, systemPrompt) {
|
|
|
372
372
|
else {
|
|
373
373
|
// tool messages accept text only — images are converted to an
|
|
374
374
|
// EXPLICIT text note (toOpenAIToolResultContent), never dropped
|
|
375
|
-
// (
|
|
375
|
+
// (round 6).
|
|
376
376
|
out.push({
|
|
377
377
|
role: "tool",
|
|
378
378
|
tool_call_id: msg.callId,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincemakes/kiso-provider-openai",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.30",
|
|
4
4
|
"description": "kiso OpenAI-compatible adapter — 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.1.
|
|
24
|
+
"@vincemakes/kiso-core": "0.1.29",
|
|
25
25
|
"openai": "^7.3.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|