@vincemakes/kiso-provider-openai 0.1.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/LICENSE +21 -0
- package/README.md +9 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +348 -0
- package/package.json +44 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 kiso contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# @vincemakes/kiso-provider-openai
|
|
2
|
+
|
|
3
|
+
The OpenAI-compatible adapter on the official SDK (OpenAI, GLM, Kimi,
|
|
4
|
+
DeepSeek, OpenRouter via base_url): reasoning dialects digested into
|
|
5
|
+
thinking, real streaming usage, exhaustive finish-reason mapping,
|
|
6
|
+
connection/timeout/5xx classification.
|
|
7
|
+
|
|
8
|
+
Requires Node >= 22. See the repository README for the framework
|
|
9
|
+
overview.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI-compat adapter — built on the official SDK, covering OpenAI and the
|
|
3
|
+
* compat family (GLM, Kimi, DeepSeek, OpenRouter, ...) via base_url swap.
|
|
4
|
+
*
|
|
5
|
+
* Dialect digestion happens HERE, never in the union (ADR-0003):
|
|
6
|
+
* - reasoning_content / reasoning deltas (DeepSeek, GLM, Qwen) → `thinking`;
|
|
7
|
+
* - streaming tool calls: arguments arrive as fragmented deltas, keyed by
|
|
8
|
+
* index; the first delta of an index carries the call id + name. The
|
|
9
|
+
* adapter accumulates each call and emits `tool_call_end` with the parsed
|
|
10
|
+
* JSON (or null — never a silent repair) at the end of the stream.
|
|
11
|
+
*
|
|
12
|
+
* Invariants: at least one `usage` precedes the final `stop` when the
|
|
13
|
+
* provider reports usage (not all compat providers do); `stop` reason maps
|
|
14
|
+
* from finish_reason.
|
|
15
|
+
*/
|
|
16
|
+
import OpenAI from "openai";
|
|
17
|
+
import type { ChatCompletionChunk } from "openai/resources/chat/completions";
|
|
18
|
+
import type { Adapter } from "@vincemakes/kiso-core";
|
|
19
|
+
/** Config accepted by the high-level factory (七: the provider owns its SDK). */
|
|
20
|
+
export interface OpenAICompatProviderConfig {
|
|
21
|
+
readonly apiKey?: string;
|
|
22
|
+
readonly baseUrl?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* High-level factory (七): builds the adapter FROM CONFIG, owning the SDK
|
|
26
|
+
* inside this package. Consumers (and the runtime's lazy provider path)
|
|
27
|
+
* import ONLY @vincemakes/kiso-provider-openai — the SDK stays a private dependency
|
|
28
|
+
* of this package, so nested installs resolve it next to here, never
|
|
29
|
+
* through a hoisted root.
|
|
30
|
+
*/
|
|
31
|
+
export declare function createOpenAICompatProvider(config?: OpenAICompatProviderConfig): Adapter;
|
|
32
|
+
export declare function createOpenAICompatAdapter(client: OpenAI): Adapter;
|
|
33
|
+
export type { ChatCompletionChunk };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI-compat adapter — built on the official SDK, covering OpenAI and the
|
|
3
|
+
* compat family (GLM, Kimi, DeepSeek, OpenRouter, ...) via base_url swap.
|
|
4
|
+
*
|
|
5
|
+
* Dialect digestion happens HERE, never in the union (ADR-0003):
|
|
6
|
+
* - reasoning_content / reasoning deltas (DeepSeek, GLM, Qwen) → `thinking`;
|
|
7
|
+
* - streaming tool calls: arguments arrive as fragmented deltas, keyed by
|
|
8
|
+
* index; the first delta of an index carries the call id + name. The
|
|
9
|
+
* adapter accumulates each call and emits `tool_call_end` with the parsed
|
|
10
|
+
* JSON (or null — never a silent repair) at the end of the stream.
|
|
11
|
+
*
|
|
12
|
+
* Invariants: at least one `usage` precedes the final `stop` when the
|
|
13
|
+
* provider reports usage (not all compat providers do); `stop` reason maps
|
|
14
|
+
* from finish_reason.
|
|
15
|
+
*/
|
|
16
|
+
import OpenAI from "openai";
|
|
17
|
+
import { mapApiError } from "@vincemakes/kiso-core";
|
|
18
|
+
/**
|
|
19
|
+
* High-level factory (七): builds the adapter FROM CONFIG, owning the SDK
|
|
20
|
+
* inside this package. Consumers (and the runtime's lazy provider path)
|
|
21
|
+
* import ONLY @vincemakes/kiso-provider-openai — the SDK stays a private dependency
|
|
22
|
+
* of this package, so nested installs resolve it next to here, never
|
|
23
|
+
* through a hoisted root.
|
|
24
|
+
*/
|
|
25
|
+
export function createOpenAICompatProvider(config = {}) {
|
|
26
|
+
const client = new OpenAI({
|
|
27
|
+
...(config.apiKey !== undefined ? { apiKey: config.apiKey } : {}),
|
|
28
|
+
...(config.baseUrl !== undefined ? { baseURL: config.baseUrl } : {}),
|
|
29
|
+
});
|
|
30
|
+
return createOpenAICompatAdapter(client);
|
|
31
|
+
}
|
|
32
|
+
export function createOpenAICompatAdapter(client) {
|
|
33
|
+
return {
|
|
34
|
+
async *stream(options) {
|
|
35
|
+
// Area 6: the stream CREATION is inside the error normalization —
|
|
36
|
+
// a 429/5xx/connection failure before the first byte is a mapped,
|
|
37
|
+
// retryable StructuredError, so the loop's pre-stream retry works.
|
|
38
|
+
let stream;
|
|
39
|
+
try {
|
|
40
|
+
stream = await client.chat.completions.create({
|
|
41
|
+
model: options.model,
|
|
42
|
+
messages: toOpenAIMessages(options.messages, options.systemPrompt),
|
|
43
|
+
stream: true,
|
|
44
|
+
// D5: request real streaming usage — without this the
|
|
45
|
+
// provider never sends a usage chunk and we would
|
|
46
|
+
// report known:false forever.
|
|
47
|
+
stream_options: { include_usage: true },
|
|
48
|
+
...(options.tools?.length ? { tools: options.tools.map(toOpenAITool) } : {}),
|
|
49
|
+
...(options.maxTokens !== undefined ? { max_tokens: options.maxTokens } : {}),
|
|
50
|
+
...(options.temperature !== undefined ? { temperature: options.temperature } : {}),
|
|
51
|
+
},
|
|
52
|
+
// Phase B: cancellation reaches the SDK; the system prompt is a
|
|
53
|
+
// first-class message, not a dropped option.
|
|
54
|
+
options.signal !== undefined ? { signal: options.signal } : undefined);
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
throw toOpenAIError(err);
|
|
58
|
+
}
|
|
59
|
+
const pending = new Map();
|
|
60
|
+
let finishReason = null;
|
|
61
|
+
let usageSent = false;
|
|
62
|
+
let stopReason = "end_turn";
|
|
63
|
+
// P1-10: the FIRST finish reason is FINAL — later content and
|
|
64
|
+
// finish reasons are ignored (a provider that emits text after a
|
|
65
|
+
// content_filter must not be reported as a clean end_turn). The
|
|
66
|
+
// usage chunk is still accepted after the finish: some compat
|
|
67
|
+
// providers send it late.
|
|
68
|
+
let finishSeen = false;
|
|
69
|
+
try {
|
|
70
|
+
for await (const chunk of stream) {
|
|
71
|
+
if (finishSeen) {
|
|
72
|
+
if (chunk.usage) {
|
|
73
|
+
usageSent = true;
|
|
74
|
+
const details = chunk.usage.prompt_tokens_details;
|
|
75
|
+
yield {
|
|
76
|
+
seq: 0,
|
|
77
|
+
type: "usage",
|
|
78
|
+
inputTokens: chunk.usage.prompt_tokens ?? null,
|
|
79
|
+
outputTokens: chunk.usage.completion_tokens ?? null,
|
|
80
|
+
cacheRead: details?.cached_tokens ?? null,
|
|
81
|
+
cacheWrite: null,
|
|
82
|
+
known: true,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
continue; // content and finish reasons after the first finish: ignored
|
|
86
|
+
}
|
|
87
|
+
// Reasoning dialect → thinking (digested here, not in the union).
|
|
88
|
+
const reasoning = chunk.choices?.[0]?.delta
|
|
89
|
+
?.reasoning_content;
|
|
90
|
+
if (reasoning) {
|
|
91
|
+
yield { seq: 0, type: "thinking", text: reasoning };
|
|
92
|
+
}
|
|
93
|
+
const delta = chunk.choices?.[0]?.delta;
|
|
94
|
+
if (delta?.content) {
|
|
95
|
+
yield { seq: 0, type: "text_delta", text: delta.content };
|
|
96
|
+
}
|
|
97
|
+
for (const tc of delta?.tool_calls ?? []) {
|
|
98
|
+
const buffered = pending.get(tc.index);
|
|
99
|
+
if (!buffered) {
|
|
100
|
+
// 六: no fallback id is adopted yet — the id must
|
|
101
|
+
// arrive from the provider to become the identity.
|
|
102
|
+
pending.set(tc.index, {
|
|
103
|
+
index: tc.index,
|
|
104
|
+
id: "",
|
|
105
|
+
name: tc.function?.name ?? "",
|
|
106
|
+
json: "",
|
|
107
|
+
emittedStart: false,
|
|
108
|
+
pendingDeltas: [],
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
const call = pending.get(tc.index);
|
|
112
|
+
// Some compat providers stream the name/id in a LATER
|
|
113
|
+
// delta; update on every delta so tool_call_end never
|
|
114
|
+
// carries an empty name (review finding 10).
|
|
115
|
+
if (tc.function?.name)
|
|
116
|
+
call.name = tc.function.name;
|
|
117
|
+
if (tc.id) {
|
|
118
|
+
// 九: the FIRST non-empty id is the call's identity,
|
|
119
|
+
// forever. A DIFFERENT id later is a protocol
|
|
120
|
+
// violation — a structured error, never a silent
|
|
121
|
+
// switch (start/delta/end must share one identity).
|
|
122
|
+
if (call.id !== "" && tc.id !== call.id) {
|
|
123
|
+
throw {
|
|
124
|
+
code: "invalid_request",
|
|
125
|
+
retryable: false,
|
|
126
|
+
message: `tool call ${call.index} changed id mid-stream: ${call.id} → ${tc.id}`,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
call.id = tc.id;
|
|
130
|
+
// The identity is now known: emit the start, then
|
|
131
|
+
// flush the argument deltas that arrived before it
|
|
132
|
+
// under the SAME id (六: start → delta → end all
|
|
133
|
+
// share one identity, never the fallback).
|
|
134
|
+
if (!call.emittedStart) {
|
|
135
|
+
call.emittedStart = true;
|
|
136
|
+
yield {
|
|
137
|
+
seq: 0,
|
|
138
|
+
type: "tool_call_start",
|
|
139
|
+
callId: call.id,
|
|
140
|
+
name: call.name,
|
|
141
|
+
};
|
|
142
|
+
for (const d of call.pendingDeltas) {
|
|
143
|
+
yield {
|
|
144
|
+
seq: 0,
|
|
145
|
+
type: "tool_call_input_delta",
|
|
146
|
+
callId: call.id,
|
|
147
|
+
inputJsonDelta: d,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
call.pendingDeltas = [];
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (tc.function?.arguments) {
|
|
154
|
+
call.json += tc.function.arguments;
|
|
155
|
+
if (call.emittedStart) {
|
|
156
|
+
yield {
|
|
157
|
+
seq: 0,
|
|
158
|
+
type: "tool_call_input_delta",
|
|
159
|
+
callId: call.id,
|
|
160
|
+
inputJsonDelta: tc.function.arguments,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
call.pendingDeltas.push(tc.function.arguments);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (chunk.usage) {
|
|
169
|
+
usageSent = true;
|
|
170
|
+
// 六: REAL cached-token data is read from the provider's
|
|
171
|
+
// prompt_tokens_details — an absent value is null, NEVER
|
|
172
|
+
// faked as a zero-cache turn. OpenAI does not report a
|
|
173
|
+
// cache write; null is the honest answer.
|
|
174
|
+
const details = chunk.usage.prompt_tokens_details;
|
|
175
|
+
yield {
|
|
176
|
+
seq: 0,
|
|
177
|
+
type: "usage",
|
|
178
|
+
inputTokens: chunk.usage.prompt_tokens ?? null,
|
|
179
|
+
outputTokens: chunk.usage.completion_tokens ?? null,
|
|
180
|
+
cacheRead: details?.cached_tokens ?? null,
|
|
181
|
+
cacheWrite: null,
|
|
182
|
+
known: true,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
const fr = chunk.choices?.[0]?.finish_reason;
|
|
186
|
+
if (fr) {
|
|
187
|
+
// P1-10: the FIRST finish is the terminal one.
|
|
188
|
+
finishReason = fr;
|
|
189
|
+
stopReason = mapFinishReason(fr);
|
|
190
|
+
finishSeen = true;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
throw toOpenAIError(err);
|
|
196
|
+
}
|
|
197
|
+
// Close out any streamed tool calls.
|
|
198
|
+
for (const call of [...pending.values()].sort((a, b) => a.index - b.index)) {
|
|
199
|
+
let input = null;
|
|
200
|
+
try {
|
|
201
|
+
input = call.json ? JSON.parse(call.json) : {};
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
input = null; // never a silent repair
|
|
205
|
+
}
|
|
206
|
+
// 六: a call whose id NEVER arrived adopts the index fallback
|
|
207
|
+
// here — no start/delta was emitted under any other identity,
|
|
208
|
+
// so this end is the call's first and only identity.
|
|
209
|
+
yield {
|
|
210
|
+
seq: 0,
|
|
211
|
+
type: "tool_call_end",
|
|
212
|
+
callId: call.id || `call_${call.index}`,
|
|
213
|
+
name: call.name,
|
|
214
|
+
input,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
if (!usageSent) {
|
|
218
|
+
// Area 6: no usage reported is expressed as UNKNOWN — nulls
|
|
219
|
+
// and known:false — never faked as a zero-cost turn.
|
|
220
|
+
yield { seq: 0, type: "usage", inputTokens: null, outputTokens: null, cacheRead: null, cacheWrite: null, known: false };
|
|
221
|
+
}
|
|
222
|
+
// Area 6 hardening (review finding 4): a stream that ended with
|
|
223
|
+
// NO finish_reason is a TRUNCATED turn — the stop is an explicit
|
|
224
|
+
// error, never a default end_turn/completed.
|
|
225
|
+
yield { seq: 0, type: "stop", reason: finishReason === null ? "error" : stopReason };
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
// ── Mapping helpers ────────────────────────────────────────────────────
|
|
230
|
+
/**
|
|
231
|
+
* Exhaustive over the SDK's CLOSED finish_reason union (Area 6): a new SDK
|
|
232
|
+
* enum is a compile error here; `content_filter` and `function_call` are
|
|
233
|
+
* explicit non-completions, never degraded into `end_turn`.
|
|
234
|
+
*/
|
|
235
|
+
function mapFinishReason(reason) {
|
|
236
|
+
switch (reason) {
|
|
237
|
+
case "stop":
|
|
238
|
+
return "end_turn";
|
|
239
|
+
case "length":
|
|
240
|
+
return "max_tokens";
|
|
241
|
+
case "tool_calls":
|
|
242
|
+
return "tool_use";
|
|
243
|
+
case "function_call":
|
|
244
|
+
return "function_call";
|
|
245
|
+
case "content_filter":
|
|
246
|
+
return "content_filter";
|
|
247
|
+
case null:
|
|
248
|
+
// D3: a chunk with NO finish reason is not a stop — the caller
|
|
249
|
+
// decides; the trailing stop is only emitted when one was seen.
|
|
250
|
+
return "error";
|
|
251
|
+
default: {
|
|
252
|
+
// D3: an unknown finish reason is an error, never completed.
|
|
253
|
+
return "error";
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
function toOpenAIContent(content) {
|
|
258
|
+
if (typeof content === "string") {
|
|
259
|
+
return [{ type: "text", text: content }];
|
|
260
|
+
}
|
|
261
|
+
return content.map((block) => block.type === "text"
|
|
262
|
+
? { type: "text", text: block.text }
|
|
263
|
+
: {
|
|
264
|
+
type: "image_url",
|
|
265
|
+
// 六: a base64 block becomes a REAL data URL —
|
|
266
|
+
// `data:<media>;base64,<data>` — never an empty string URL.
|
|
267
|
+
// URL-sourced blocks pass the provider URL through.
|
|
268
|
+
image_url: {
|
|
269
|
+
url: block.sourceType === "base64"
|
|
270
|
+
? `data:${block.mediaType ?? "image/png"};base64,${block.data ?? ""}`
|
|
271
|
+
: (block.url ?? ""),
|
|
272
|
+
},
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* 六: OpenAI tool results accept TEXT ONLY — an image block is converted to
|
|
277
|
+
* an explicit, honest text note (what kind of image was omitted and why),
|
|
278
|
+
* never silently dropped.
|
|
279
|
+
*/
|
|
280
|
+
function toOpenAIToolResultContent(content) {
|
|
281
|
+
if (typeof content === "string")
|
|
282
|
+
return content;
|
|
283
|
+
return content
|
|
284
|
+
.map((b) => b.type === "text"
|
|
285
|
+
? b.text
|
|
286
|
+
: `[image omitted — OpenAI tool results accept text only: ${b.sourceType === "base64" ? `${b.mediaType ?? "image"} (${b.data?.length ?? 0} base64 chars)` : `url ${b.url ?? ""}`}]`)
|
|
287
|
+
.join("");
|
|
288
|
+
}
|
|
289
|
+
function toOpenAIMessages(messages, systemPrompt) {
|
|
290
|
+
const out = [];
|
|
291
|
+
// The OpenAI API takes the system prompt as a system-role message; compat
|
|
292
|
+
// providers (GLM/Kimi/DeepSeek/OpenRouter) follow the same shape. It was
|
|
293
|
+
// silently dropped before Phase B.
|
|
294
|
+
if (systemPrompt !== undefined) {
|
|
295
|
+
out.push({ role: "system", content: systemPrompt });
|
|
296
|
+
}
|
|
297
|
+
for (const msg of messages) {
|
|
298
|
+
if (msg.role === "user") {
|
|
299
|
+
out.push({ role: "user", content: toOpenAIContent(msg.content) });
|
|
300
|
+
}
|
|
301
|
+
else if (msg.role === "assistant") {
|
|
302
|
+
out.push({
|
|
303
|
+
role: "assistant",
|
|
304
|
+
content: msg.blocks.filter((b) => b.type === "text").map((b) => b.text).join(""),
|
|
305
|
+
tool_calls: msg.blocks
|
|
306
|
+
.filter((b) => b.type === "tool_use")
|
|
307
|
+
.map((b) => {
|
|
308
|
+
const t = b;
|
|
309
|
+
return {
|
|
310
|
+
id: t.callId,
|
|
311
|
+
type: "function",
|
|
312
|
+
function: { name: t.name, arguments: JSON.stringify(t.input) },
|
|
313
|
+
};
|
|
314
|
+
}),
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
// tool messages accept text only — images are converted to an
|
|
319
|
+
// EXPLICIT text note (toOpenAIToolResultContent), never dropped
|
|
320
|
+
// (六).
|
|
321
|
+
out.push({
|
|
322
|
+
role: "tool",
|
|
323
|
+
tool_call_id: msg.callId,
|
|
324
|
+
content: toOpenAIToolResultContent(msg.content),
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return out;
|
|
329
|
+
}
|
|
330
|
+
function toOpenAITool(tool) {
|
|
331
|
+
return {
|
|
332
|
+
type: "function",
|
|
333
|
+
function: { name: tool.name, description: tool.description, parameters: tool.inputSchema },
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
function toOpenAIError(err) {
|
|
337
|
+
// D4: connection-level failures are recognized, not lumped into unknown.
|
|
338
|
+
if (err instanceof OpenAI.APIConnectionTimeoutError) {
|
|
339
|
+
return { code: "timeout", retryable: true, message: err.message };
|
|
340
|
+
}
|
|
341
|
+
if (err instanceof OpenAI.APIConnectionError) {
|
|
342
|
+
return { code: "network", retryable: true, message: err.message };
|
|
343
|
+
}
|
|
344
|
+
if (err instanceof OpenAI.APIError) {
|
|
345
|
+
return mapApiError(err.status, err.message);
|
|
346
|
+
}
|
|
347
|
+
return err;
|
|
348
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vincemakes/kiso-provider-openai",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "kiso OpenAI-compatible adapter \u2014 OpenAI + the compat family (GLM, Kimi, DeepSeek, OpenRouter) via base_url swap, reasoning dialects digested.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"README.md",
|
|
16
|
+
"LICENSE"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc -p tsconfig.build.json",
|
|
20
|
+
"typecheck": "tsc -p tsconfig.json",
|
|
21
|
+
"test": "vitest run"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@vincemakes/kiso-core": "0.1.0",
|
|
25
|
+
"openai": "^7.3.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "^26.1.2",
|
|
29
|
+
"typescript": "^5.7.2",
|
|
30
|
+
"vitest": "^3.0.0"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=22"
|
|
34
|
+
},
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/vincemakes/kiso.git",
|
|
38
|
+
"directory": "packages/provider-openai"
|
|
39
|
+
},
|
|
40
|
+
"bugs": {
|
|
41
|
+
"url": "https://github.com/vincemakes/kiso/issues"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://github.com/vincemakes/kiso/tree/main/packages/provider-openai#readme"
|
|
44
|
+
}
|