@morlay/dsh-llm-openai-compatible 0.0.1 → 0.0.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.
- package/LICENSE +21 -0
- package/README.md +0 -9
- package/cordis.patch.yml +3 -3
- package/dist/index.d.mts +109 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +895 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +19 -18
- package/src/adapter.ts +426 -0
- package/src/index.ts +497 -0
- package/src/serialize.ts +336 -0
- package/src/translate.ts +195 -0
- package/lib/adapter.d.mts +0 -137
- package/lib/adapter.d.mts.map +0 -1
- package/lib/adapter.mjs +0 -295
- package/lib/adapter.mjs.map +0 -1
- package/lib/index.d.mts +0 -89
- package/lib/index.d.mts.map +0 -1
- package/lib/index.mjs +0 -293
- package/lib/index.mjs.map +0 -1
- package/lib/serialize.d.mts +0 -68
- package/lib/serialize.d.mts.map +0 -1
- package/lib/serialize.mjs +0 -276
- package/lib/serialize.mjs.map +0 -1
- package/lib/translate.d.mts +0 -19
- package/lib/translate.d.mts.map +0 -1
- package/lib/translate.mjs +0 -209
- package/lib/translate.mjs.map +0 -1
package/lib/adapter.mjs
DELETED
|
@@ -1,295 +0,0 @@
|
|
|
1
|
-
import { serializeCallOptions, serializeCallOptionsWithImages } from "./serialize.mjs";
|
|
2
|
-
import { translate } from "./translate.mjs";
|
|
3
|
-
import { CONTEXT_WINDOW_EXCEEDED_CODE, LlmAdapter, LlmError, ProviderRequestId, QUOTA_EXCEEDED_CODE, ReasoningEffortId, attributionHeaders, contentHasImage, isContextWindowExceededError, isQuotaExceededError } from "@deepseek-ai/dsh-llm";
|
|
4
|
-
import { deadline, idleWatchdog, timeoutOf } from "@deepseek-ai/dsh-timeout";
|
|
5
|
-
import { APICallError } from "@ai-sdk/provider";
|
|
6
|
-
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
|
7
|
-
//#region src/adapter.ts
|
|
8
|
-
/**
|
|
9
|
-
* `OpenAICompatibleAdapter`: a multi-provider harness adapter built on
|
|
10
|
-
* `@ai-sdk/openai-compatible`. One instance serves every provider route in
|
|
11
|
-
* the plugin's `providers` dict; profile facts arrive through a thunk resolved
|
|
12
|
-
* once per operation, so the registering plugin owns validation, layering, and
|
|
13
|
-
* credential policy, and a changed profile reaches the next request without a
|
|
14
|
-
* restart. The SDK owns wire serialization and SSE parsing; this adapter owns
|
|
15
|
-
* harness message conversion, sampling-default merging (via
|
|
16
|
-
* `serialize.ts`), chunk translation (via `translate.ts`), and error
|
|
17
|
-
* normalization.
|
|
18
|
-
* @module dsh-llm-openai-compatible/adapter
|
|
19
|
-
*/
|
|
20
|
-
/** Default maximum idle interval while an adapter stream read is outstanding. */
|
|
21
|
-
const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 3e5;
|
|
22
|
-
/** Default combined request/response context capacity for unconfigured models. */
|
|
23
|
-
const DEFAULT_CONTEXT_WINDOW = 262144;
|
|
24
|
-
/** Default per-request output-token cap for unconfigured models. */
|
|
25
|
-
const DEFAULT_MAX_TOKENS = 32768;
|
|
26
|
-
/** Default bound on accumulated base64 image payload per request. */
|
|
27
|
-
const DEFAULT_MAX_REQUEST_IMAGE_BYTES = 20971520;
|
|
28
|
-
/** Code stamped on the idle-watchdog timeout reason. */
|
|
29
|
-
const STREAM_IDLE_TIMEOUT_CODE = "LLM_STREAM_IDLE_TIMEOUT";
|
|
30
|
-
/** Code stamped on the whole-request deadline timeout reason. */
|
|
31
|
-
const REQUEST_TIMEOUT_CODE = "LLM_REQUEST_TIMEOUT";
|
|
32
|
-
/** The provider options key the SDK forwards into the request body. */
|
|
33
|
-
const PROVIDER_OPTIONS_KEY = "openai-compatible";
|
|
34
|
-
/**
|
|
35
|
-
* Convert provider token accounting into disjoint AI SDK usage. OpenAI's
|
|
36
|
-
* `prompt_tokens_details.cached_tokens` and the DeepSeek dialect's
|
|
37
|
-
* `prompt_cache_hit_tokens` both report cache reads folded into
|
|
38
|
-
* `prompt_tokens`; the harness convention is disjoint counts, so cache reads
|
|
39
|
-
* are split out regardless of which field the endpoint used.
|
|
40
|
-
*/
|
|
41
|
-
function convertUsage(usage) {
|
|
42
|
-
if (usage == null) return {
|
|
43
|
-
inputTokens: {
|
|
44
|
-
total: 0,
|
|
45
|
-
noCache: 0,
|
|
46
|
-
cacheRead: void 0,
|
|
47
|
-
cacheWrite: void 0
|
|
48
|
-
},
|
|
49
|
-
outputTokens: {
|
|
50
|
-
total: 0,
|
|
51
|
-
text: void 0,
|
|
52
|
-
reasoning: void 0
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
const promptTokens = usage.prompt_tokens ?? 0;
|
|
56
|
-
const completionTokens = usage.completion_tokens ?? 0;
|
|
57
|
-
const cacheRead = usage.prompt_tokens_details?.cached_tokens ?? usage.prompt_cache_hit_tokens ?? 0;
|
|
58
|
-
const reasoningTokens = usage.completion_tokens_details?.reasoning_tokens ?? 0;
|
|
59
|
-
return {
|
|
60
|
-
inputTokens: {
|
|
61
|
-
total: promptTokens,
|
|
62
|
-
noCache: Math.max(0, promptTokens - cacheRead),
|
|
63
|
-
cacheRead,
|
|
64
|
-
cacheWrite: void 0
|
|
65
|
-
},
|
|
66
|
-
outputTokens: {
|
|
67
|
-
total: completionTokens,
|
|
68
|
-
text: Math.max(0, completionTokens - reasoningTokens),
|
|
69
|
-
reasoning: reasoningTokens
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
/** The first real blocks of one resolve: display + catalog metadata. */
|
|
74
|
-
function modelInfo(profile, model) {
|
|
75
|
-
return {
|
|
76
|
-
provider: profile.provider,
|
|
77
|
-
id: model.id,
|
|
78
|
-
name: model.name ?? model.id,
|
|
79
|
-
...model.description === void 0 ? {} : { description: model.description },
|
|
80
|
-
inputModalities: [...model.inputModalities]
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
/** The harness reasoning info for one model, honoring its declared efforts. */
|
|
84
|
-
function reasoningInfo(model, defaultEffort) {
|
|
85
|
-
const declaration = model?.reasoningEfforts;
|
|
86
|
-
if (declaration === void 0 || declaration === false) return {};
|
|
87
|
-
return { reasoning: {
|
|
88
|
-
efforts: Object.entries(declaration).map(([id]) => ({
|
|
89
|
-
id: ReasoningEffortId(id),
|
|
90
|
-
name: `${id.charAt(0).toUpperCase()}${id.slice(1)}`
|
|
91
|
-
})),
|
|
92
|
-
...defaultEffort !== void 0 && declaration[defaultEffort] !== void 0 ? { defaultEffort: ReasoningEffortId(defaultEffort) } : {}
|
|
93
|
-
} };
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Map an HTTP status to a stable LlmError code.
|
|
97
|
-
* @param status - status of a non-2xx provider response.
|
|
98
|
-
* @param error - parsed provider error body, when available.
|
|
99
|
-
* @returns the normalized harness error code.
|
|
100
|
-
*/
|
|
101
|
-
function httpErrorCode(status, error) {
|
|
102
|
-
if (status === 401 || status === 403) return "AUTH";
|
|
103
|
-
if (status === 413) return "INVALID_REQUEST";
|
|
104
|
-
const detail = [
|
|
105
|
-
error?.code,
|
|
106
|
-
error?.type,
|
|
107
|
-
error?.message
|
|
108
|
-
].filter(Boolean).join(" ");
|
|
109
|
-
if (isQuotaExceededError(detail)) return QUOTA_EXCEEDED_CODE;
|
|
110
|
-
if (status === 429) return "RATE_LIMIT";
|
|
111
|
-
if (status === 400) {
|
|
112
|
-
if (isContextWindowExceededError(detail)) return CONTEXT_WINDOW_EXCEEDED_CODE;
|
|
113
|
-
return "INVALID_REQUEST";
|
|
114
|
-
}
|
|
115
|
-
if (status >= 500) return "SERVER";
|
|
116
|
-
return `HTTP_${status}`;
|
|
117
|
-
}
|
|
118
|
-
/** Parse a provider error body out of an API-call error's JSON response body. */
|
|
119
|
-
function providerErrorBody(error) {
|
|
120
|
-
if (error.responseBody === void 0) return void 0;
|
|
121
|
-
try {
|
|
122
|
-
return JSON.parse(error.responseBody).error;
|
|
123
|
-
} catch {
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
/** Extract a provider-issued request id from response headers when present. */
|
|
128
|
-
function requestId(headers) {
|
|
129
|
-
if (headers === void 0) return void 0;
|
|
130
|
-
const value = headers["x-request-id"] ?? headers["x-openai-compatible-request-id"];
|
|
131
|
-
return value === void 0 || value.length === 0 ? void 0 : ProviderRequestId(value);
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Multi-provider adapter. Each operation reads the current profiles, so a
|
|
135
|
-
* configuration change reaches the next request without a restart; the
|
|
136
|
-
* underlying SDK provider instance is cached per resolved profile and rebuilt
|
|
137
|
-
* when the profile object changes.
|
|
138
|
-
*/
|
|
139
|
-
var OpenAICompatibleAdapter = class extends LlmAdapter {
|
|
140
|
-
config;
|
|
141
|
-
sdkProviders = /* @__PURE__ */ new Map();
|
|
142
|
-
constructor(config) {
|
|
143
|
-
super();
|
|
144
|
-
this.config = config;
|
|
145
|
-
}
|
|
146
|
-
/** The profile for one route, or the not-owned failure. */
|
|
147
|
-
profileOf(provider) {
|
|
148
|
-
const profile = this.config.profiles().get(provider);
|
|
149
|
-
if (profile === void 0) throw new LlmError(`OpenAI-compatible adapter does not own provider "${provider}"`, "NO_ADAPTER");
|
|
150
|
-
return profile;
|
|
151
|
-
}
|
|
152
|
-
/** The configured descriptor for one exact route/model pair; unlisted ids pass through. */
|
|
153
|
-
modelOf(profile, model) {
|
|
154
|
-
return profile.models.find((entry) => entry.id === model);
|
|
155
|
-
}
|
|
156
|
-
/** The SDK chat model for one route/model, cached per resolved profile. */
|
|
157
|
-
sdkModel(profile, modelId) {
|
|
158
|
-
let byModel = this.sdkProviders.get(profile);
|
|
159
|
-
if (byModel === void 0) {
|
|
160
|
-
byModel = /* @__PURE__ */ new Map();
|
|
161
|
-
this.sdkProviders.set(profile, byModel);
|
|
162
|
-
}
|
|
163
|
-
let model = byModel.get(modelId);
|
|
164
|
-
if (model === void 0) {
|
|
165
|
-
model = createOpenAICompatible({
|
|
166
|
-
name: PROVIDER_OPTIONS_KEY,
|
|
167
|
-
baseURL: profile.baseURL,
|
|
168
|
-
headers: {
|
|
169
|
-
...profile.headers,
|
|
170
|
-
...attributionHeaders()
|
|
171
|
-
},
|
|
172
|
-
includeUsage: true,
|
|
173
|
-
convertUsage
|
|
174
|
-
}).chatModel(modelId);
|
|
175
|
-
byModel.set(modelId, model);
|
|
176
|
-
}
|
|
177
|
-
return model;
|
|
178
|
-
}
|
|
179
|
-
providerInfo(provider) {
|
|
180
|
-
return {
|
|
181
|
-
id: provider,
|
|
182
|
-
name: this.config.profiles().get(provider)?.displayName ?? provider
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
providerRetryPolicy(provider) {
|
|
186
|
-
return this.config.profiles().get(provider)?.retryPolicy;
|
|
187
|
-
}
|
|
188
|
-
listModels(provider) {
|
|
189
|
-
const profile = this.profileOf(provider);
|
|
190
|
-
return Promise.resolve(profile.models.map((model) => modelInfo(profile, model)));
|
|
191
|
-
}
|
|
192
|
-
resolveModel(provider, model, _signal) {
|
|
193
|
-
const profile = this.profileOf(provider);
|
|
194
|
-
const configured = this.modelOf(profile, model);
|
|
195
|
-
const contextWindow = configured?.contextWindow ?? profile.defaultContextWindow;
|
|
196
|
-
const maxTokens = configured?.maxTokens ?? profile.defaultMaxTokens;
|
|
197
|
-
return Promise.resolve({
|
|
198
|
-
...configured === void 0 ? {
|
|
199
|
-
provider,
|
|
200
|
-
id: model,
|
|
201
|
-
name: model,
|
|
202
|
-
inputModalities: ["text"]
|
|
203
|
-
} : modelInfo(profile, configured),
|
|
204
|
-
context: { contextWindow },
|
|
205
|
-
...maxTokens !== void 0 ? { defaultMaxTokens: maxTokens } : {},
|
|
206
|
-
...reasoningInfo(configured, profile.reasoning)
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
async *stream(options) {
|
|
210
|
-
const profile = this.profileOf(options.provider);
|
|
211
|
-
const model = this.modelOf(profile, options.model);
|
|
212
|
-
const hasImages = options.messages.some((message) => contentHasImage(message.content));
|
|
213
|
-
let attachments;
|
|
214
|
-
if (hasImages) {
|
|
215
|
-
if (model?.inputModalities.includes("image") !== true) throw new LlmError(`OpenAI-compatible model "${options.model}" does not accept image input.`, "UNSUPPORTED_CONTENT");
|
|
216
|
-
attachments = this.config.resolveAttachments?.();
|
|
217
|
-
if (attachments === void 0) throw new LlmError("OpenAI-compatible image conversion requires the durable attachment service.", "UNSUPPORTED_CONTENT");
|
|
218
|
-
}
|
|
219
|
-
const apiKey = await this.config.resolveApiKey(options.provider, profile);
|
|
220
|
-
const userId = this.config.resolveUserId();
|
|
221
|
-
const consumer = new AbortController();
|
|
222
|
-
const upstream = options.signal === void 0 ? consumer.signal : AbortSignal.any([options.signal, consumer.signal]);
|
|
223
|
-
const overall = profile.timeoutMs === void 0 ? void 0 : deadline(upstream, profile.timeoutMs, REQUEST_TIMEOUT_CODE);
|
|
224
|
-
const watchdog = idleWatchdog(overall?.signal ?? upstream, profile.streamIdleTimeoutMs, STREAM_IDLE_TIMEOUT_CODE);
|
|
225
|
-
try {
|
|
226
|
-
const callOptions = attachments === void 0 ? await serializeCallOptions(options, profile, model) : await serializeCallOptionsWithImages(options, profile, model, {
|
|
227
|
-
attachments,
|
|
228
|
-
maxRequestImageBytes: profile.maxRequestImageBytes,
|
|
229
|
-
signal: watchdog.signal
|
|
230
|
-
});
|
|
231
|
-
const sdkModel = this.sdkModel(profile, options.model);
|
|
232
|
-
let result;
|
|
233
|
-
try {
|
|
234
|
-
result = await sdkModel.doStream({
|
|
235
|
-
...callOptions,
|
|
236
|
-
abortSignal: watchdog.signal,
|
|
237
|
-
headers: {
|
|
238
|
-
...apiKey === void 0 ? {} : { authorization: `Bearer ${apiKey}` },
|
|
239
|
-
"x-openai-compatible-harness-user-id": String(userId),
|
|
240
|
-
...options.sessionId !== void 0 ? { "x-openai-compatible-harness-session-id": String(options.sessionId) } : {},
|
|
241
|
-
...options.purpose === "compaction" ? { "x-openai-compatible-harness-compact": "1" } : {}
|
|
242
|
-
}
|
|
243
|
-
});
|
|
244
|
-
} catch (error) {
|
|
245
|
-
throw this.normalizeTransportError(error, profile);
|
|
246
|
-
}
|
|
247
|
-
const iterator = translate(result.stream)[Symbol.asyncIterator]();
|
|
248
|
-
let exhausted = false;
|
|
249
|
-
try {
|
|
250
|
-
while (true) {
|
|
251
|
-
const next = await watchdog.next(iterator);
|
|
252
|
-
if (next.done) {
|
|
253
|
-
exhausted = true;
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
yield next.value;
|
|
257
|
-
}
|
|
258
|
-
} catch (error) {
|
|
259
|
-
if (timeoutOf(watchdog.signal, "LLM_STREAM_IDLE_TIMEOUT") !== void 0) throw new LlmError(`OpenAI-compatible stream idle timeout after ${profile.streamIdleTimeoutMs}ms`, "TIMEOUT", { cause: error });
|
|
260
|
-
if (profile.timeoutMs !== void 0 && timeoutOf(watchdog.signal, "LLM_REQUEST_TIMEOUT") !== void 0) throw new LlmError(`OpenAI-compatible request timeout after ${profile.timeoutMs}ms`, "TIMEOUT", { cause: error });
|
|
261
|
-
if (options.signal?.aborted) throw new LlmError("OpenAI-compatible request aborted by caller", "ABORTED", { cause: error });
|
|
262
|
-
if (error instanceof LlmError) throw error;
|
|
263
|
-
throw this.normalizeTransportError(error, profile);
|
|
264
|
-
} finally {
|
|
265
|
-
consumer.abort("OpenAI-compatible stream consumer stopped");
|
|
266
|
-
if (!exhausted) try {
|
|
267
|
-
await iterator.return(void 0);
|
|
268
|
-
} catch {}
|
|
269
|
-
}
|
|
270
|
-
} finally {
|
|
271
|
-
watchdog[Symbol.dispose]();
|
|
272
|
-
overall?.[Symbol.dispose]();
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
/** Normalize an SDK/transport failure into a harness LlmError. */
|
|
276
|
-
normalizeTransportError(error, profile) {
|
|
277
|
-
if (error instanceof LlmError) return error;
|
|
278
|
-
if (APICallError.isInstance(error)) {
|
|
279
|
-
const providerError = providerErrorBody(error);
|
|
280
|
-
const message = typeof providerError?.message === "string" ? providerError.message : error.message;
|
|
281
|
-
const id = requestId(error.responseHeaders);
|
|
282
|
-
return new LlmError(message, httpErrorCode(error.statusCode ?? 0, providerError), {
|
|
283
|
-
...error.statusCode === void 0 ? {} : { status: error.statusCode },
|
|
284
|
-
...id === void 0 ? {} : { requestId: id },
|
|
285
|
-
cause: error
|
|
286
|
-
});
|
|
287
|
-
}
|
|
288
|
-
if (error instanceof Error) return new LlmError(`OpenAI-compatible API request to ${profile.baseURL} failed`, "TRANSPORT", { cause: error });
|
|
289
|
-
return new LlmError(`OpenAI-compatible API request to ${profile.baseURL} failed`, "TRANSPORT");
|
|
290
|
-
}
|
|
291
|
-
};
|
|
292
|
-
//#endregion
|
|
293
|
-
export { DEFAULT_CONTEXT_WINDOW, DEFAULT_MAX_REQUEST_IMAGE_BYTES, DEFAULT_MAX_TOKENS, DEFAULT_STREAM_IDLE_TIMEOUT_MS, OpenAICompatibleAdapter, PROVIDER_OPTIONS_KEY, REQUEST_TIMEOUT_CODE, STREAM_IDLE_TIMEOUT_CODE, convertUsage, httpErrorCode };
|
|
294
|
-
|
|
295
|
-
//# sourceMappingURL=adapter.mjs.map
|
package/lib/adapter.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.mjs","names":[],"sources":["../src/adapter.ts"],"sourcesContent":["/**\n * `OpenAICompatibleAdapter`: a multi-provider harness adapter built on\n * `@ai-sdk/openai-compatible`. One instance serves every provider route in\n * the plugin's `providers` dict; profile facts arrive through a thunk resolved\n * once per operation, so the registering plugin owns validation, layering, and\n * credential policy, and a changed profile reaches the next request without a\n * restart. The SDK owns wire serialization and SSE parsing; this adapter owns\n * harness message conversion, sampling-default merging (via\n * `serialize.ts`), chunk translation (via `translate.ts`), and error\n * normalization.\n * @module dsh-llm-openai-compatible/adapter\n */\n\nimport {\n CONTEXT_WINDOW_EXCEEDED_CODE,\n QUOTA_EXCEEDED_CODE,\n LlmAdapter,\n LlmError,\n ProviderRequestId,\n ReasoningEffortId,\n attributionHeaders,\n contentHasImage,\n isContextWindowExceededError,\n isQuotaExceededError,\n} from \"@deepseek-ai/dsh-llm\";\nimport type {\n GenerateOptions,\n LlmModelInfo,\n LlmProviderInfo,\n LlmResolvedModelInfo,\n ModelModality,\n ResolvedRetryPolicy,\n StreamChunk,\n} from \"@deepseek-ai/dsh-llm\";\nimport type { AttachmentStore } from \"@deepseek-ai/dsh-attachment\";\nimport type { CredentialRef } from \"@deepseek-ai/dsh-credentials\";\nimport { deadline, idleWatchdog, timeoutOf } from \"@deepseek-ai/dsh-timeout\";\nimport { APICallError } from \"@ai-sdk/provider\";\nimport type { LanguageModelV4, LanguageModelV4Usage } from \"@ai-sdk/provider\";\nimport { createOpenAICompatible } from \"@ai-sdk/openai-compatible\";\nimport { serializeCallOptions, serializeCallOptionsWithImages } from \"./serialize.ts\";\nimport { translate } from \"./translate.ts\";\n\n/** Default maximum idle interval while an adapter stream read is outstanding. */\nexport const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 300_000;\n/** Default combined request/response context capacity for unconfigured models. */\nexport const DEFAULT_CONTEXT_WINDOW = 262_144;\n/** Default per-request output-token cap for unconfigured models. */\nexport const DEFAULT_MAX_TOKENS = 32_768;\n/** Default bound on accumulated base64 image payload per request. */\nexport const DEFAULT_MAX_REQUEST_IMAGE_BYTES = 20 * 1024 * 1024;\n/** Code stamped on the idle-watchdog timeout reason. */\nexport const STREAM_IDLE_TIMEOUT_CODE = \"LLM_STREAM_IDLE_TIMEOUT\";\n/** Code stamped on the whole-request deadline timeout reason. */\nexport const REQUEST_TIMEOUT_CODE = \"LLM_REQUEST_TIMEOUT\";\n/** The provider options key the SDK forwards into the request body. */\nexport const PROVIDER_OPTIONS_KEY = \"openai-compatible\";\n\n/** Selectable reasoning effort levels for one provider route. */\nexport type ReasoningEffort = \"off\" | \"low\" | \"high\" | \"max\";\n\n/** One validated catalog model of a provider route. */\nexport interface ResolvedModelProfile {\n id: string;\n name?: string;\n description?: string;\n contextWindow?: number;\n maxTokens?: number;\n inputModalities: readonly ModelModality[];\n /**\n * Declared reasoning efforts: key = selectable level, value = wire\n * `reasoning_effort` spelling. `false` rejects the capability outright;\n * absent means the model carries no reasoning metadata. A `null` wire\n * spelling (only legal for `off`) means \"omit the field\".\n */\n reasoningEfforts?: false | Partial<Record<ReasoningEffort, string | null>>;\n}\n\n/** One validated provider route profile, detached and ready for per-request reads. */\nexport interface ResolvedProviderProfile {\n provider: string;\n displayName: string;\n /** Credential reference; absence means the route sends no authorization header. */\n apiKeyEnv?: CredentialRef;\n /** Required endpoint base; requests hit `${baseURL}/chat/completions`. */\n baseURL: string;\n headers?: Readonly<Record<string, string>>;\n // === sampling defaults (request-level values win) ===\n temperature?: number;\n topP?: number;\n topK?: number;\n presencePenalty?: number;\n frequencyPenalty?: number;\n seed?: number;\n /** Deployment default reasoning level; omission keeps the provider default. */\n reasoning?: ReasoningEffort;\n // === model catalog ===\n models: readonly ResolvedModelProfile[];\n defaultContextWindow: number;\n defaultMaxTokens: number;\n // === transport ===\n maxRequestImageBytes: number;\n streamIdleTimeoutMs: number;\n /** Whole-request deadline in milliseconds; unset arms no overall timer. */\n timeoutMs?: number;\n retryPolicy: ResolvedRetryPolicy;\n}\n\n/** Constructor options for {@link OpenAICompatibleAdapter}: the hooks the plugin owns. */\nexport interface OpenAICompatibleAdapterOptions {\n /** Current validated profiles by provider route; called once per operation. */\n profiles: () => ReadonlyMap<string, ResolvedProviderProfile>;\n /**\n * Resolve the credential for one already-resolved profile; called once per\n * stream call and frozen for that call. `undefined` means the route sends no\n * authorization header (an unauthenticated endpoint such as local Ollama).\n */\n resolveApiKey: (\n provider: string,\n profile: ResolvedProviderProfile,\n ) => Promise<string | undefined>;\n /** Resolve the harness anonymous user id for request attribution headers. */\n resolveUserId: () => string;\n /** Resolve the optional durable attachment service at request time. */\n resolveAttachments?: () => AttachmentStore | undefined;\n}\n\n/** The wire usage shape the SDK converter receives (subset of the OpenAI shape). */\ninterface WireUsageLike {\n prompt_tokens?: number | null | undefined;\n completion_tokens?: number | null | undefined;\n prompt_tokens_details?: { cached_tokens?: number | null | undefined } | null | undefined;\n /** DeepSeek-dialect cache hits folded into prompt_tokens. */\n prompt_cache_hit_tokens?: number | null | undefined;\n completion_tokens_details?: { reasoning_tokens?: number | null | undefined } | null | undefined;\n}\n\n/**\n * Convert provider token accounting into disjoint AI SDK usage. OpenAI's\n * `prompt_tokens_details.cached_tokens` and the DeepSeek dialect's\n * `prompt_cache_hit_tokens` both report cache reads folded into\n * `prompt_tokens`; the harness convention is disjoint counts, so cache reads\n * are split out regardless of which field the endpoint used.\n */\nexport function convertUsage(usage: WireUsageLike | null | undefined): LanguageModelV4Usage {\n if (usage == null) {\n return {\n inputTokens: { total: 0, noCache: 0, cacheRead: void 0, cacheWrite: void 0 },\n outputTokens: { total: 0, text: void 0, reasoning: void 0 },\n };\n }\n const promptTokens = usage.prompt_tokens ?? 0;\n const completionTokens = usage.completion_tokens ?? 0;\n const cacheRead =\n usage.prompt_tokens_details?.cached_tokens ?? usage.prompt_cache_hit_tokens ?? 0;\n const reasoningTokens = usage.completion_tokens_details?.reasoning_tokens ?? 0;\n return {\n inputTokens: {\n total: promptTokens,\n noCache: Math.max(0, promptTokens - cacheRead),\n cacheRead,\n cacheWrite: void 0,\n },\n outputTokens: {\n total: completionTokens,\n text: Math.max(0, completionTokens - reasoningTokens),\n reasoning: reasoningTokens,\n },\n };\n}\n\n/** The first real blocks of one resolve: display + catalog metadata. */\nfunction modelInfo(profile: ResolvedProviderProfile, model: ResolvedModelProfile): LlmModelInfo {\n return {\n provider: profile.provider,\n id: model.id,\n name: model.name ?? model.id,\n ...(model.description === void 0 ? {} : { description: model.description }),\n inputModalities: [...model.inputModalities],\n };\n}\n\n/** The harness reasoning info for one model, honoring its declared efforts. */\nfunction reasoningInfo(\n model: ResolvedModelProfile | undefined,\n defaultEffort: ReasoningEffort | undefined,\n): Pick<LlmResolvedModelInfo, \"reasoning\"> {\n const declaration = model?.reasoningEfforts;\n if (declaration === void 0 || declaration === false) return {};\n const entries = Object.entries(declaration) as [ReasoningEffort, string | null | undefined][];\n const efforts = entries.map(([id]) => ({\n id: ReasoningEffortId(id),\n name: `${id.charAt(0).toUpperCase()}${id.slice(1)}`,\n }));\n return {\n reasoning: {\n efforts,\n // A configured default the model does not declare is silently dropped\n // here (describing a model must never throw); the request path still\n // refuses it, which is where a bad deployment default belongs.\n ...(defaultEffort !== void 0 && declaration[defaultEffort] !== void 0\n ? { defaultEffort: ReasoningEffortId(defaultEffort) }\n : {}),\n },\n };\n}\n\n/**\n * Map an HTTP status to a stable LlmError code.\n * @param status - status of a non-2xx provider response.\n * @param error - parsed provider error body, when available.\n * @returns the normalized harness error code.\n */\nexport function httpErrorCode(\n status: number,\n error?: { code?: unknown; type?: unknown; message?: unknown },\n): string {\n if (status === 401 || status === 403) return \"AUTH\";\n if (status === 413) return \"INVALID_REQUEST\";\n const detail = [error?.code, error?.type, error?.message].filter(Boolean).join(\" \");\n if (isQuotaExceededError(detail)) return QUOTA_EXCEEDED_CODE;\n if (status === 429) return \"RATE_LIMIT\";\n if (status === 400) {\n if (isContextWindowExceededError(detail)) return CONTEXT_WINDOW_EXCEEDED_CODE;\n return \"INVALID_REQUEST\";\n }\n if (status >= 500) return \"SERVER\";\n return `HTTP_${status}`;\n}\n\n/** Parse a provider error body out of an API-call error's JSON response body. */\nfunction providerErrorBody(\n error: APICallError,\n): { code?: unknown; type?: unknown; message?: unknown } | undefined {\n if (error.responseBody === void 0) return void 0;\n try {\n const parsed = JSON.parse(error.responseBody) as {\n error?: { code?: unknown; type?: unknown; message?: unknown };\n };\n return parsed.error;\n } catch {\n return void 0;\n }\n}\n\n/** Extract a provider-issued request id from response headers when present. */\nfunction requestId(headers: Record<string, string> | undefined): ProviderRequestId | undefined {\n if (headers === void 0) return void 0;\n const value = headers[\"x-request-id\"] ?? headers[\"x-openai-compatible-request-id\"];\n return value === void 0 || value.length === 0 ? void 0 : ProviderRequestId(value);\n}\n\n/**\n * Multi-provider adapter. Each operation reads the current profiles, so a\n * configuration change reaches the next request without a restart; the\n * underlying SDK provider instance is cached per resolved profile and rebuilt\n * when the profile object changes.\n */\nexport class OpenAICompatibleAdapter extends LlmAdapter {\n private readonly config: OpenAICompatibleAdapterOptions;\n private readonly sdkProviders = new Map<ResolvedProviderProfile, Map<string, LanguageModelV4>>();\n\n constructor(config: OpenAICompatibleAdapterOptions) {\n super();\n this.config = config;\n }\n\n /** The profile for one route, or the not-owned failure. */\n private profileOf(provider: string): ResolvedProviderProfile {\n const profile = this.config.profiles().get(provider);\n if (profile === void 0)\n throw new LlmError(\n `OpenAI-compatible adapter does not own provider \"${provider}\"`,\n \"NO_ADAPTER\",\n );\n return profile;\n }\n\n /** The configured descriptor for one exact route/model pair; unlisted ids pass through. */\n private modelOf(\n profile: ResolvedProviderProfile,\n model: string,\n ): ResolvedModelProfile | undefined {\n return profile.models.find((entry) => entry.id === model);\n }\n\n /** The SDK chat model for one route/model, cached per resolved profile. */\n private sdkModel(profile: ResolvedProviderProfile, modelId: string): LanguageModelV4 {\n let byModel = this.sdkProviders.get(profile);\n if (byModel === void 0) {\n byModel = new Map();\n this.sdkProviders.set(profile, byModel);\n }\n let model = byModel.get(modelId);\n if (model === void 0) {\n const provider = createOpenAICompatible({\n name: PROVIDER_OPTIONS_KEY,\n baseURL: profile.baseURL,\n headers: { ...profile.headers, ...attributionHeaders() },\n includeUsage: true,\n convertUsage,\n });\n model = provider.chatModel(modelId);\n byModel.set(modelId, model);\n }\n return model;\n }\n\n providerInfo(provider: string): LlmProviderInfo {\n return {\n id: provider,\n name: this.config.profiles().get(provider)?.displayName ?? provider,\n };\n }\n\n providerRetryPolicy(provider: string): ResolvedRetryPolicy | undefined {\n return this.config.profiles().get(provider)?.retryPolicy;\n }\n\n listModels(provider: string): Promise<readonly LlmModelInfo[]> {\n const profile = this.profileOf(provider);\n return Promise.resolve(profile.models.map((model) => modelInfo(profile, model)));\n }\n\n resolveModel(\n provider: string,\n model: string,\n _signal?: AbortSignal,\n ): Promise<LlmResolvedModelInfo> {\n const profile = this.profileOf(provider);\n const configured = this.modelOf(profile, model);\n const contextWindow = configured?.contextWindow ?? profile.defaultContextWindow;\n const maxTokens = configured?.maxTokens ?? profile.defaultMaxTokens;\n return Promise.resolve({\n ...(configured === void 0\n ? { provider, id: model, name: model, inputModalities: [\"text\" as const] }\n : modelInfo(profile, configured)),\n context: { contextWindow },\n ...(maxTokens !== void 0 ? { defaultMaxTokens: maxTokens } : {}),\n ...reasoningInfo(configured, profile.reasoning),\n });\n }\n\n async *stream(options: GenerateOptions): AsyncGenerator<StreamChunk> {\n const profile = this.profileOf(options.provider);\n const model = this.modelOf(profile, options.model);\n const hasImages = options.messages.some((message) => contentHasImage(message.content));\n let attachments: AttachmentStore | undefined;\n if (hasImages) {\n if (model?.inputModalities.includes(\"image\") !== true) {\n throw new LlmError(\n `OpenAI-compatible model \"${options.model}\" does not accept image input.`,\n \"UNSUPPORTED_CONTENT\",\n );\n }\n attachments = this.config.resolveAttachments?.();\n if (attachments === void 0)\n throw new LlmError(\n \"OpenAI-compatible image conversion requires the durable attachment service.\",\n \"UNSUPPORTED_CONTENT\",\n );\n }\n const apiKey = await this.config.resolveApiKey(options.provider, profile);\n const userId = this.config.resolveUserId();\n const consumer = new AbortController();\n const upstream =\n options.signal === void 0\n ? consumer.signal\n : AbortSignal.any([options.signal, consumer.signal]);\n const overall =\n profile.timeoutMs === void 0\n ? void 0\n : deadline(upstream, profile.timeoutMs, REQUEST_TIMEOUT_CODE);\n const watchdog = idleWatchdog(\n overall?.signal ?? upstream,\n profile.streamIdleTimeoutMs,\n STREAM_IDLE_TIMEOUT_CODE,\n );\n try {\n const callOptions =\n attachments === void 0\n ? await serializeCallOptions(options, profile, model)\n : await serializeCallOptionsWithImages(options, profile, model, {\n attachments,\n maxRequestImageBytes: profile.maxRequestImageBytes,\n signal: watchdog.signal,\n });\n const sdkModel = this.sdkModel(profile, options.model);\n let result;\n try {\n result = await sdkModel.doStream({\n ...callOptions,\n abortSignal: watchdog.signal,\n headers: {\n ...(apiKey === void 0 ? {} : { authorization: `Bearer ${apiKey}` }),\n \"x-openai-compatible-harness-user-id\": String(userId),\n ...(options.sessionId !== void 0\n ? { \"x-openai-compatible-harness-session-id\": String(options.sessionId) }\n : {}),\n ...(options.purpose === \"compaction\"\n ? { \"x-openai-compatible-harness-compact\": \"1\" }\n : {}),\n },\n });\n } catch (error) {\n throw this.normalizeTransportError(error, profile);\n }\n const iterator = translate(result.stream)[Symbol.asyncIterator]();\n let exhausted = false;\n try {\n while (true) {\n const next = await watchdog.next(iterator);\n if (next.done) {\n exhausted = true;\n return;\n }\n yield next.value;\n }\n } catch (error) {\n if (timeoutOf(watchdog.signal, STREAM_IDLE_TIMEOUT_CODE) !== void 0) {\n throw new LlmError(\n `OpenAI-compatible stream idle timeout after ${profile.streamIdleTimeoutMs}ms`,\n \"TIMEOUT\",\n { cause: error },\n );\n }\n if (\n profile.timeoutMs !== void 0 &&\n timeoutOf(watchdog.signal, REQUEST_TIMEOUT_CODE) !== void 0\n ) {\n throw new LlmError(\n `OpenAI-compatible request timeout after ${profile.timeoutMs}ms`,\n \"TIMEOUT\",\n { cause: error },\n );\n }\n if (options.signal?.aborted)\n throw new LlmError(\"OpenAI-compatible request aborted by caller\", \"ABORTED\", {\n cause: error,\n });\n if (error instanceof LlmError) throw error;\n throw this.normalizeTransportError(error, profile);\n } finally {\n consumer.abort(\"OpenAI-compatible stream consumer stopped\");\n if (!exhausted) {\n try {\n await iterator.return(void 0);\n } catch {\n // The transport already aborted; teardown is best-effort.\n }\n }\n }\n } finally {\n watchdog[Symbol.dispose]();\n overall?.[Symbol.dispose]();\n }\n }\n\n /** Normalize an SDK/transport failure into a harness LlmError. */\n private normalizeTransportError(error: unknown, profile: ResolvedProviderProfile): LlmError {\n if (error instanceof LlmError) return error;\n if (APICallError.isInstance(error)) {\n const providerError = providerErrorBody(error);\n const message =\n typeof providerError?.message === \"string\" ? providerError.message : error.message;\n const id = requestId(error.responseHeaders);\n return new LlmError(message, httpErrorCode(error.statusCode ?? 0, providerError), {\n ...(error.statusCode === void 0 ? {} : { status: error.statusCode }),\n ...(id === void 0 ? {} : { requestId: id }),\n cause: error,\n });\n }\n if (error instanceof Error) {\n return new LlmError(\n `OpenAI-compatible API request to ${profile.baseURL} failed`,\n \"TRANSPORT\",\n { cause: error },\n );\n }\n return new LlmError(`OpenAI-compatible API request to ${profile.baseURL} failed`, \"TRANSPORT\");\n }\n}\n"],"mappings":"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"}
|
package/lib/index.d.mts
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { DEFAULT_CONTEXT_WINDOW, DEFAULT_MAX_REQUEST_IMAGE_BYTES, DEFAULT_MAX_TOKENS, DEFAULT_STREAM_IDLE_TIMEOUT_MS, OpenAICompatibleAdapter, OpenAICompatibleAdapterOptions, ReasoningEffort, ResolvedModelProfile, ResolvedProviderProfile } from "./adapter.mjs";
|
|
2
|
-
import z from "@deepseek-ai/schemastery";
|
|
3
|
-
import { ModelModality, RetryPolicyConfig } from "@deepseek-ai/dsh-llm";
|
|
4
|
-
import { Context } from "@deepseek-ai/cordis";
|
|
5
|
-
//#region src/index.d.ts
|
|
6
|
-
declare const name = "llm-openai-compatible";
|
|
7
|
-
declare const inject: string[];
|
|
8
|
-
declare const NS: import("@deepseek-ai/dsh-settings").SettingsNamespace;
|
|
9
|
-
/** Selectable reasoning levels a profile or model may declare. */
|
|
10
|
-
declare const REASONING_LEVELS: readonly ["off", "low", "high", "max"];
|
|
11
|
-
/** Accepted model input modalities. */
|
|
12
|
-
declare const MODEL_MODALITIES: readonly ["text", "image"];
|
|
13
|
-
/** Source shape of one model catalog entry. */
|
|
14
|
-
interface ModelProfileSource {
|
|
15
|
-
id: string;
|
|
16
|
-
name?: string;
|
|
17
|
-
description?: string;
|
|
18
|
-
contextWindow?: number;
|
|
19
|
-
maxTokens?: number;
|
|
20
|
-
inputModalities?: ModelModality[];
|
|
21
|
-
reasoningEfforts?: false | Partial<Record<ReasoningEffort, string | null>>;
|
|
22
|
-
}
|
|
23
|
-
/** Source shape of one provider route profile; the `providers` dict key IS the route. */
|
|
24
|
-
interface ProviderProfileSource {
|
|
25
|
-
/** Credential reference (environment-variable name); absence sends no authorization header. */
|
|
26
|
-
apiKeyEnv?: string;
|
|
27
|
-
/** Name shown by configuration surfaces; defaults to the route key. */
|
|
28
|
-
displayName?: string;
|
|
29
|
-
/** Required endpoint base; requests hit `${baseURL}/chat/completions`. */
|
|
30
|
-
baseURL: string;
|
|
31
|
-
/** Extra request headers, merged under the mandatory attribution headers. */
|
|
32
|
-
headers?: Record<string, string>;
|
|
33
|
-
temperature?: number;
|
|
34
|
-
topP?: number;
|
|
35
|
-
topK?: number;
|
|
36
|
-
presencePenalty?: number;
|
|
37
|
-
frequencyPenalty?: number;
|
|
38
|
-
seed?: number;
|
|
39
|
-
/** Deployment default reasoning level; omission keeps the provider default. */
|
|
40
|
-
reasoning?: ReasoningEffort;
|
|
41
|
-
/** This route's model catalog; omission serves an empty catalog (unlisted ids pass through). */
|
|
42
|
-
models?: ModelProfileSource[];
|
|
43
|
-
defaultContextWindow?: number;
|
|
44
|
-
defaultMaxTokens?: number;
|
|
45
|
-
maxRequestImageBytes?: number;
|
|
46
|
-
streamIdleTimeoutMs?: number;
|
|
47
|
-
/** Whole-request deadline in milliseconds; unset arms no overall timer. */
|
|
48
|
-
timeoutMs?: number;
|
|
49
|
-
retryPolicy?: RetryPolicyConfig;
|
|
50
|
-
}
|
|
51
|
-
/** Plugin configuration: the provider routes this instance owns. */
|
|
52
|
-
interface Config {
|
|
53
|
-
/** Provider routes, keyed by route. An empty (or omitted) dict is the dormant posture. */
|
|
54
|
-
providers?: Record<string, ProviderProfileSource>;
|
|
55
|
-
}
|
|
56
|
-
/** Runtime schema for {@link Config}. */
|
|
57
|
-
declare const Config: z<Config>;
|
|
58
|
-
/**
|
|
59
|
-
* The one explicit resolve step from a raw profile to validated connection
|
|
60
|
-
* facts. Programmatic construction may bypass Schemastery normalization, so
|
|
61
|
-
* every default and bound is re-judged here — for the composition entry at
|
|
62
|
-
* load (fail loud) and for each settings snapshot at its first use.
|
|
63
|
-
* @param provider - the route key owning this profile.
|
|
64
|
-
* @param source - raw profile from config or a resolved settings snapshot.
|
|
65
|
-
* @returns validated connection facts plus the credential reference.
|
|
66
|
-
*/
|
|
67
|
-
declare function resolveAdapterOptions(provider: string, source: ProviderProfileSource): ResolvedProviderProfile;
|
|
68
|
-
/**
|
|
69
|
-
* Validate profiles and return a detached route-keyed map suitable for
|
|
70
|
-
* per-request reads. This is the one explicit resolve step, so an omitted dict
|
|
71
|
-
* resolves to the empty (dormant) route set here rather than through a hidden
|
|
72
|
-
* fallback.
|
|
73
|
-
* @param providers - configured provider profiles keyed by route.
|
|
74
|
-
* @returns validated profiles in configuration order.
|
|
75
|
-
*/
|
|
76
|
-
declare function resolveProfiles(providers: Readonly<Record<string, ProviderProfileSource>> | undefined): Map<string, ResolvedProviderProfile>;
|
|
77
|
-
/**
|
|
78
|
-
* Reject a section this adapter could not serve. Registered as the settings
|
|
79
|
-
* namespace's validator, so an unserviceable profile is refused where it is
|
|
80
|
-
* written instead of being stored and then quietly disabling every route in
|
|
81
|
-
* the namespace.
|
|
82
|
-
* @param config - the resolved section to check.
|
|
83
|
-
*/
|
|
84
|
-
declare function assertServiceable(config: Config): void;
|
|
85
|
-
/** Register one generic OpenAI-compatible adapter for all configured provider routes. */
|
|
86
|
-
declare function apply(ctx: Context, config: Config): void;
|
|
87
|
-
//#endregion
|
|
88
|
-
export { Config, DEFAULT_CONTEXT_WINDOW, DEFAULT_MAX_REQUEST_IMAGE_BYTES, DEFAULT_MAX_TOKENS, DEFAULT_STREAM_IDLE_TIMEOUT_MS, MODEL_MODALITIES, ModelProfileSource, NS, OpenAICompatibleAdapter, type OpenAICompatibleAdapterOptions, ProviderProfileSource, REASONING_LEVELS, type ReasoningEffort, type ResolvedModelProfile, type ResolvedProviderProfile, apply, assertServiceable, inject, name, resolveAdapterOptions, resolveProfiles };
|
|
89
|
-
//# sourceMappingURL=index.d.mts.map
|
package/lib/index.d.mts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;cAsDa;cACA;cACA,wCAAE;;cAGF;;cAEA;;UAGI;EACf;EACA;EACA;EACA;EACA;EACA,kBAAkB;EAClB,2BAA2B,QAAQ,OAAO;;;UAI3B;;EAEf;;EAEA;;EAEA;;EAEA,UAAU;EAEV;EACA;EACA;EACA;EACA;EACA;;EAEA,YAAY;;EAEZ,SAAS;EACT;EACA;EACA;EACA;;EAEA;EACA,cAAc;;;UAIC;;EAEf,YAAY,eAAe;;;cAuChB,QAAQ,EAAE;;;;;;;;;;iBA8HP,sBACd,kBACA,QAAQ,wBACP;;;;;;;;;iBAgHa,gBACd,WAAW,SAAS,eAAe,sCAClC,YAAY;;;;;;;;iBAmBC,kBAAkB,QAAQ;;iBAoD1B,MAAM,KAAK,SAAS,QAAQ"}
|