dsh-opendesign-corouter 0.1.1 → 0.1.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/README.md +6 -4
- package/lib/adapter.d.ts +3 -0
- package/lib/client.d.ts +117 -12
- package/lib/client.js +834 -123
- package/lib/config.d.ts +11 -0
- package/lib/generated/typert.host.js +100 -11
- package/lib/generated/typert.remote-client.d.ts +2 -0
- package/lib/generated/typert.remote-client.js +100 -11
- package/lib/index.d.ts +16 -0
- package/lib/index.js +1532 -203
- package/lib/index.js.map +4 -4
- package/lib/model-discovery.d.ts +39 -0
- package/lib/model-settings.d.ts +45 -0
- package/lib/protocol-options.d.ts +11 -0
- package/lib/protocols/chat-completions.d.ts +10 -0
- package/lib/protocols/errors.d.ts +45 -0
- package/lib/protocols/headers.d.ts +14 -0
- package/lib/protocols/index.d.ts +17 -0
- package/lib/protocols/messages.d.ts +29 -0
- package/lib/protocols/parts.d.ts +22 -0
- package/lib/protocols/responses.d.ts +32 -0
- package/lib/protocols/sse.d.ts +25 -0
- package/lib/protocols/types.d.ts +39 -0
- package/lib/protocols/usage.d.ts +34 -0
- package/lib/quota.d.ts +21 -1
- package/lib/remote.d.ts +14 -0
- package/lib/stream.d.ts +11 -3
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -52,13 +52,13 @@ import { randomUUID as randomUUID2, createHash } from "node:crypto";
|
|
|
52
52
|
import { credentialKey, credentialRef } from "@deepseek-ai/dsh-credentials";
|
|
53
53
|
import { launchEnvironmentOf } from "@deepseek-ai/dsh-launch-environment";
|
|
54
54
|
import { Remote, RemoteError, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
|
|
55
|
+
import { LlmError as LlmError11 } from "@deepseek-ai/dsh-llm";
|
|
55
56
|
|
|
56
57
|
// src/adapter.ts
|
|
57
58
|
import {
|
|
58
59
|
LlmAdapter,
|
|
59
|
-
LlmError as
|
|
60
|
+
LlmError as LlmError10,
|
|
60
61
|
ReasoningEffortId,
|
|
61
|
-
attributionHeaders,
|
|
62
62
|
assertUsableApiKey
|
|
63
63
|
} from "@deepseek-ai/dsh-llm";
|
|
64
64
|
|
|
@@ -210,24 +210,134 @@ var MODELS = [
|
|
|
210
210
|
{ id: "qwen3.7-max", name: "Qwen3.7 Max", group: "paid" }
|
|
211
211
|
];
|
|
212
212
|
var BY_ID = new Map(MODELS.map((model) => [model.id, model]));
|
|
213
|
-
function modelsFor(group2) {
|
|
214
|
-
return MODELS.filter((model) => model.group === group2);
|
|
215
|
-
}
|
|
216
213
|
function findModel(id) {
|
|
217
214
|
return BY_ID.get(id);
|
|
218
215
|
}
|
|
219
216
|
|
|
220
217
|
// src/config.ts
|
|
221
218
|
import Schema from "@deepseek-ai/schemastery";
|
|
219
|
+
|
|
220
|
+
// src/protocol-options.ts
|
|
221
|
+
var API_PROTOCOL_IDS = ["chat-completions", "responses", "messages"];
|
|
222
|
+
function openAiBaseURL(baseURL) {
|
|
223
|
+
const base = baseURL.replace(/\/+$/u, "");
|
|
224
|
+
return new URL(base).pathname === "/" ? `${base}/v1` : base;
|
|
225
|
+
}
|
|
226
|
+
function inferenceEndpoint(baseURL, protocol) {
|
|
227
|
+
const suffix = protocol === "responses" ? "/responses" : protocol === "messages" ? "/messages" : "/chat/completions";
|
|
228
|
+
return baseURL.trim() === "" ? suffix : `${openAiBaseURL(baseURL.trim())}${suffix}`;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// src/util.ts
|
|
232
|
+
function record(value) {
|
|
233
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
234
|
+
}
|
|
235
|
+
function finite(value) {
|
|
236
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
237
|
+
}
|
|
238
|
+
function safeSecret(raw) {
|
|
239
|
+
const value = raw.trim();
|
|
240
|
+
if (!value || /[^\x20-\x7e]/u.test(value)) throw new Error("\u51ED\u636E\u4E3A\u7A7A\u6216\u542B\u6709\u4E0D\u53EF\u7528\u4E8E HTTP \u8BF7\u6C42\u5934\u7684\u5B57\u7B26");
|
|
241
|
+
return value;
|
|
242
|
+
}
|
|
243
|
+
function combinedSignal(signal, timeoutMs) {
|
|
244
|
+
const deadline = AbortSignal.timeout(timeoutMs);
|
|
245
|
+
return signal ? AbortSignal.any([signal, deadline]) : deadline;
|
|
246
|
+
}
|
|
247
|
+
function sleep(ms, signal) {
|
|
248
|
+
return new Promise((resolve) => {
|
|
249
|
+
if (signal.aborted) return resolve();
|
|
250
|
+
const done = () => {
|
|
251
|
+
clearTimeout(timer);
|
|
252
|
+
signal.removeEventListener("abort", done);
|
|
253
|
+
resolve();
|
|
254
|
+
};
|
|
255
|
+
const timer = setTimeout(done, ms);
|
|
256
|
+
signal.addEventListener("abort", done, { once: true });
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// src/model-settings.ts
|
|
261
|
+
var MAX_ENTRIES = 512;
|
|
262
|
+
var MAX_MODEL_ID_LENGTH = 128;
|
|
263
|
+
var MODEL_ID = /^[A-Za-z0-9._:/-]{1,128}$/u;
|
|
264
|
+
var MAX_TEMPERATURE = 2;
|
|
265
|
+
var MAX_TOP_P = 1;
|
|
266
|
+
var MAX_TOKENS = 1e7;
|
|
267
|
+
var MAX_CONTEXT_WINDOW = 1e8;
|
|
268
|
+
var REASONING_EFFORTS = ["none", "minimal", "low", "medium", "high", "xhigh", "ultra", "max"];
|
|
269
|
+
var RESERVED_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
270
|
+
var SETTINGS_FIELDS = /* @__PURE__ */ new Set(["enabled", "temperature", "topP", "maxTokens", "contextWindow", "reasoningEffort"]);
|
|
271
|
+
function flag(value) {
|
|
272
|
+
if (typeof value !== "boolean") throw new Error("\u6A21\u578B\u8BBE\u7F6E\u7684 enabled \u5FC5\u987B\u662F\u5E03\u5C14\u503C");
|
|
273
|
+
return value;
|
|
274
|
+
}
|
|
275
|
+
function bounded(value, max, label) {
|
|
276
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > max) throw new Error(`\u6A21\u578B\u8BBE\u7F6E\u7684 ${label} \u5FC5\u987B\u662F 0 \u5230 ${max} \u4E4B\u95F4\u7684\u6709\u9650\u6570\u5B57`);
|
|
277
|
+
return value;
|
|
278
|
+
}
|
|
279
|
+
function capacity(value, max, label) {
|
|
280
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1 || value > max) throw new Error(`\u6A21\u578B\u8BBE\u7F6E\u7684 ${label} \u5FC5\u987B\u662F 1 \u5230 ${max} \u4E4B\u95F4\u7684\u6574\u6570`);
|
|
281
|
+
return value;
|
|
282
|
+
}
|
|
283
|
+
function effort(value) {
|
|
284
|
+
const hit = REASONING_EFFORTS.find((candidate) => candidate === value);
|
|
285
|
+
if (hit === void 0) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u7684 reasoningEffort \u4E0D\u5728\u5141\u8BB8\u7684\u53D6\u503C\u5185");
|
|
286
|
+
return hit;
|
|
287
|
+
}
|
|
288
|
+
function settingsOf(value) {
|
|
289
|
+
if (!record(value)) throw new Error("\u6BCF\u4E2A\u6A21\u578B\u7684\u8BBE\u7F6E\u5FC5\u987B\u662F\u5BF9\u8C61");
|
|
290
|
+
for (const field of Object.keys(value)) {
|
|
291
|
+
if (!SETTINGS_FIELDS.has(field)) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u542B\u6709\u4E0D\u652F\u6301\u7684\u5B57\u6BB5");
|
|
292
|
+
}
|
|
293
|
+
const out = {};
|
|
294
|
+
if (value.enabled !== void 0) out.enabled = flag(value.enabled);
|
|
295
|
+
if (value.temperature !== void 0) out.temperature = bounded(value.temperature, MAX_TEMPERATURE, "temperature");
|
|
296
|
+
if (value.topP !== void 0) out.topP = bounded(value.topP, MAX_TOP_P, "topP");
|
|
297
|
+
if (value.maxTokens !== void 0) out.maxTokens = capacity(value.maxTokens, MAX_TOKENS, "maxTokens");
|
|
298
|
+
if (value.contextWindow !== void 0) out.contextWindow = capacity(value.contextWindow, MAX_CONTEXT_WINDOW, "contextWindow");
|
|
299
|
+
if (value.reasoningEffort !== void 0) out.reasoningEffort = effort(value.reasoningEffort);
|
|
300
|
+
return out;
|
|
301
|
+
}
|
|
302
|
+
function parseModelSettingsMap(value) {
|
|
303
|
+
if (!record(value)) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u5FC5\u987B\u662F\u5BF9\u8C61");
|
|
304
|
+
const keys = Object.keys(value);
|
|
305
|
+
if (keys.length > MAX_ENTRIES) throw new Error(`\u6A21\u578B\u8BBE\u7F6E\u6700\u591A\u5141\u8BB8 ${MAX_ENTRIES} \u4E2A\u6A21\u578B`);
|
|
306
|
+
const out = {};
|
|
307
|
+
for (const id of keys) {
|
|
308
|
+
if (RESERVED_KEYS.has(id)) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u4E0D\u5141\u8BB8\u4FDD\u7559\u5B57\u6BB5\u540D");
|
|
309
|
+
if (!MODEL_ID.test(id)) throw new Error(`\u6A21\u578B ID \u5FC5\u987B\u662F 1\u2013${MAX_MODEL_ID_LENGTH} \u4F4D\u5B57\u6BCD\u3001\u6570\u5B57\u6216 . _ : / -`);
|
|
310
|
+
out[id] = settingsOf(value[id]);
|
|
311
|
+
}
|
|
312
|
+
return out;
|
|
313
|
+
}
|
|
314
|
+
function applyModelSettings(row, settings) {
|
|
315
|
+
const { contextWindow, maxTokens } = settings ?? {};
|
|
316
|
+
if (contextWindow === void 0 && maxTokens === void 0) return { ...row };
|
|
317
|
+
return {
|
|
318
|
+
...row,
|
|
319
|
+
...contextWindow === void 0 ? {} : { contextWindow: capacity(contextWindow, MAX_CONTEXT_WINDOW, "contextWindow") },
|
|
320
|
+
...maxTokens === void 0 ? {} : { maxOutputTokens: capacity(maxTokens, MAX_TOKENS, "maxTokens") }
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
function isModelEnabled(id, settings = {}) {
|
|
324
|
+
if (RESERVED_KEYS.has(id) || !Object.hasOwn(settings, id)) return true;
|
|
325
|
+
return settings[id]?.enabled !== false;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// src/config.ts
|
|
222
329
|
var SETTINGS_NS = "llm-opendesign-corouter";
|
|
223
330
|
var PLAN_PROVIDER = "opendesign-corouter-plan";
|
|
224
331
|
var PAID_PROVIDER = "opendesign-corouter-paid";
|
|
225
332
|
var DEFAULT_BASE_URL = "https://amr-link.open-design.ai/v1";
|
|
226
333
|
var Config = Schema.object({
|
|
227
334
|
apiKeyEnv: Schema.string().pattern(/^[A-Za-z_][A-Za-z0-9_]*$/u).default("COROUTER_API_KEY").description("CoRouter API Key \u7684\u73AF\u5883\u53D8\u91CF\u6216 DSH \u51ED\u636E\u5F15\u7528\u540D").volatile(),
|
|
228
|
-
baseURL: Schema.transform(Schema.string(), (value) => assertBaseURL(value)).default(DEFAULT_BASE_URL).description("
|
|
335
|
+
baseURL: Schema.transform(Schema.string(), (value) => assertBaseURL(value)).default(DEFAULT_BASE_URL).description("CoRouter \u7F51\u5173\u5730\u5740\uFF1B\u4E0D\u7528\u4E8E\u63A7\u5236\u53F0\u767B\u5F55\u6216\u989D\u5EA6\u67E5\u8BE2").volatile(),
|
|
336
|
+
apiProtocol: Schema.union(API_PROTOCOL_IDS).default("chat-completions").description("\u63A8\u7406 API \u534F\u8BAE\uFF1B\u6A21\u578B\u53D1\u73B0\u59CB\u7EC8\u4F7F\u7528 OpenAI models \u63A5\u53E3").volatile(),
|
|
337
|
+
modelSettings: Schema.transform(Schema.dict(Schema.any()), (value) => parseModelSettingsMap(value)).default({}).description("\u9010\u6A21\u578B\u542F\u7528\u72B6\u6001\u548C\u8BF7\u6C42\u53C2\u6570").volatile(),
|
|
229
338
|
useDesktopCredential: Schema.boolean().default(true).description("\u4F18\u5148\u8BFB\u53D6 OpenDesign \u684C\u9762\u7AEF\u767B\u5F55\u6001\u67E5\u8BE2\u989D\u5EA6").volatile(),
|
|
230
339
|
emptyTurnGuard: Schema.boolean().default(true).description("\u76D1\u63A7\u5957\u9910\u8D60\u9001\u6A21\u578B\u7684\u7A7A\u8F6E/\u534F\u8BAE\u6B8B\u6E23\uFF1B\u7531\u5BBF\u4E3B\u91CD\u8BD5\u7B56\u7565\u5904\u7406\uFF0C\u53EF\u80FD\u989D\u5916\u6D88\u8017\u8D60\u9001\u989D\u5EA6").volatile(),
|
|
340
|
+
quotaRefreshSeconds: Schema.natural().min(5).max(3600).default(60).description("\u989D\u5EA6\u81EA\u52A8\u5237\u65B0\u95F4\u9694\uFF08\u79D2\uFF09").volatile(),
|
|
231
341
|
requestTimeoutMs: Schema.natural().min(1e3).max(36e5).default(18e5).description("\u63A8\u7406\u8BF7\u6C42\u603B\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09").volatile(),
|
|
232
342
|
streamIdleTimeoutMs: Schema.natural().min(1e3).max(18e5).default(3e5).description("\u6D41\u5F0F\u54CD\u5E94\u65E0\u6570\u636E\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09").volatile()
|
|
233
343
|
});
|
|
@@ -246,6 +356,9 @@ function readConfig(config) {
|
|
|
246
356
|
return {
|
|
247
357
|
apiKeyEnv: config.apiKeyEnv.get(),
|
|
248
358
|
baseURL: assertBaseURL(config.baseURL.get()),
|
|
359
|
+
apiProtocol: config.apiProtocol.get(),
|
|
360
|
+
modelSettings: parseModelSettingsMap(config.modelSettings.get()),
|
|
361
|
+
quotaRefreshSeconds: config.quotaRefreshSeconds.get(),
|
|
249
362
|
useDesktopCredential: config.useDesktopCredential.get(),
|
|
250
363
|
emptyTurnGuard: config.emptyTurnGuard.get(),
|
|
251
364
|
requestTimeoutMs: config.requestTimeoutMs.get(),
|
|
@@ -347,49 +460,214 @@ function estimateTokens(value) {
|
|
|
347
460
|
}
|
|
348
461
|
|
|
349
462
|
// src/stream.ts
|
|
350
|
-
import { LlmError as
|
|
463
|
+
import { LlmError as LlmError4, ToolCallId } from "@deepseek-ai/dsh-llm";
|
|
351
464
|
|
|
352
|
-
// src/
|
|
353
|
-
|
|
354
|
-
|
|
465
|
+
// src/protocols/errors.ts
|
|
466
|
+
import {
|
|
467
|
+
ACCOUNT_QUOTA_EXCEEDED_CODE,
|
|
468
|
+
CONTEXT_WINDOW_EXCEEDED_CODE,
|
|
469
|
+
INVALID_CREDENTIAL_CODE,
|
|
470
|
+
LlmError as LlmError2,
|
|
471
|
+
QUOTA_EXCEEDED_CODE,
|
|
472
|
+
isContextWindowExceededError,
|
|
473
|
+
isQuotaExceededError
|
|
474
|
+
} from "@deepseek-ai/dsh-llm";
|
|
475
|
+
var MESSAGES = {
|
|
476
|
+
[CONTEXT_WINDOW_EXCEEDED_CODE]: "OpenDesign\uFF1A\u8BF7\u6C42\u8D85\u51FA\u6A21\u578B\u4E0A\u4E0B\u6587\u957F\u5EA6\uFF0C\u8BF7\u7F29\u77ED\u8F93\u5165\u540E\u91CD\u8BD5",
|
|
477
|
+
[ACCOUNT_QUOTA_EXCEEDED_CODE]: "OpenDesign\uFF1A\u5957\u9910\u6216\u8D26\u6237\u989D\u5EA6\u4E0D\u8DB3\uFF0C\u8BF7\u68C0\u67E5\u5957\u9910\u989D\u5EA6\u540E\u91CD\u8BD5",
|
|
478
|
+
[QUOTA_EXCEEDED_CODE]: "OpenDesign\uFF1A\u94B1\u5305\u6216\u79EF\u5206\u4F59\u989D\u4E0D\u8DB3\uFF0C\u8BF7\u5145\u503C\u540E\u91CD\u8BD5",
|
|
479
|
+
[INVALID_CREDENTIAL_CODE]: "OpenDesign\uFF1AAPI Key \u65E0\u6548\u6216\u7F3A\u5931\uFF0C\u8BF7\u5728\u8BBE\u7F6E\u4E2D\u66F4\u65B0\u51ED\u636E",
|
|
480
|
+
AUTH: "OpenDesign\uFF1A\u8BA4\u8BC1\u672A\u901A\u8FC7\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55\u540E\u91CD\u8BD5",
|
|
481
|
+
PERMISSION: "OpenDesign\uFF1A\u5F53\u524D\u8D26\u6237\u65E0\u6743\u8BBF\u95EE\u8BE5\u6A21\u578B",
|
|
482
|
+
RATE_LIMIT: "OpenDesign\uFF1A\u8BF7\u6C42\u8FC7\u4E8E\u9891\u7E41\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",
|
|
483
|
+
TIMEOUT: "OpenDesign\uFF1A\u8BF7\u6C42\u8D85\u65F6\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",
|
|
484
|
+
SERVER: "OpenDesign\uFF1A\u4E0A\u6E38\u670D\u52A1\u6682\u4E0D\u53EF\u7528\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",
|
|
485
|
+
INVALID_REQUEST: "OpenDesign\uFF1A\u8BF7\u6C42\u88AB\u4E0A\u6E38\u62D2\u7EDD",
|
|
486
|
+
INVALID_RESPONSE: "OpenDesign\uFF1A\u4E0A\u6E38\u54CD\u5E94\u683C\u5F0F\u65E0\u6548"
|
|
487
|
+
};
|
|
488
|
+
var UNKNOWN_MESSAGE = "OpenDesign\uFF1A\u8BF7\u6C42\u5931\u8D25";
|
|
489
|
+
var STRUCTURED_CREDENTIAL = /invalid[_-]?(?:x[_-]?)?api[_-]?key|missing[_-]?(?:x[_-]?)?api[_-]?key|incorrect[_-]?(?:x[_-]?)?api[_-]?key|api[_-]?key[_-]?(?:invalid|missing|incorrect)|invalid[_-]?credentials?|credentials?[_-]?(?:invalid|missing)/u;
|
|
490
|
+
var STRUCTURED_CONTEXT = /context[_-]?(?:length|window)[_-]?(?:exceeded|exceed|overflow)|context[_-]?overflow|(?:prompt|input)[_-]?too[_-]?long/u;
|
|
491
|
+
var STRUCTURED_WALLET = /(?:insufficient|low|exhausted|depleted|empty|zero)[_-]?(?:credits?|balance|funds)|(?:credits?|balance|wallet|funds)[_-]?(?:insufficient|exhausted|depleted|low|empty)|(?:credit|wallet)[_-]balance[_-](?:too[_-]low|insufficient|exhausted)/u;
|
|
492
|
+
var STRUCTURED_PLAN = /(?:insufficient|exceeded|exhausted|reached|over)[_-]?(?:quota|plan|usage[_-]?limit)|quota[_-]?(?:exceeded|exhausted|reached)|billing[_-]?hard[_-]?limit|usage[_-]?limit[_-]?(?:exceeded|reached)|(?:subscription|membership|plan)(?:[_-](?:quota|credits?|limit))?[_-](?:exceeded|exhausted|insufficient|reached)/u;
|
|
493
|
+
var STRUCTURED_RATE = /rate[_-]?limit|too[_-]?many[_-]?requests|resource[_-]?exhausted|(?:requests?|tokens?)[_-]?per[_-]?(?:minute|second)/u;
|
|
494
|
+
var STRUCTURED_TIMEOUT = /timeout|timed[_-]?out|deadline[_-]?exceeded/u;
|
|
495
|
+
var STRUCTURED_SERVER = /server[_-]?error|api[_-]?error|internal[_-]?(?:server[_-]?)?error|service[_-]?unavailable|unavailable|overloaded|bad[_-]?gateway|gateway[_-]?error/u;
|
|
496
|
+
var STRUCTURED_AUTH = /authentication|unauthorized|not[_-]?authenticated/u;
|
|
497
|
+
var STRUCTURED_PERMISSION = /permission|forbidden|access[_-]?denied|insufficient[_-]?scope/u;
|
|
498
|
+
var PROSE_CREDENTIAL = /invalid[ _-]?(?:x[ _-]?)?api[ _-]?key|(?:x[ _-]?)?api[ _-]?key[ _-]?(?:is[ _-]?)?(?:invalid|missing|incorrect|not[ _-]?valid)|missing[ _-]?(?:x[ _-]?)?api[ _-]?key|incorrect[ _-]?(?:x[ _-]?)?api[ _-]?key|invalid[ _-]?credentials?\b|api[ _-]?key\s*(?:无效|缺失|错误)|密钥(?:无效|缺失|错误)/u;
|
|
499
|
+
var PROSE_CONTEXT_EN = /\b(?:prompt|input|messages?)\s+(?:is\s+|are\s+)?too\s+(?:long|large)\b|\bmaximum context\b|\btokens? > \d+ maximum\b/u;
|
|
500
|
+
var PROSE_CONTEXT_ZH = /超出[^。]{0,8}上下文|上下文[^。]{0,16}(?:超出|超过|超限|过长|已满)|(?:输入|请求|提示词)[^。]{0,8}(?:过长|太长)|超过[^。]{0,8}上下文/u;
|
|
501
|
+
var PROSE_QUOTA_EN = /\b(?:credits?|balance|quota|funds|wallet)\b[^.;]{0,24}\b(?:insufficient|exhausted|depleted|used[ _-]?up|too[ _-]?low|empty|zero|exceeded)\b|\b(?:insufficient|exhausted|depleted|empty|zero)\b[^.;]{0,24}\b(?:credits?|balance|quota|funds|wallet)\b/u;
|
|
502
|
+
var PROSE_QUOTA_ZH = /(?:余额|积分|额度|配额|钱包)[^。]{0,12}(?:不足|用尽|耗尽|用完|超限|超出|已达上限|超过上限)|(?:不足|用尽|耗尽)[^。]{0,6}(?:余额|额度|配额|积分)/u;
|
|
503
|
+
var PROSE_QUOTA_PLAN = /套餐|订阅|会员|账户额度|账户配额|用量上限|使用上限|\bplan\b|\bsubscription\b|\bmembership\b|\bbilling\b|hard[ _-]?limit|usage[ _-]?limit/u;
|
|
504
|
+
var PROSE_QUOTA_WALLET = /钱包|充值|余额|积分|\bwallet\b|\brecharge\b|top[ _-]?up|\bbalance\b|\bcredits?\b|\bfunds?\b/u;
|
|
505
|
+
var PROSE_RATE = /rate[ _-]?limit|\btoo many requests\b|\bresource[ _-]?exhausted\b|请求过于频繁|频率超限|触发限流|接口限流/u;
|
|
506
|
+
var PROSE_TIMEOUT = /\btimeout\b|\btimed out\b|\bdeadline exceeded\b|请求超时|超时/u;
|
|
507
|
+
var PROSE_SERVER = /\bserver error\b|\bapi error\b|\binternal (?:server )?error\b|\bservice unavailable\b|\boverloaded\b|\bbad gateway\b|服务暂不可用|服务不可用|服务器错误|服务器繁忙|内部错误/u;
|
|
508
|
+
var PROSE_AUTH = /\bauthentication\b|\bunauthorized\b|\bnot authenticated\b|认证失败|登录已失效|未登录/u;
|
|
509
|
+
var PROSE_PERMISSION = /\bpermission\b|\bforbidden\b|\baccess denied\b|\bnot authorized\b|\binsufficient (?:permissions?|scope)\b|权限不足|无权限|无权访问/u;
|
|
510
|
+
function fail(code, status) {
|
|
511
|
+
const options = status === void 0 ? {} : { status };
|
|
512
|
+
return new LlmError2(MESSAGES[code] ?? UNKNOWN_MESSAGE, code, options);
|
|
355
513
|
}
|
|
356
|
-
function
|
|
357
|
-
return
|
|
514
|
+
function matches(fields, pattern) {
|
|
515
|
+
return fields.some((field) => pattern.test(field));
|
|
358
516
|
}
|
|
359
|
-
function
|
|
360
|
-
|
|
361
|
-
if (
|
|
362
|
-
return
|
|
517
|
+
function structuredQuota(fields) {
|
|
518
|
+
if (matches(fields, STRUCTURED_WALLET)) return QUOTA_EXCEEDED_CODE;
|
|
519
|
+
if (matches(fields, STRUCTURED_PLAN)) return ACCOUNT_QUOTA_EXCEEDED_CODE;
|
|
520
|
+
return void 0;
|
|
363
521
|
}
|
|
364
|
-
function
|
|
365
|
-
|
|
366
|
-
|
|
522
|
+
function proseQuota(detail) {
|
|
523
|
+
if (PROSE_QUOTA_PLAN.test(detail)) return ACCOUNT_QUOTA_EXCEEDED_CODE;
|
|
524
|
+
if (PROSE_QUOTA_WALLET.test(detail)) return QUOTA_EXCEEDED_CODE;
|
|
525
|
+
return ACCOUNT_QUOTA_EXCEEDED_CODE;
|
|
526
|
+
}
|
|
527
|
+
function classifyStructured(fields) {
|
|
528
|
+
if (matches(fields, STRUCTURED_CREDENTIAL)) return INVALID_CREDENTIAL_CODE;
|
|
529
|
+
if (matches(fields, STRUCTURED_CONTEXT)) return CONTEXT_WINDOW_EXCEEDED_CODE;
|
|
530
|
+
const quota = structuredQuota(fields);
|
|
531
|
+
if (quota !== void 0) return quota;
|
|
532
|
+
if (matches(fields, STRUCTURED_RATE)) return "RATE_LIMIT";
|
|
533
|
+
if (matches(fields, STRUCTURED_TIMEOUT)) return "TIMEOUT";
|
|
534
|
+
if (matches(fields, STRUCTURED_SERVER)) return "SERVER";
|
|
535
|
+
if (matches(fields, STRUCTURED_AUTH)) return "AUTH";
|
|
536
|
+
if (matches(fields, STRUCTURED_PERMISSION)) return "PERMISSION";
|
|
537
|
+
return void 0;
|
|
367
538
|
}
|
|
368
|
-
function
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
539
|
+
function classifyProse(detail) {
|
|
540
|
+
if (detail === "") return void 0;
|
|
541
|
+
if (isContextWindowExceededError(detail) || PROSE_CONTEXT_EN.test(detail) || PROSE_CONTEXT_ZH.test(detail)) return CONTEXT_WINDOW_EXCEEDED_CODE;
|
|
542
|
+
if (isQuotaExceededError(detail) || PROSE_QUOTA_EN.test(detail) || PROSE_QUOTA_ZH.test(detail)) return proseQuota(detail);
|
|
543
|
+
if (PROSE_RATE.test(detail)) return "RATE_LIMIT";
|
|
544
|
+
if (PROSE_TIMEOUT.test(detail)) return "TIMEOUT";
|
|
545
|
+
if (PROSE_SERVER.test(detail)) return "SERVER";
|
|
546
|
+
if (PROSE_AUTH.test(detail)) return "AUTH";
|
|
547
|
+
if (PROSE_PERMISSION.test(detail)) return "PERMISSION";
|
|
548
|
+
return void 0;
|
|
549
|
+
}
|
|
550
|
+
function classifyStatus(status) {
|
|
551
|
+
if (status === void 0) return void 0;
|
|
552
|
+
if (status === 401) return "AUTH";
|
|
553
|
+
if (status === 403) return "PERMISSION";
|
|
554
|
+
if (status === 402) return QUOTA_EXCEEDED_CODE;
|
|
555
|
+
if (status === 429) return "RATE_LIMIT";
|
|
556
|
+
if (status === 408 || status === 504) return "TIMEOUT";
|
|
557
|
+
if (status >= 500) return "SERVER";
|
|
558
|
+
return void 0;
|
|
559
|
+
}
|
|
560
|
+
function fieldsOf(error) {
|
|
561
|
+
const structured = [];
|
|
562
|
+
for (const value of [error.code, error.type]) {
|
|
563
|
+
if (typeof value !== "string") continue;
|
|
564
|
+
const normalized = value.trim().toLowerCase();
|
|
565
|
+
if (normalized !== "") structured.push(normalized);
|
|
566
|
+
}
|
|
567
|
+
return { structured, prose: typeof error.message === "string" ? error.message.toLowerCase() : "" };
|
|
568
|
+
}
|
|
569
|
+
function classify(fields, status, fallback) {
|
|
570
|
+
const structured = classifyStructured(fields.structured);
|
|
571
|
+
if (structured !== void 0 && structured !== "AUTH" && structured !== "PERMISSION") return fail(structured, status);
|
|
572
|
+
if (structured === void 0 && status !== void 0 && status >= 500 && status !== 504) return fail("SERVER", status);
|
|
573
|
+
if (PROSE_CREDENTIAL.test(fields.prose)) return fail(INVALID_CREDENTIAL_CODE, status);
|
|
574
|
+
if (structured !== void 0) return fail(structured, status);
|
|
575
|
+
const prose = classifyProse(fields.prose);
|
|
576
|
+
if (prose !== void 0) return fail(prose, status);
|
|
577
|
+
const byStatus = classifyStatus(status);
|
|
578
|
+
return fail(byStatus ?? fallback, status);
|
|
379
579
|
}
|
|
380
|
-
|
|
381
|
-
// src/stream.ts
|
|
382
580
|
function gatewayError(status, body) {
|
|
383
581
|
const error = record(body) && record(body.error) ? body.error : record(body) ? body : {};
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
return
|
|
582
|
+
return classify(fieldsOf(error), status, "INVALID_REQUEST");
|
|
583
|
+
}
|
|
584
|
+
function anthropicError(status, body) {
|
|
585
|
+
const error = record(body) && record(body.error) ? body.error : {};
|
|
586
|
+
return classify(fieldsOf(error), status, "INVALID_REQUEST");
|
|
587
|
+
}
|
|
588
|
+
function inBandError(error, fallback = "INVALID_RESPONSE") {
|
|
589
|
+
return classify(fieldsOf(record(error) ? error : {}), void 0, fallback);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// src/protocols/sse.ts
|
|
593
|
+
import { LlmError as LlmError3 } from "@deepseek-ai/dsh-llm";
|
|
594
|
+
var MAX_FRAME_CHARS = 4 * 1024 * 1024;
|
|
595
|
+
async function* sseFrames(body, signal, idleMs) {
|
|
596
|
+
const reader = body.getReader();
|
|
597
|
+
const decoder = new TextDecoder();
|
|
598
|
+
let buffer = "";
|
|
599
|
+
let data = [];
|
|
600
|
+
let dataSize = 0;
|
|
601
|
+
let name2;
|
|
602
|
+
function dispatch() {
|
|
603
|
+
const payload = data.join("\n");
|
|
604
|
+
const event = name2;
|
|
605
|
+
data = [];
|
|
606
|
+
dataSize = 0;
|
|
607
|
+
name2 = void 0;
|
|
608
|
+
return payload ? [{ ...event === void 0 ? {} : { event }, data: payload }] : [];
|
|
609
|
+
}
|
|
610
|
+
function line(value) {
|
|
611
|
+
if (!value) return dispatch();
|
|
612
|
+
if (value.startsWith(":")) return [];
|
|
613
|
+
if (value.startsWith("data:")) {
|
|
614
|
+
const part = value.slice(5).replace(/^ /u, "");
|
|
615
|
+
dataSize += part.length;
|
|
616
|
+
if (dataSize > MAX_FRAME_CHARS) throw new LlmError3("OpenDesign\uFF1A\u6D41\u5F0F\u4E8B\u4EF6\u8D85\u8FC7\u5927\u5C0F\u9650\u5236", "INVALID_RESPONSE");
|
|
617
|
+
data.push(part);
|
|
618
|
+
return [];
|
|
619
|
+
}
|
|
620
|
+
if (value.startsWith("event:")) {
|
|
621
|
+
const event = value.slice(6).trim();
|
|
622
|
+
name2 = event === "" ? void 0 : event;
|
|
623
|
+
}
|
|
624
|
+
return [];
|
|
625
|
+
}
|
|
626
|
+
async function read() {
|
|
627
|
+
signal.throwIfAborted();
|
|
628
|
+
let timer;
|
|
629
|
+
let abort = () => {
|
|
630
|
+
};
|
|
631
|
+
try {
|
|
632
|
+
return await Promise.race([
|
|
633
|
+
reader.read(),
|
|
634
|
+
new Promise((_, reject) => {
|
|
635
|
+
timer = setTimeout(() => reject(new LlmError3("OpenDesign\uFF1A\u6D41\u5F0F\u54CD\u5E94\u957F\u65F6\u95F4\u6CA1\u6709\u6570\u636E", "TIMEOUT")), idleMs);
|
|
636
|
+
abort = () => reject(signal.reason);
|
|
637
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
638
|
+
if (signal.aborted) abort();
|
|
639
|
+
})
|
|
640
|
+
]);
|
|
641
|
+
} finally {
|
|
642
|
+
clearTimeout(timer);
|
|
643
|
+
signal.removeEventListener("abort", abort);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
try {
|
|
647
|
+
while (true) {
|
|
648
|
+
const result = await read();
|
|
649
|
+
buffer += result.done ? decoder.decode() : decoder.decode(result.value, { stream: true });
|
|
650
|
+
let match;
|
|
651
|
+
while (match = /\r\n|\n|\r(?!$)/u.exec(buffer)) {
|
|
652
|
+
const at = match.index;
|
|
653
|
+
const value = buffer.slice(0, at);
|
|
654
|
+
buffer = buffer.slice(at + match[0].length);
|
|
655
|
+
for (const frame of line(value)) yield frame;
|
|
656
|
+
}
|
|
657
|
+
if (buffer.length > MAX_FRAME_CHARS) throw new LlmError3("OpenDesign\uFF1A\u6D41\u5F0F\u884C\u8D85\u8FC7\u5927\u5C0F\u9650\u5236", "INVALID_RESPONSE");
|
|
658
|
+
if (result.done) {
|
|
659
|
+
if (buffer) for (const frame of line(buffer.replace(/\r$/u, ""))) yield frame;
|
|
660
|
+
for (const frame of dispatch()) yield frame;
|
|
661
|
+
break;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
} finally {
|
|
665
|
+
await reader.cancel().catch(() => void 0);
|
|
666
|
+
reader.releaseLock();
|
|
667
|
+
}
|
|
392
668
|
}
|
|
669
|
+
|
|
670
|
+
// src/stream.ts
|
|
393
671
|
function tokenUsage(value) {
|
|
394
672
|
const prompt = finite(value.prompt_tokens), output = finite(value.completion_tokens);
|
|
395
673
|
if (prompt === void 0 || output === void 0 || prompt < 0 || output < 0) return;
|
|
@@ -409,9 +687,8 @@ function tokenUsage(value) {
|
|
|
409
687
|
};
|
|
410
688
|
}
|
|
411
689
|
async function* consumeSse(body, signal, idleMs) {
|
|
412
|
-
|
|
413
|
-
let
|
|
414
|
-
let nextIndex = 0, count2 = 0, sawTool = false, text2, reasoning;
|
|
690
|
+
let nextIndex = 0, count2 = 0, sawTool = false, done = false;
|
|
691
|
+
let text2, reasoning;
|
|
415
692
|
const tools = /* @__PURE__ */ new Map();
|
|
416
693
|
let finish, usage;
|
|
417
694
|
function close() {
|
|
@@ -419,17 +696,14 @@ async function* consumeSse(body, signal, idleMs) {
|
|
|
419
696
|
if (text2) out.push({ type: "block-end", index: text2.index, block: { type: "text", text: text2.text } });
|
|
420
697
|
if (reasoning) out.push({ type: "block-end", index: reasoning.index, block: { type: "reasoning", text: reasoning.text } });
|
|
421
698
|
for (const call of tools.values()) {
|
|
422
|
-
if (!call.id || !call.name) throw new
|
|
699
|
+
if (!call.id || !call.name) throw new LlmError4("OpenDesign\uFF1A\u5DE5\u5177\u8C03\u7528\u7F3A\u5C11\u6807\u8BC6\u6216\u540D\u79F0", "INVALID_RESPONSE");
|
|
423
700
|
out.push({ type: "block-end", index: call.index, block: { type: "tool-call", id: ToolCallId(call.id), name: call.name, arguments: call.arguments } });
|
|
424
701
|
}
|
|
425
702
|
text2 = reasoning = void 0;
|
|
426
703
|
tools.clear();
|
|
427
704
|
return out.sort((a, b) => ("index" in a ? a.index : 0) - ("index" in b ? b.index : 0));
|
|
428
705
|
}
|
|
429
|
-
function event() {
|
|
430
|
-
const payload = data.join("\n");
|
|
431
|
-
data = [];
|
|
432
|
-
dataSize = 0;
|
|
706
|
+
function event(payload) {
|
|
433
707
|
if (!payload) return [];
|
|
434
708
|
if (payload.trim() === "[DONE]") {
|
|
435
709
|
done = true;
|
|
@@ -439,18 +713,18 @@ async function* consumeSse(body, signal, idleMs) {
|
|
|
439
713
|
try {
|
|
440
714
|
value = JSON.parse(payload);
|
|
441
715
|
} catch {
|
|
442
|
-
throw new
|
|
716
|
+
throw new LlmError4("OpenDesign\uFF1A\u65E0\u6548\u7684\u6D41\u5F0F JSON", "INVALID_RESPONSE");
|
|
443
717
|
}
|
|
444
|
-
if (!record(value)) throw new
|
|
718
|
+
if (!record(value)) throw new LlmError4("OpenDesign\uFF1A\u6D41\u5F0F\u4E8B\u4EF6\u4E0D\u662F\u5BF9\u8C61", "INVALID_RESPONSE");
|
|
445
719
|
if (record(value.error)) throw gatewayError(void 0, value);
|
|
446
720
|
if (record(value.usage)) usage = tokenUsage(value.usage) ?? usage;
|
|
447
721
|
if (!Array.isArray(value.choices) || !value.choices.length) return [];
|
|
448
722
|
const choice = value.choices[0];
|
|
449
|
-
if (!record(choice)) throw new
|
|
723
|
+
if (!record(choice)) throw new LlmError4("OpenDesign\uFF1A\u65E0\u6548\u7684\u6D41\u5F0F\u9009\u9879", "INVALID_RESPONSE");
|
|
450
724
|
const delta = record(choice.delta) ? choice.delta : {};
|
|
451
725
|
const out = [];
|
|
452
726
|
const hasDelta = typeof delta.content === "string" && delta.content !== "" || typeof delta.reasoning_content === "string" && delta.reasoning_content !== "" || Array.isArray(delta.tool_calls) && delta.tool_calls.length > 0;
|
|
453
|
-
if (finish && hasDelta) throw new
|
|
727
|
+
if (finish && hasDelta) throw new LlmError4("OpenDesign\uFF1A\u54CD\u5E94\u7ED3\u675F\u540E\u4ECD\u6536\u5230\u5185\u5BB9", "INVALID_RESPONSE");
|
|
454
728
|
const thinking = typeof delta.reasoning_content === "string" ? delta.reasoning_content : typeof delta.reasoning === "string" ? delta.reasoning : "";
|
|
455
729
|
if (thinking) {
|
|
456
730
|
if (!reasoning) {
|
|
@@ -471,7 +745,7 @@ async function* consumeSse(body, signal, idleMs) {
|
|
|
471
745
|
out.push({ type: "text-delta", index: text2.index, text: delta.content });
|
|
472
746
|
}
|
|
473
747
|
if (Array.isArray(delta.tool_calls)) for (const item of delta.tool_calls) {
|
|
474
|
-
if (!record(item) || typeof item.index !== "number" || !Number.isInteger(item.index) || item.index < 0) throw new
|
|
748
|
+
if (!record(item) || typeof item.index !== "number" || !Number.isInteger(item.index) || item.index < 0) throw new LlmError4("OpenDesign\uFF1A\u65E0\u6548\u7684\u5DE5\u5177\u8C03\u7528\u7D22\u5F15", "INVALID_RESPONSE");
|
|
475
749
|
let call = tools.get(item.index);
|
|
476
750
|
if (!call) {
|
|
477
751
|
call = { index: nextIndex++, id: "", name: "", arguments: "", started: false };
|
|
@@ -481,7 +755,7 @@ async function* consumeSse(body, signal, idleMs) {
|
|
|
481
755
|
}
|
|
482
756
|
const fn = record(item.function) ? item.function : {};
|
|
483
757
|
if (typeof item.id === "string" && item.id) {
|
|
484
|
-
if (call.id && call.id !== item.id) throw new
|
|
758
|
+
if (call.id && call.id !== item.id) throw new LlmError4("OpenDesign\uFF1A\u5DE5\u5177\u8C03\u7528\u6807\u8BC6\u53D1\u751F\u53D8\u5316", "INVALID_RESPONSE");
|
|
485
759
|
call.id = item.id;
|
|
486
760
|
}
|
|
487
761
|
if (typeof fn.name === "string" && fn.name) call.name = fn.name;
|
|
@@ -496,7 +770,7 @@ async function* consumeSse(body, signal, idleMs) {
|
|
|
496
770
|
}
|
|
497
771
|
}
|
|
498
772
|
if (typeof choice.finish_reason === "string" && choice.finish_reason) {
|
|
499
|
-
if (finish) throw new
|
|
773
|
+
if (finish) throw new LlmError4("OpenDesign\uFF1A\u91CD\u590D\u7684\u54CD\u5E94\u7EC8\u6B62\u4E8B\u4EF6", "INVALID_RESPONSE");
|
|
500
774
|
switch (choice.finish_reason) {
|
|
501
775
|
case "length":
|
|
502
776
|
finish = { kind: "max-tokens" };
|
|
@@ -509,70 +783,22 @@ async function* consumeSse(body, signal, idleMs) {
|
|
|
509
783
|
finish = { kind: "stop" };
|
|
510
784
|
break;
|
|
511
785
|
default:
|
|
512
|
-
throw new
|
|
786
|
+
throw new LlmError4("OpenDesign\uFF1A\u6A21\u578B\u54CD\u5E94\u88AB\u8FC7\u6EE4\u6216\u5F02\u5E38\u7EC8\u6B62", "INVALID_RESPONSE");
|
|
513
787
|
}
|
|
514
788
|
out.push(...close());
|
|
515
789
|
}
|
|
516
790
|
return out;
|
|
517
791
|
}
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
if (
|
|
521
|
-
const part = value.slice(5).replace(/^ /u, "");
|
|
522
|
-
dataSize += part.length;
|
|
523
|
-
if (dataSize > 4 * 1024 * 1024) throw new LlmError2("OpenDesign\uFF1A\u6D41\u5F0F\u4E8B\u4EF6\u8D85\u8FC7\u5927\u5C0F\u9650\u5236", "INVALID_RESPONSE");
|
|
524
|
-
data.push(part);
|
|
525
|
-
}
|
|
526
|
-
return [];
|
|
527
|
-
}
|
|
528
|
-
async function read() {
|
|
529
|
-
signal.throwIfAborted();
|
|
530
|
-
let timer;
|
|
531
|
-
let abort = () => {
|
|
532
|
-
};
|
|
533
|
-
try {
|
|
534
|
-
return await Promise.race([
|
|
535
|
-
reader.read(),
|
|
536
|
-
new Promise((_, reject) => {
|
|
537
|
-
timer = setTimeout(() => reject(new LlmError2("OpenDesign\uFF1A\u6D41\u5F0F\u54CD\u5E94\u957F\u65F6\u95F4\u6CA1\u6709\u6570\u636E", "TIMEOUT")), idleMs);
|
|
538
|
-
abort = () => reject(signal.reason);
|
|
539
|
-
signal.addEventListener("abort", abort, { once: true });
|
|
540
|
-
if (signal.aborted) abort();
|
|
541
|
-
})
|
|
542
|
-
]);
|
|
543
|
-
} finally {
|
|
544
|
-
clearTimeout(timer);
|
|
545
|
-
signal.removeEventListener("abort", abort);
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
try {
|
|
549
|
-
while (!done) {
|
|
550
|
-
const result = await read();
|
|
551
|
-
buffer += result.done ? decoder.decode() : decoder.decode(result.value, { stream: true });
|
|
552
|
-
let match;
|
|
553
|
-
while (match = /\r\n|\n|\r(?!$)/u.exec(buffer)) {
|
|
554
|
-
const at = match.index, value = buffer.slice(0, at);
|
|
555
|
-
buffer = buffer.slice(at + match[0].length);
|
|
556
|
-
for (const chunk of line(value)) yield chunk;
|
|
557
|
-
if (done) break;
|
|
558
|
-
}
|
|
559
|
-
if (buffer.length > 4 * 1024 * 1024) throw new LlmError2("OpenDesign\uFF1A\u6D41\u5F0F\u884C\u8D85\u8FC7\u5927\u5C0F\u9650\u5236", "INVALID_RESPONSE");
|
|
560
|
-
if (result.done) {
|
|
561
|
-
if (buffer && !done) for (const chunk of line(buffer.replace(/\r$/u, ""))) yield chunk;
|
|
562
|
-
if (!done) for (const chunk of event()) yield chunk;
|
|
563
|
-
break;
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
signal.throwIfAborted();
|
|
567
|
-
if (!finish) throw new LlmError2("OpenDesign\uFF1A\u54CD\u5E94\u6D41\u63D0\u524D\u4E2D\u65AD\uFF0C\u672A\u6536\u5230\u7ED3\u675F\u539F\u56E0", "INVALID_RESPONSE");
|
|
568
|
-
if (!count2) throw new LlmError2("OpenDesign\uFF1A\u6A21\u578B\u8FD4\u56DE\u7A7A\u54CD\u5E94", "EMPTY_RESPONSE");
|
|
569
|
-
if (finish.kind === "tool-calls" && !sawTool) throw new LlmError2("OpenDesign\uFF1A\u54CD\u5E94\u58F0\u660E\u5DE5\u5177\u8C03\u7528\uFF0C\u4F46\u672A\u8FD4\u56DE\u4EFB\u4F55\u5DE5\u5177\u5757", "INVALID_RESPONSE");
|
|
570
|
-
if (usage) yield { type: "usage", usage };
|
|
571
|
-
yield { type: "finish", reason: finish };
|
|
572
|
-
} finally {
|
|
573
|
-
await reader.cancel().catch(() => void 0);
|
|
574
|
-
reader.releaseLock();
|
|
792
|
+
for await (const frame of sseFrames(body, signal, idleMs)) {
|
|
793
|
+
for (const chunk of event(frame.data)) yield chunk;
|
|
794
|
+
if (done) break;
|
|
575
795
|
}
|
|
796
|
+
signal.throwIfAborted();
|
|
797
|
+
if (!finish) throw new LlmError4("OpenDesign\uFF1A\u54CD\u5E94\u6D41\u63D0\u524D\u4E2D\u65AD\uFF0C\u672A\u6536\u5230\u7ED3\u675F\u539F\u56E0", "INVALID_RESPONSE");
|
|
798
|
+
if (!count2) throw new LlmError4("OpenDesign\uFF1A\u6A21\u578B\u8FD4\u56DE\u7A7A\u54CD\u5E94", "EMPTY_RESPONSE");
|
|
799
|
+
if (finish.kind === "tool-calls" && !sawTool) throw new LlmError4("OpenDesign\uFF1A\u54CD\u5E94\u58F0\u660E\u5DE5\u5177\u8C03\u7528\uFF0C\u4F46\u672A\u8FD4\u56DE\u4EFB\u4F55\u5DE5\u5177\u5757", "INVALID_RESPONSE");
|
|
800
|
+
if (usage) yield { type: "usage", usage };
|
|
801
|
+
yield { type: "finish", reason: finish };
|
|
576
802
|
}
|
|
577
803
|
|
|
578
804
|
// src/empty-turn.ts
|
|
@@ -698,22 +924,775 @@ var EmptyTurnTracker = class {
|
|
|
698
924
|
}
|
|
699
925
|
};
|
|
700
926
|
|
|
927
|
+
// src/protocols/index.ts
|
|
928
|
+
import { LlmError as LlmError9 } from "@deepseek-ai/dsh-llm";
|
|
929
|
+
|
|
930
|
+
// src/protocols/chat-completions.ts
|
|
931
|
+
import { LlmError as LlmError5 } from "@deepseek-ai/dsh-llm";
|
|
932
|
+
|
|
933
|
+
// src/protocols/headers.ts
|
|
934
|
+
import { attributionHeaders } from "@deepseek-ai/dsh-llm";
|
|
935
|
+
function streamHeaders(apiKey, extra = {}) {
|
|
936
|
+
return {
|
|
937
|
+
...attributionHeaders(),
|
|
938
|
+
authorization: `Bearer ${apiKey}`,
|
|
939
|
+
"content-type": "application/json",
|
|
940
|
+
accept: "text/event-stream",
|
|
941
|
+
...extra
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
// src/protocols/chat-completions.ts
|
|
946
|
+
function buildChatCompletionsRequest(ctx) {
|
|
947
|
+
const { options, model } = ctx;
|
|
948
|
+
const replayReasoning = model.id.startsWith("deepseek-") || model.id.startsWith("kimi-");
|
|
949
|
+
const messages2 = chatMessages(options, (ref) => ctx.images.get(ref.attachmentId), replayReasoning);
|
|
950
|
+
const tools = chatTools(options);
|
|
951
|
+
const body = { model: model.id, messages: messages2, stream: true, stream_options: { include_usage: true } };
|
|
952
|
+
if (tools) body.tools = tools;
|
|
953
|
+
if (options.temperature !== void 0) body.temperature = options.temperature;
|
|
954
|
+
if (ctx.topP !== void 0) body.top_p = ctx.topP;
|
|
955
|
+
if (options.maxTokens !== void 0) {
|
|
956
|
+
if (!Number.isSafeInteger(options.maxTokens) || options.maxTokens <= 0) throw new LlmError5("maxTokens \u5FC5\u987B\u662F\u6B63\u6574\u6570", "INVALID_REQUEST");
|
|
957
|
+
body.max_tokens = options.maxTokens;
|
|
958
|
+
}
|
|
959
|
+
if (options.reasoningEffort !== void 0) body.reasoning_effort = options.reasoningEffort;
|
|
960
|
+
if (options.stop?.length) body.stop = options.stop;
|
|
961
|
+
return { url: inferenceEndpoint(ctx.baseURL, "chat-completions"), headers: streamHeaders(ctx.apiKey), body };
|
|
962
|
+
}
|
|
963
|
+
var chatCompletions = {
|
|
964
|
+
id: "chat-completions",
|
|
965
|
+
build: buildChatCompletionsRequest,
|
|
966
|
+
stream(body, signal, idleMs) {
|
|
967
|
+
return consumeSse(body, signal, idleMs);
|
|
968
|
+
}
|
|
969
|
+
};
|
|
970
|
+
|
|
971
|
+
// src/protocols/messages.ts
|
|
972
|
+
import {
|
|
973
|
+
LlmError as LlmError7,
|
|
974
|
+
ToolCallId as ToolCallId2
|
|
975
|
+
} from "@deepseek-ai/dsh-llm";
|
|
976
|
+
|
|
977
|
+
// src/protocols/parts.ts
|
|
978
|
+
import { LlmError as LlmError6, textOnlyImageText as textOnlyImageText2 } from "@deepseek-ai/dsh-llm";
|
|
979
|
+
function projectContent(blocks, image) {
|
|
980
|
+
let text2 = "";
|
|
981
|
+
const images = [];
|
|
982
|
+
for (const block of blocks) {
|
|
983
|
+
switch (block.type) {
|
|
984
|
+
case "text":
|
|
985
|
+
text2 += block.text;
|
|
986
|
+
break;
|
|
987
|
+
case "image": {
|
|
988
|
+
const url = block.offloaded ? void 0 : image?.(block.attachment);
|
|
989
|
+
if (url) images.push(url);
|
|
990
|
+
else text2 += `${text2 ? "\n" : ""}${textOnlyImageText2(block.attachment)}`;
|
|
991
|
+
break;
|
|
992
|
+
}
|
|
993
|
+
case "file":
|
|
994
|
+
throw new LlmError6("\u539F\u59CB\u6587\u4EF6\u672A\u88AB\u5BBF\u4E3B\u6295\u5F71\u4E3A\u6587\u672C\u53E5\u67C4", "UNSUPPORTED_OPTION");
|
|
995
|
+
// 工具定义已由 Core 物化进 options.tools;工具更新块只对声明 in-history 的路由有意义。
|
|
996
|
+
default:
|
|
997
|
+
break;
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
return { text: text2, images };
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
// src/protocols/usage.ts
|
|
1004
|
+
function valid(value) {
|
|
1005
|
+
return value !== void 0 && Number.isFinite(value) && value >= 0;
|
|
1006
|
+
}
|
|
1007
|
+
function openAiUsage(raw) {
|
|
1008
|
+
const input = raw.input, output = raw.output;
|
|
1009
|
+
if (!valid(input) || !valid(output)) return void 0;
|
|
1010
|
+
const read = valid(raw.cacheRead) ? raw.cacheRead : 0;
|
|
1011
|
+
const write = valid(raw.cacheWrite) ? raw.cacheWrite : 0;
|
|
1012
|
+
const cached = read + write <= input;
|
|
1013
|
+
const reasoning = valid(raw.reasoning) && raw.reasoning <= output ? raw.reasoning : void 0;
|
|
1014
|
+
return {
|
|
1015
|
+
inputTokens: cached ? input - read - write : input,
|
|
1016
|
+
outputTokens: output,
|
|
1017
|
+
totalTokens: input + output,
|
|
1018
|
+
...cached && read > 0 ? { cacheReadTokens: read } : {},
|
|
1019
|
+
...cached && write > 0 ? { cacheWriteTokens: write } : {},
|
|
1020
|
+
...reasoning !== void 0 ? { reasoningTokens: reasoning } : {}
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
function anthropicUsage(raw) {
|
|
1024
|
+
const input = raw.input, output = raw.output;
|
|
1025
|
+
if (!valid(input) || !valid(output)) return void 0;
|
|
1026
|
+
const read = valid(raw.cacheRead) ? raw.cacheRead : void 0;
|
|
1027
|
+
const write = valid(raw.cacheWrite) ? raw.cacheWrite : void 0;
|
|
1028
|
+
const reasoning = valid(raw.reasoning) && raw.reasoning <= output ? raw.reasoning : void 0;
|
|
1029
|
+
const total = valid(raw.total) ? raw.total : input + (read ?? 0) + (write ?? 0) + output;
|
|
1030
|
+
return {
|
|
1031
|
+
inputTokens: input,
|
|
1032
|
+
outputTokens: output,
|
|
1033
|
+
totalTokens: total,
|
|
1034
|
+
...read !== void 0 && read > 0 ? { cacheReadTokens: read } : {},
|
|
1035
|
+
...write !== void 0 && write > 0 ? { cacheWriteTokens: write } : {},
|
|
1036
|
+
...reasoning !== void 0 ? { reasoningTokens: reasoning } : {}
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
// src/protocols/messages.ts
|
|
1041
|
+
var MESSAGES_DEFAULT_MAX_TOKENS = 4096;
|
|
1042
|
+
var ANTHROPIC_VERSION = "2023-06-01";
|
|
1043
|
+
var THINKING_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
|
|
1044
|
+
var DATA_URL = /^data:([^;,]+);base64,(.*)$/su;
|
|
1045
|
+
function messagesURL(baseURL) {
|
|
1046
|
+
return inferenceEndpoint(baseURL, "messages");
|
|
1047
|
+
}
|
|
1048
|
+
function imagePart(url) {
|
|
1049
|
+
const match = DATA_URL.exec(url);
|
|
1050
|
+
return match?.[1] && match[2] ? { type: "image", source: { type: "base64", media_type: match[1], data: match[2] } } : { type: "image", source: { type: "url", url } };
|
|
1051
|
+
}
|
|
1052
|
+
function toolInput(argumentsText) {
|
|
1053
|
+
if (argumentsText.trim() === "") return {};
|
|
1054
|
+
let parsed;
|
|
1055
|
+
try {
|
|
1056
|
+
parsed = JSON.parse(argumentsText);
|
|
1057
|
+
} catch {
|
|
1058
|
+
throw new LlmError7("\u5386\u53F2\u5DE5\u5177\u8C03\u7528\u53C2\u6570\u4E0D\u662F\u5408\u6CD5 JSON\uFF0C\u65E0\u6CD5\u6295\u5F71\u4E3A Anthropic tool_use.input", "INVALID_REQUEST");
|
|
1059
|
+
}
|
|
1060
|
+
if (!record(parsed)) throw new LlmError7("\u5386\u53F2\u5DE5\u5177\u8C03\u7528\u53C2\u6570\u4E0D\u662F JSON \u5BF9\u8C61\uFF0C\u65E0\u6CD5\u6295\u5F71\u4E3A Anthropic tool_use.input", "INVALID_REQUEST");
|
|
1061
|
+
return parsed;
|
|
1062
|
+
}
|
|
1063
|
+
function buildMessagesRequest(ctx) {
|
|
1064
|
+
const { options, model } = ctx;
|
|
1065
|
+
const maxTokens = options.maxTokens ?? MESSAGES_DEFAULT_MAX_TOKENS;
|
|
1066
|
+
if (!Number.isSafeInteger(maxTokens) || maxTokens <= 0) throw new LlmError7("maxTokens \u5FC5\u987B\u662F\u6B63\u6574\u6570", "INVALID_REQUEST");
|
|
1067
|
+
const system = [];
|
|
1068
|
+
const messages2 = [];
|
|
1069
|
+
const lookup = ctx.images.size ? (ref) => ctx.images.get(ref.attachmentId) : void 0;
|
|
1070
|
+
let results = [];
|
|
1071
|
+
let lifted = [];
|
|
1072
|
+
const flushResults = () => {
|
|
1073
|
+
if (results.length) {
|
|
1074
|
+
messages2.push({ role: "user", content: results });
|
|
1075
|
+
results = [];
|
|
1076
|
+
}
|
|
1077
|
+
};
|
|
1078
|
+
const flushLifted = () => {
|
|
1079
|
+
flushResults();
|
|
1080
|
+
if (lifted.length) {
|
|
1081
|
+
messages2.push({
|
|
1082
|
+
role: "user",
|
|
1083
|
+
content: [{ type: "text", text: "(Image content from the preceding tool result.)" }, ...lifted.map(imagePart)]
|
|
1084
|
+
});
|
|
1085
|
+
lifted = [];
|
|
1086
|
+
}
|
|
1087
|
+
};
|
|
1088
|
+
for (const message of options.messages) {
|
|
1089
|
+
if (message.role !== "tool") flushLifted();
|
|
1090
|
+
const projected = projectContent(message.content, message.role === "user" || message.role === "tool" ? lookup : void 0);
|
|
1091
|
+
switch (message.role) {
|
|
1092
|
+
case "system":
|
|
1093
|
+
case "developer":
|
|
1094
|
+
if (projected.text) system.push(projected.text);
|
|
1095
|
+
break;
|
|
1096
|
+
case "user": {
|
|
1097
|
+
const content2 = [
|
|
1098
|
+
...projected.text ? [{ type: "text", text: projected.text }] : [],
|
|
1099
|
+
...projected.images.map(imagePart)
|
|
1100
|
+
];
|
|
1101
|
+
if (content2.length) messages2.push({ role: "user", content: content2 });
|
|
1102
|
+
break;
|
|
1103
|
+
}
|
|
1104
|
+
case "assistant": {
|
|
1105
|
+
const content2 = [];
|
|
1106
|
+
if (projected.text) content2.push({ type: "text", text: projected.text });
|
|
1107
|
+
for (const block of message.content) {
|
|
1108
|
+
if (block.type === "tool-call") content2.push({ type: "tool_use", id: block.id, name: block.name, input: toolInput(block.arguments) });
|
|
1109
|
+
}
|
|
1110
|
+
if (content2.length) messages2.push({ role: "assistant", content: content2 });
|
|
1111
|
+
break;
|
|
1112
|
+
}
|
|
1113
|
+
case "tool": {
|
|
1114
|
+
results.push({
|
|
1115
|
+
type: "tool_result",
|
|
1116
|
+
tool_use_id: message.toolCallId,
|
|
1117
|
+
content: projected.text || (projected.images.length ? "(Image content follows in the next user message.)" : "")
|
|
1118
|
+
});
|
|
1119
|
+
lifted.push(...projected.images);
|
|
1120
|
+
break;
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
flushLifted();
|
|
1125
|
+
if (options.system?.trim()) system.unshift(options.system);
|
|
1126
|
+
const body = { model: model.id, max_tokens: maxTokens, messages: messages2, stream: true };
|
|
1127
|
+
if (system.length) body.system = system.join("\n\n");
|
|
1128
|
+
if (options.tools?.length) body.tools = options.tools.map((tool) => ({ name: tool.name, description: tool.description, input_schema: tool.parameters }));
|
|
1129
|
+
if (options.temperature !== void 0) body.temperature = options.temperature;
|
|
1130
|
+
if (ctx.topP !== void 0) body.top_p = ctx.topP;
|
|
1131
|
+
if (options.stop?.length) body.stop_sequences = options.stop;
|
|
1132
|
+
if (options.reasoningEffort !== void 0) {
|
|
1133
|
+
const effort2 = String(options.reasoningEffort);
|
|
1134
|
+
if (effort2 === "none") body.thinking = { type: "disabled" };
|
|
1135
|
+
else if (THINKING_EFFORTS.includes(effort2)) {
|
|
1136
|
+
body.thinking = { type: "adaptive" };
|
|
1137
|
+
body.output_config = { effort: effort2 };
|
|
1138
|
+
} else throw new LlmError7(`Anthropic Messages \u534F\u8BAE\u4E0D\u63A5\u53D7\u601D\u8003\u6DF1\u5EA6\u300C${effort2}\u300D`, "UNSUPPORTED_OPTION");
|
|
1139
|
+
}
|
|
1140
|
+
return { url: messagesURL(ctx.baseURL), headers: streamHeaders(ctx.apiKey, { "anthropic-version": ANTHROPIC_VERSION }), body };
|
|
1141
|
+
}
|
|
1142
|
+
function num(value) {
|
|
1143
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
1144
|
+
}
|
|
1145
|
+
async function* streamMessages(body, signal, idleMs) {
|
|
1146
|
+
let nextIndex = 0;
|
|
1147
|
+
let count2 = 0;
|
|
1148
|
+
let finish;
|
|
1149
|
+
let stopReason;
|
|
1150
|
+
let sawTool = false;
|
|
1151
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
1152
|
+
const raw = {
|
|
1153
|
+
input: void 0,
|
|
1154
|
+
output: void 0,
|
|
1155
|
+
cacheRead: void 0,
|
|
1156
|
+
cacheWrite: void 0
|
|
1157
|
+
};
|
|
1158
|
+
let usage;
|
|
1159
|
+
function stateAt(index) {
|
|
1160
|
+
const state = typeof index === "number" ? blocks.get(index) : void 0;
|
|
1161
|
+
if (!state) throw new LlmError7("OpenDesign\uFF1A\u6D41\u5F0F\u5185\u5BB9\u5757\u7D22\u5F15\u65E0\u6548", "INVALID_RESPONSE");
|
|
1162
|
+
return state;
|
|
1163
|
+
}
|
|
1164
|
+
function assign(state) {
|
|
1165
|
+
if (state.index < 0) {
|
|
1166
|
+
state.index = nextIndex++;
|
|
1167
|
+
count2++;
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
function close() {
|
|
1171
|
+
const out = [];
|
|
1172
|
+
for (const state of blocks.values()) {
|
|
1173
|
+
if (!state.started) continue;
|
|
1174
|
+
if (state.kind === "text") out.push({ type: "block-end", index: state.index, block: { type: "text", text: state.text } });
|
|
1175
|
+
else if (state.kind === "reasoning") out.push({ type: "block-end", index: state.index, block: { type: "reasoning", text: state.text } });
|
|
1176
|
+
else if (state.kind === "tool") {
|
|
1177
|
+
if (!state.id || !state.name) throw new LlmError7("OpenDesign\uFF1A\u5DE5\u5177\u8C03\u7528\u7F3A\u5C11\u6807\u8BC6\u6216\u540D\u79F0", "INVALID_RESPONSE");
|
|
1178
|
+
out.push({ type: "block-end", index: state.index, block: { type: "tool-call", id: ToolCallId2(state.id), name: state.name, arguments: state.args } });
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
return out.sort((a, b) => ("index" in a ? a.index : 0) - ("index" in b ? b.index : 0));
|
|
1182
|
+
}
|
|
1183
|
+
function reasonFromStop(stop) {
|
|
1184
|
+
switch (stop) {
|
|
1185
|
+
case "end_turn":
|
|
1186
|
+
case "stop_sequence":
|
|
1187
|
+
return { kind: "stop" };
|
|
1188
|
+
case "max_tokens":
|
|
1189
|
+
case "model_context_window_exceeded":
|
|
1190
|
+
return { kind: "max-tokens" };
|
|
1191
|
+
case "tool_use":
|
|
1192
|
+
return { kind: "tool-calls" };
|
|
1193
|
+
case "refusal":
|
|
1194
|
+
throw new LlmError7("OpenDesign\uFF1A\u6A21\u578B\u62D2\u7EDD\u56DE\u7B54\uFF08refusal\uFF09", "INVALID_RESPONSE");
|
|
1195
|
+
case "pause_turn":
|
|
1196
|
+
throw new LlmError7("OpenDesign\uFF1A\u6A21\u578B\u6682\u505C\u8F6E\u6B21\uFF0C\u9700\u91CD\u65B0\u53D1\u8D77\u8BF7\u6C42", "INVALID_RESPONSE");
|
|
1197
|
+
default:
|
|
1198
|
+
throw new LlmError7("OpenDesign\uFF1A\u6A21\u578B\u54CD\u5E94\u88AB\u8FC7\u6EE4\u6216\u5F02\u5E38\u7EC8\u6B62", "INVALID_RESPONSE");
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
for await (const frame of sseFrames(body, signal, idleMs)) {
|
|
1202
|
+
let value;
|
|
1203
|
+
try {
|
|
1204
|
+
value = JSON.parse(frame.data);
|
|
1205
|
+
} catch {
|
|
1206
|
+
throw new LlmError7("OpenDesign\uFF1A\u65E0\u6548\u7684\u6D41\u5F0F JSON", "INVALID_RESPONSE");
|
|
1207
|
+
}
|
|
1208
|
+
if (!record(value)) throw new LlmError7("OpenDesign\uFF1A\u6D41\u5F0F\u4E8B\u4EF6\u4E0D\u662F\u5BF9\u8C61", "INVALID_RESPONSE");
|
|
1209
|
+
const type = typeof value.type === "string" && value.type !== "" ? value.type : frame.event;
|
|
1210
|
+
if (!type) throw new LlmError7("OpenDesign\uFF1A\u6D41\u5F0F\u4E8B\u4EF6\u7F3A\u5C11\u7C7B\u578B", "INVALID_RESPONSE");
|
|
1211
|
+
const out = [];
|
|
1212
|
+
switch (type) {
|
|
1213
|
+
case "message_start": {
|
|
1214
|
+
const message = record(value.message) ? value.message : {};
|
|
1215
|
+
const counters = record(message.usage) ? message.usage : {};
|
|
1216
|
+
raw.input = num(counters.input_tokens) ?? raw.input;
|
|
1217
|
+
raw.output = num(counters.output_tokens) ?? raw.output;
|
|
1218
|
+
raw.cacheRead = num(counters.cache_read_input_tokens) ?? raw.cacheRead;
|
|
1219
|
+
raw.cacheWrite = num(counters.cache_creation_input_tokens) ?? raw.cacheWrite;
|
|
1220
|
+
break;
|
|
1221
|
+
}
|
|
1222
|
+
case "content_block_start": {
|
|
1223
|
+
const index = value.index;
|
|
1224
|
+
if (typeof index !== "number" || !Number.isSafeInteger(index) || index < 0) throw new LlmError7("OpenDesign\uFF1A\u6D41\u5F0F\u5185\u5BB9\u5757\u7D22\u5F15\u65E0\u6548", "INVALID_RESPONSE");
|
|
1225
|
+
const block = record(value.content_block) ? value.content_block : {};
|
|
1226
|
+
const kind = typeof block.type === "string" ? block.type : "";
|
|
1227
|
+
const state = { index: -1, kind: "skip", text: "", id: "", name: "", args: "", started: false };
|
|
1228
|
+
if (kind === "text") state.kind = "text";
|
|
1229
|
+
else if (kind === "thinking") state.kind = "reasoning";
|
|
1230
|
+
else if (kind === "tool_use") state.kind = "tool";
|
|
1231
|
+
if (state.kind === "tool") {
|
|
1232
|
+
state.id = typeof block.id === "string" ? block.id : "";
|
|
1233
|
+
state.name = typeof block.name === "string" ? block.name : "";
|
|
1234
|
+
sawTool = true;
|
|
1235
|
+
}
|
|
1236
|
+
if (state.kind === "text" && typeof block.text === "string" && block.text !== "") state.text = block.text;
|
|
1237
|
+
if (state.kind === "reasoning" && typeof block.thinking === "string" && block.thinking !== "") state.text = block.thinking;
|
|
1238
|
+
blocks.set(index, state);
|
|
1239
|
+
if (state.kind === "tool") {
|
|
1240
|
+
assign(state);
|
|
1241
|
+
state.started = true;
|
|
1242
|
+
out.push({ type: "block-start", index: state.index, blockType: "tool-call" });
|
|
1243
|
+
if (state.id) out.push({ type: "tool-call-delta", index: state.index, id: ToolCallId2(state.id), ...state.name ? { name: state.name } : {}, argumentsDelta: "" });
|
|
1244
|
+
} else if ((state.kind === "text" || state.kind === "reasoning") && state.text !== "") {
|
|
1245
|
+
assign(state);
|
|
1246
|
+
state.started = true;
|
|
1247
|
+
out.push({ type: "block-start", index: state.index, blockType: state.kind === "text" ? "text" : "reasoning" });
|
|
1248
|
+
out.push(state.kind === "text" ? { type: "text-delta", index: state.index, text: state.text } : { type: "reasoning-delta", index: state.index, text: state.text });
|
|
1249
|
+
}
|
|
1250
|
+
break;
|
|
1251
|
+
}
|
|
1252
|
+
case "content_block_delta": {
|
|
1253
|
+
const state = stateAt(value.index);
|
|
1254
|
+
const delta = record(value.delta) ? value.delta : {};
|
|
1255
|
+
const deltaType = typeof delta.type === "string" ? delta.type : "";
|
|
1256
|
+
if (deltaType === "text_delta") {
|
|
1257
|
+
const text2 = typeof delta.text === "string" ? delta.text : "";
|
|
1258
|
+
if (!text2) break;
|
|
1259
|
+
assign(state);
|
|
1260
|
+
if (!state.started) {
|
|
1261
|
+
state.started = true;
|
|
1262
|
+
out.push({ type: "block-start", index: state.index, blockType: "text" });
|
|
1263
|
+
}
|
|
1264
|
+
state.text += text2;
|
|
1265
|
+
out.push({ type: "text-delta", index: state.index, text: text2 });
|
|
1266
|
+
break;
|
|
1267
|
+
}
|
|
1268
|
+
if (deltaType === "thinking_delta") {
|
|
1269
|
+
const text2 = typeof delta.thinking === "string" ? delta.thinking : "";
|
|
1270
|
+
if (!text2) break;
|
|
1271
|
+
assign(state);
|
|
1272
|
+
if (!state.started) {
|
|
1273
|
+
state.started = true;
|
|
1274
|
+
out.push({ type: "block-start", index: state.index, blockType: "reasoning" });
|
|
1275
|
+
}
|
|
1276
|
+
state.text += text2;
|
|
1277
|
+
out.push({ type: "reasoning-delta", index: state.index, text: text2 });
|
|
1278
|
+
break;
|
|
1279
|
+
}
|
|
1280
|
+
if (deltaType === "input_json_delta") {
|
|
1281
|
+
if (state.kind !== "tool") throw new LlmError7("OpenDesign\uFF1A\u5DE5\u5177\u53C2\u6570\u5206\u7247\u843D\u5728\u975E\u5DE5\u5177\u5757\u4E0A", "INVALID_RESPONSE");
|
|
1282
|
+
const part = typeof delta.partial_json === "string" ? delta.partial_json : "";
|
|
1283
|
+
state.args += part;
|
|
1284
|
+
if (state.id) out.push({ type: "tool-call-delta", index: state.index, id: ToolCallId2(state.id), ...state.name ? { name: state.name } : {}, argumentsDelta: part });
|
|
1285
|
+
break;
|
|
1286
|
+
}
|
|
1287
|
+
break;
|
|
1288
|
+
}
|
|
1289
|
+
case "message_delta": {
|
|
1290
|
+
const delta = record(value.delta) ? value.delta : {};
|
|
1291
|
+
if (typeof delta.stop_reason === "string") stopReason = delta.stop_reason;
|
|
1292
|
+
const counters = record(value.usage) ? value.usage : {};
|
|
1293
|
+
raw.output = num(counters.output_tokens) ?? raw.output;
|
|
1294
|
+
raw.input = num(counters.input_tokens) ?? raw.input;
|
|
1295
|
+
raw.cacheRead = num(counters.cache_read_input_tokens) ?? raw.cacheRead;
|
|
1296
|
+
raw.cacheWrite = num(counters.cache_creation_input_tokens) ?? raw.cacheWrite;
|
|
1297
|
+
break;
|
|
1298
|
+
}
|
|
1299
|
+
case "message_stop": {
|
|
1300
|
+
const reason = reasonFromStop(stopReason);
|
|
1301
|
+
if (reason.kind === "tool-calls" && !sawTool) throw new LlmError7("OpenDesign\uFF1A\u54CD\u5E94\u58F0\u660E\u5DE5\u5177\u8C03\u7528\uFF0C\u4F46\u672A\u8FD4\u56DE\u4EFB\u4F55\u5DE5\u5177\u5757", "INVALID_RESPONSE");
|
|
1302
|
+
if (!count2) throw new LlmError7("OpenDesign\uFF1A\u6A21\u578B\u8FD4\u56DE\u7A7A\u54CD\u5E94", "EMPTY_RESPONSE");
|
|
1303
|
+
usage = anthropicUsage(raw) ?? usage;
|
|
1304
|
+
out.push(...close());
|
|
1305
|
+
if (usage) out.push({ type: "usage", usage });
|
|
1306
|
+
finish = reason;
|
|
1307
|
+
out.push({ type: "finish", reason });
|
|
1308
|
+
break;
|
|
1309
|
+
}
|
|
1310
|
+
case "error":
|
|
1311
|
+
throw anthropicError(void 0, value);
|
|
1312
|
+
case "ping":
|
|
1313
|
+
default:
|
|
1314
|
+
break;
|
|
1315
|
+
}
|
|
1316
|
+
for (const chunk of out) yield chunk;
|
|
1317
|
+
if (finish) break;
|
|
1318
|
+
}
|
|
1319
|
+
signal.throwIfAborted();
|
|
1320
|
+
if (!finish) throw new LlmError7("OpenDesign\uFF1A\u54CD\u5E94\u6D41\u63D0\u524D\u4E2D\u65AD\uFF0C\u672A\u6536\u5230\u7ED3\u675F\u4E8B\u4EF6", "INVALID_RESPONSE");
|
|
1321
|
+
}
|
|
1322
|
+
var messages = {
|
|
1323
|
+
id: "messages",
|
|
1324
|
+
build: buildMessagesRequest,
|
|
1325
|
+
stream: streamMessages
|
|
1326
|
+
};
|
|
1327
|
+
|
|
1328
|
+
// src/protocols/responses.ts
|
|
1329
|
+
import {
|
|
1330
|
+
LlmError as LlmError8,
|
|
1331
|
+
ToolCallId as ToolCallId3
|
|
1332
|
+
} from "@deepseek-ai/dsh-llm";
|
|
1333
|
+
var REASONING_EFFORTS2 = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
1334
|
+
function userContent(text2, images) {
|
|
1335
|
+
if (!images.length) return text2;
|
|
1336
|
+
return [
|
|
1337
|
+
...text2 ? [{ type: "input_text", text: text2 }] : [],
|
|
1338
|
+
...images.map((url) => ({ type: "input_image", image_url: url, detail: "auto" }))
|
|
1339
|
+
];
|
|
1340
|
+
}
|
|
1341
|
+
function buildResponsesRequest(ctx) {
|
|
1342
|
+
const { options, model } = ctx;
|
|
1343
|
+
const instructions = [];
|
|
1344
|
+
const input = [];
|
|
1345
|
+
const lookup = ctx.images.size ? (ref) => ctx.images.get(ref.attachmentId) : void 0;
|
|
1346
|
+
let lifted = [];
|
|
1347
|
+
const flushLifted = () => {
|
|
1348
|
+
if (!lifted.length) return;
|
|
1349
|
+
input.push({ role: "user", content: userContent("(Image content from the preceding tool result.)", lifted) });
|
|
1350
|
+
lifted = [];
|
|
1351
|
+
};
|
|
1352
|
+
for (const message of options.messages) {
|
|
1353
|
+
if (message.role !== "tool") flushLifted();
|
|
1354
|
+
const projected = projectContent(message.content, message.role === "user" || message.role === "tool" ? lookup : void 0);
|
|
1355
|
+
switch (message.role) {
|
|
1356
|
+
case "system":
|
|
1357
|
+
case "developer":
|
|
1358
|
+
if (projected.text) instructions.push(projected.text);
|
|
1359
|
+
break;
|
|
1360
|
+
case "user":
|
|
1361
|
+
if (projected.text || projected.images.length) input.push({ role: "user", content: userContent(projected.text, projected.images) });
|
|
1362
|
+
break;
|
|
1363
|
+
case "assistant": {
|
|
1364
|
+
if (projected.text) input.push({ role: "assistant", content: projected.text });
|
|
1365
|
+
for (const block of message.content) {
|
|
1366
|
+
if (block.type === "tool-call") input.push({ type: "function_call", call_id: block.id, name: block.name, arguments: block.arguments });
|
|
1367
|
+
}
|
|
1368
|
+
break;
|
|
1369
|
+
}
|
|
1370
|
+
case "tool": {
|
|
1371
|
+
input.push({
|
|
1372
|
+
type: "function_call_output",
|
|
1373
|
+
call_id: message.toolCallId,
|
|
1374
|
+
output: projected.text || (projected.images.length ? "(Image content follows in the next user message.)" : "")
|
|
1375
|
+
});
|
|
1376
|
+
lifted.push(...projected.images);
|
|
1377
|
+
break;
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
flushLifted();
|
|
1382
|
+
if (options.system?.trim()) instructions.unshift(options.system);
|
|
1383
|
+
const body = { model: model.id, input, stream: true, store: false };
|
|
1384
|
+
if (instructions.length) body.instructions = instructions.join("\n\n");
|
|
1385
|
+
if (options.tools?.length) body.tools = options.tools.map((tool) => ({ type: "function", name: tool.name, description: tool.description, parameters: tool.parameters }));
|
|
1386
|
+
if (options.temperature !== void 0) body.temperature = options.temperature;
|
|
1387
|
+
if (ctx.topP !== void 0) body.top_p = ctx.topP;
|
|
1388
|
+
if (options.maxTokens !== void 0) {
|
|
1389
|
+
if (!Number.isSafeInteger(options.maxTokens) || options.maxTokens <= 0) throw new LlmError8("maxTokens \u5FC5\u987B\u662F\u6B63\u6574\u6570", "INVALID_REQUEST");
|
|
1390
|
+
body.max_output_tokens = options.maxTokens;
|
|
1391
|
+
}
|
|
1392
|
+
if (options.reasoningEffort !== void 0) {
|
|
1393
|
+
const effort2 = String(options.reasoningEffort);
|
|
1394
|
+
if (!REASONING_EFFORTS2.includes(effort2)) throw new LlmError8(`Responses \u534F\u8BAE\u4E0D\u63A5\u53D7\u601D\u8003\u6DF1\u5EA6\u300C${effort2}\u300D`, "UNSUPPORTED_OPTION");
|
|
1395
|
+
body.reasoning = { effort: effort2, summary: "auto" };
|
|
1396
|
+
}
|
|
1397
|
+
if (options.stop?.length) throw new LlmError8("Responses \u534F\u8BAE\u4E0D\u652F\u6301 stop \u5E8F\u5217", "UNSUPPORTED_OPTION");
|
|
1398
|
+
return { url: inferenceEndpoint(ctx.baseURL, "responses"), headers: streamHeaders(ctx.apiKey), body };
|
|
1399
|
+
}
|
|
1400
|
+
function str(value) {
|
|
1401
|
+
return typeof value === "string" && value !== "" ? value : void 0;
|
|
1402
|
+
}
|
|
1403
|
+
function int(value) {
|
|
1404
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
|
|
1405
|
+
}
|
|
1406
|
+
function responseUsage(response) {
|
|
1407
|
+
if (!record(response.usage)) return void 0;
|
|
1408
|
+
const usage = response.usage;
|
|
1409
|
+
const input = record(usage.input_tokens_details) ? usage.input_tokens_details : {};
|
|
1410
|
+
const output = record(usage.output_tokens_details) ? usage.output_tokens_details : {};
|
|
1411
|
+
return openAiUsage({
|
|
1412
|
+
input: typeof usage.input_tokens === "number" ? usage.input_tokens : void 0,
|
|
1413
|
+
output: typeof usage.output_tokens === "number" ? usage.output_tokens : void 0,
|
|
1414
|
+
cacheRead: typeof input.cached_tokens === "number" ? input.cached_tokens : void 0,
|
|
1415
|
+
cacheWrite: typeof input.cache_write_tokens === "number" ? input.cache_write_tokens : void 0,
|
|
1416
|
+
reasoning: typeof output.reasoning_tokens === "number" ? output.reasoning_tokens : void 0
|
|
1417
|
+
});
|
|
1418
|
+
}
|
|
1419
|
+
async function* streamResponses(body, signal, idleMs) {
|
|
1420
|
+
let nextIndex = 0;
|
|
1421
|
+
let count2 = 0;
|
|
1422
|
+
let usage;
|
|
1423
|
+
let finish;
|
|
1424
|
+
const texts = /* @__PURE__ */ new Map();
|
|
1425
|
+
const reasonings = /* @__PURE__ */ new Map();
|
|
1426
|
+
const calls = /* @__PURE__ */ new Map();
|
|
1427
|
+
const keyByOutput = /* @__PURE__ */ new Map();
|
|
1428
|
+
function textState(key) {
|
|
1429
|
+
let state = texts.get(key);
|
|
1430
|
+
if (!state) {
|
|
1431
|
+
state = { index: nextIndex++, text: "", started: false };
|
|
1432
|
+
texts.set(key, state);
|
|
1433
|
+
count2++;
|
|
1434
|
+
}
|
|
1435
|
+
return state;
|
|
1436
|
+
}
|
|
1437
|
+
function reasoningState(key) {
|
|
1438
|
+
let state = reasonings.get(key);
|
|
1439
|
+
if (!state) {
|
|
1440
|
+
state = { index: nextIndex++, text: "", started: false };
|
|
1441
|
+
reasonings.set(key, state);
|
|
1442
|
+
count2++;
|
|
1443
|
+
}
|
|
1444
|
+
return state;
|
|
1445
|
+
}
|
|
1446
|
+
function appendText(state, delta, out) {
|
|
1447
|
+
if (!state.started) {
|
|
1448
|
+
state.started = true;
|
|
1449
|
+
out.push({ type: "block-start", index: state.index, blockType: "text" });
|
|
1450
|
+
}
|
|
1451
|
+
state.text += delta;
|
|
1452
|
+
out.push({ type: "text-delta", index: state.index, text: delta });
|
|
1453
|
+
}
|
|
1454
|
+
function appendReasoning(state, delta, out) {
|
|
1455
|
+
if (!state.started) {
|
|
1456
|
+
state.started = true;
|
|
1457
|
+
out.push({ type: "block-start", index: state.index, blockType: "reasoning" });
|
|
1458
|
+
}
|
|
1459
|
+
state.text += delta;
|
|
1460
|
+
out.push({ type: "reasoning-delta", index: state.index, text: delta });
|
|
1461
|
+
}
|
|
1462
|
+
function callState(key, item, out) {
|
|
1463
|
+
let call = calls.get(key);
|
|
1464
|
+
if (!call) {
|
|
1465
|
+
call = { index: nextIndex++, id: "", name: "", args: "", started: false };
|
|
1466
|
+
calls.set(key, call);
|
|
1467
|
+
count2++;
|
|
1468
|
+
out?.push({ type: "block-start", index: call.index, blockType: "tool-call" });
|
|
1469
|
+
}
|
|
1470
|
+
if (item) {
|
|
1471
|
+
const id = str(item.call_id) ?? str(item.id);
|
|
1472
|
+
if (id && !call.id) call.id = id;
|
|
1473
|
+
const name2 = str(item.name);
|
|
1474
|
+
if (name2) call.name = name2;
|
|
1475
|
+
}
|
|
1476
|
+
return call;
|
|
1477
|
+
}
|
|
1478
|
+
function flushCall(call, delta, out) {
|
|
1479
|
+
if (!call.id) return;
|
|
1480
|
+
const name2 = call.name ? { name: call.name } : {};
|
|
1481
|
+
if (!call.started) {
|
|
1482
|
+
call.started = true;
|
|
1483
|
+
out.push({ type: "tool-call-delta", index: call.index, id: ToolCallId3(call.id), ...name2, argumentsDelta: call.args });
|
|
1484
|
+
return;
|
|
1485
|
+
}
|
|
1486
|
+
out.push({ type: "tool-call-delta", index: call.index, id: ToolCallId3(call.id), ...name2, argumentsDelta: delta });
|
|
1487
|
+
}
|
|
1488
|
+
function keyFor(value, kind, outputIndex) {
|
|
1489
|
+
const itemId = str(value.item_id) ?? (outputIndex === void 0 ? void 0 : keyByOutput.get(outputIndex));
|
|
1490
|
+
return `${kind}:${itemId ?? `idx:${outputIndex ?? -1}`}`;
|
|
1491
|
+
}
|
|
1492
|
+
function hydrate(output, out) {
|
|
1493
|
+
if (!Array.isArray(output)) return;
|
|
1494
|
+
for (const item of output) {
|
|
1495
|
+
if (!record(item)) continue;
|
|
1496
|
+
if (item.type === "function_call") {
|
|
1497
|
+
const itemId = str(item.id) ?? str(item.call_id);
|
|
1498
|
+
const callId = str(item.call_id) ?? itemId;
|
|
1499
|
+
if (!callId || [...calls.values()].some((call2) => call2.id === callId)) continue;
|
|
1500
|
+
const call = callState(`call:${itemId ?? callId}`, item, out);
|
|
1501
|
+
call.args = str(item.arguments) ?? call.args;
|
|
1502
|
+
flushCall(call, call.args, out);
|
|
1503
|
+
continue;
|
|
1504
|
+
}
|
|
1505
|
+
if (item.type === "message") {
|
|
1506
|
+
const key = `text:${str(item.id) ?? "unknown"}:0`;
|
|
1507
|
+
if (texts.has(key)) continue;
|
|
1508
|
+
const content2 = Array.isArray(item.content) ? item.content : [];
|
|
1509
|
+
const text2 = content2.map((part) => record(part) && (part.type === "output_text" || part.type === "refusal") ? str(part.text) ?? "" : "").join("");
|
|
1510
|
+
if (!text2) continue;
|
|
1511
|
+
appendText(textState(key), text2, out);
|
|
1512
|
+
continue;
|
|
1513
|
+
}
|
|
1514
|
+
if (item.type === "reasoning") {
|
|
1515
|
+
const key = `reasoning:${str(item.id) ?? "unknown"}:s0`;
|
|
1516
|
+
if (reasonings.has(key)) continue;
|
|
1517
|
+
const summary = Array.isArray(item.summary) ? item.summary : [];
|
|
1518
|
+
const text2 = summary.map((part) => record(part) ? str(part.text) ?? "" : "").join("");
|
|
1519
|
+
if (!text2) continue;
|
|
1520
|
+
appendReasoning(reasoningState(key), text2, out);
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
function close() {
|
|
1525
|
+
const out = [];
|
|
1526
|
+
for (const state of texts.values()) if (state.started) out.push({ type: "block-end", index: state.index, block: { type: "text", text: state.text } });
|
|
1527
|
+
for (const state of reasonings.values()) if (state.started) out.push({ type: "block-end", index: state.index, block: { type: "reasoning", text: state.text } });
|
|
1528
|
+
for (const call of calls.values()) {
|
|
1529
|
+
if (!call.id || !call.name) throw new LlmError8("OpenDesign\uFF1A\u5DE5\u5177\u8C03\u7528\u7F3A\u5C11\u6807\u8BC6\u6216\u540D\u79F0", "INVALID_RESPONSE");
|
|
1530
|
+
out.push({ type: "block-end", index: call.index, block: { type: "tool-call", id: ToolCallId3(call.id), name: call.name, arguments: call.args } });
|
|
1531
|
+
}
|
|
1532
|
+
return out.sort((a, b) => ("index" in a ? a.index : 0) - ("index" in b ? b.index : 0));
|
|
1533
|
+
}
|
|
1534
|
+
function reasonOf(type, response) {
|
|
1535
|
+
if (type === "response.incomplete") {
|
|
1536
|
+
const details = record(response.incomplete_details) ? response.incomplete_details : {};
|
|
1537
|
+
const why = str(details.reason) ?? "";
|
|
1538
|
+
if (why === "max_tokens" || why === "max_output_tokens") return { kind: "max-tokens" };
|
|
1539
|
+
if (why === "content_filter") throw new LlmError8("OpenDesign\uFF1A\u6A21\u578B\u54CD\u5E94\u88AB\u5185\u5BB9\u8FC7\u6EE4", "INVALID_RESPONSE");
|
|
1540
|
+
throw new LlmError8("OpenDesign\uFF1A\u6A21\u578B\u54CD\u5E94\u672A\u5B8C\u6210\uFF0C\u4E0A\u6E38\u4E2D\u65AD", "SERVER");
|
|
1541
|
+
}
|
|
1542
|
+
const output = Array.isArray(response.output) ? response.output : [];
|
|
1543
|
+
return output.some((item) => record(item) && item.type === "function_call") || calls.size > 0 ? { kind: "tool-calls" } : { kind: "stop" };
|
|
1544
|
+
}
|
|
1545
|
+
for await (const frame of sseFrames(body, signal, idleMs)) {
|
|
1546
|
+
if (frame.data.trim() === "[DONE]") break;
|
|
1547
|
+
let value;
|
|
1548
|
+
try {
|
|
1549
|
+
value = JSON.parse(frame.data);
|
|
1550
|
+
} catch {
|
|
1551
|
+
throw new LlmError8("OpenDesign\uFF1A\u65E0\u6548\u7684\u6D41\u5F0F JSON", "INVALID_RESPONSE");
|
|
1552
|
+
}
|
|
1553
|
+
if (!record(value)) throw new LlmError8("OpenDesign\uFF1A\u6D41\u5F0F\u4E8B\u4EF6\u4E0D\u662F\u5BF9\u8C61", "INVALID_RESPONSE");
|
|
1554
|
+
const type = str(value.type) ?? frame.event;
|
|
1555
|
+
if (!type) throw new LlmError8("OpenDesign\uFF1A\u6D41\u5F0F\u4E8B\u4EF6\u7F3A\u5C11\u7C7B\u578B", "INVALID_RESPONSE");
|
|
1556
|
+
const out = [];
|
|
1557
|
+
switch (type) {
|
|
1558
|
+
case "response.output_item.added": {
|
|
1559
|
+
const outputIndex = int(value.output_index);
|
|
1560
|
+
const item = record(value.item) ? value.item : void 0;
|
|
1561
|
+
if (item && outputIndex !== void 0) {
|
|
1562
|
+
const itemId = str(item.id) ?? str(item.call_id);
|
|
1563
|
+
if (itemId) keyByOutput.set(outputIndex, itemId);
|
|
1564
|
+
}
|
|
1565
|
+
if (item?.type === "function_call") callState(keyFor({ item: value.item }, "call", outputIndex), item, out);
|
|
1566
|
+
break;
|
|
1567
|
+
}
|
|
1568
|
+
case "response.output_text.delta":
|
|
1569
|
+
case "response.output_text.done": {
|
|
1570
|
+
if (type.endsWith(".done")) break;
|
|
1571
|
+
const delta = str(value.delta);
|
|
1572
|
+
if (!delta) break;
|
|
1573
|
+
const outputIndex = int(value.output_index);
|
|
1574
|
+
const contentIndex = int(value.content_index) ?? 0;
|
|
1575
|
+
const itemId = str(value.item_id) ?? (outputIndex === void 0 ? void 0 : keyByOutput.get(outputIndex));
|
|
1576
|
+
appendText(textState(`text:${itemId ?? `idx:${outputIndex ?? -1}`}:${contentIndex}`), delta, out);
|
|
1577
|
+
break;
|
|
1578
|
+
}
|
|
1579
|
+
case "response.refusal.delta": {
|
|
1580
|
+
const delta = str(value.delta);
|
|
1581
|
+
if (!delta) break;
|
|
1582
|
+
const outputIndex = int(value.output_index);
|
|
1583
|
+
const contentIndex = int(value.content_index) ?? 0;
|
|
1584
|
+
const itemId = str(value.item_id) ?? (outputIndex === void 0 ? void 0 : keyByOutput.get(outputIndex));
|
|
1585
|
+
appendText(textState(`text:${itemId ?? `idx:${outputIndex ?? -1}`}:${contentIndex}`), delta, out);
|
|
1586
|
+
break;
|
|
1587
|
+
}
|
|
1588
|
+
case "response.reasoning_summary_text.delta": {
|
|
1589
|
+
const delta = str(value.delta);
|
|
1590
|
+
if (!delta) break;
|
|
1591
|
+
const key = keyFor(value, "reasoning", int(value.output_index));
|
|
1592
|
+
appendReasoning(reasoningState(`${key}:s${int(value.summary_index) ?? 0}`), delta, out);
|
|
1593
|
+
break;
|
|
1594
|
+
}
|
|
1595
|
+
case "response.reasoning_text.delta": {
|
|
1596
|
+
const delta = str(value.delta);
|
|
1597
|
+
if (!delta) break;
|
|
1598
|
+
const key = keyFor(value, "reasoning", int(value.output_index));
|
|
1599
|
+
appendReasoning(reasoningState(`${key}:t`), delta, out);
|
|
1600
|
+
break;
|
|
1601
|
+
}
|
|
1602
|
+
case "response.function_call_arguments.delta": {
|
|
1603
|
+
const call = callState(keyFor(value, "call", int(value.output_index)), void 0, out);
|
|
1604
|
+
const delta = str(value.delta) ?? "";
|
|
1605
|
+
call.args += delta;
|
|
1606
|
+
flushCall(call, delta, out);
|
|
1607
|
+
break;
|
|
1608
|
+
}
|
|
1609
|
+
case "response.output_item.done": {
|
|
1610
|
+
const item = record(value.item) ? value.item : void 0;
|
|
1611
|
+
if (!item) break;
|
|
1612
|
+
const outputIndex = int(value.output_index);
|
|
1613
|
+
if (item.type === "function_call") {
|
|
1614
|
+
const call = callState(keyFor(value, "call", outputIndex), item, out);
|
|
1615
|
+
const args = str(item.arguments);
|
|
1616
|
+
if (args !== void 0 && call.args === "") {
|
|
1617
|
+
call.args = args;
|
|
1618
|
+
flushCall(call, args, out);
|
|
1619
|
+
} else flushCall(call, "", out);
|
|
1620
|
+
} else if (item.type === "message") {
|
|
1621
|
+
const key = `text:${str(item.id) ?? `idx:${outputIndex ?? -1}`}:0`;
|
|
1622
|
+
if (!texts.has(key)) {
|
|
1623
|
+
const content2 = Array.isArray(item.content) ? item.content : [];
|
|
1624
|
+
const text2 = content2.map((part) => record(part) && (part.type === "output_text" || part.type === "refusal") ? str(part.text) ?? "" : "").join("");
|
|
1625
|
+
if (text2) appendText(textState(key), text2, out);
|
|
1626
|
+
}
|
|
1627
|
+
} else if (item.type === "reasoning") {
|
|
1628
|
+
const key = `reasoning:${str(item.id) ?? `idx:${outputIndex ?? -1}`}:s0`;
|
|
1629
|
+
if (!reasonings.has(key)) {
|
|
1630
|
+
const summary = Array.isArray(item.summary) ? item.summary : [];
|
|
1631
|
+
const text2 = summary.map((part) => record(part) ? str(part.text) ?? "" : "").join("");
|
|
1632
|
+
if (text2) appendReasoning(reasoningState(key), text2, out);
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
break;
|
|
1636
|
+
}
|
|
1637
|
+
case "response.completed":
|
|
1638
|
+
case "response.incomplete":
|
|
1639
|
+
case "response.failed": {
|
|
1640
|
+
const response = record(value.response) ? value.response : {};
|
|
1641
|
+
if (type === "response.failed") throw inBandError(record(response.error) ? response.error : {}, "SERVER");
|
|
1642
|
+
usage = responseUsage(response) ?? usage;
|
|
1643
|
+
hydrate(response.output, out);
|
|
1644
|
+
const reason = reasonOf(type, response);
|
|
1645
|
+
if (reason.kind === "tool-calls" && calls.size === 0) throw new LlmError8("OpenDesign\uFF1A\u54CD\u5E94\u58F0\u660E\u5DE5\u5177\u8C03\u7528\uFF0C\u4F46\u672A\u8FD4\u56DE\u4EFB\u4F55\u5DE5\u5177\u5757", "INVALID_RESPONSE");
|
|
1646
|
+
if (!count2) throw new LlmError8("OpenDesign\uFF1A\u6A21\u578B\u8FD4\u56DE\u7A7A\u54CD\u5E94", "EMPTY_RESPONSE");
|
|
1647
|
+
out.push(...close());
|
|
1648
|
+
if (usage) out.push({ type: "usage", usage });
|
|
1649
|
+
finish = reason;
|
|
1650
|
+
out.push({ type: "finish", reason });
|
|
1651
|
+
break;
|
|
1652
|
+
}
|
|
1653
|
+
case "error":
|
|
1654
|
+
throw inBandError(value);
|
|
1655
|
+
default:
|
|
1656
|
+
break;
|
|
1657
|
+
}
|
|
1658
|
+
for (const chunk of out) yield chunk;
|
|
1659
|
+
if (finish) break;
|
|
1660
|
+
}
|
|
1661
|
+
signal.throwIfAborted();
|
|
1662
|
+
if (!finish) throw new LlmError8("OpenDesign\uFF1A\u54CD\u5E94\u6D41\u63D0\u524D\u4E2D\u65AD\uFF0C\u672A\u6536\u5230\u7ED3\u675F\u4E8B\u4EF6", "INVALID_RESPONSE");
|
|
1663
|
+
}
|
|
1664
|
+
var responses = {
|
|
1665
|
+
id: "responses",
|
|
1666
|
+
build: buildResponsesRequest,
|
|
1667
|
+
stream: streamResponses
|
|
1668
|
+
};
|
|
1669
|
+
|
|
1670
|
+
// src/protocols/index.ts
|
|
1671
|
+
var PROTOCOLS = {
|
|
1672
|
+
"chat-completions": chatCompletions,
|
|
1673
|
+
responses,
|
|
1674
|
+
messages
|
|
1675
|
+
};
|
|
1676
|
+
function protocolFor(id) {
|
|
1677
|
+
const protocol = PROTOCOLS[id];
|
|
1678
|
+
if (!protocol) throw new LlmError9(`\u672A\u77E5\u7684\u63A8\u7406\u534F\u8BAE\uFF1A${String(id)}`, "INVALID_REQUEST");
|
|
1679
|
+
return protocol;
|
|
1680
|
+
}
|
|
1681
|
+
|
|
701
1682
|
// src/adapter.ts
|
|
702
1683
|
function group(provider) {
|
|
703
1684
|
if (provider === PLAN_PROVIDER) return "plan";
|
|
704
1685
|
if (provider === PAID_PROVIDER) return "paid";
|
|
705
|
-
throw new
|
|
1686
|
+
throw new LlmError10("\u672A\u77E5\u7684 CoRouter Provider", "INVALID_REQUEST");
|
|
706
1687
|
}
|
|
707
|
-
function
|
|
708
|
-
const
|
|
709
|
-
|
|
710
|
-
return model;
|
|
1688
|
+
function outputReserve(model, protocol) {
|
|
1689
|
+
const limit = model.maxOutputTokens ?? (protocol === "messages" ? 4096 : void 0);
|
|
1690
|
+
return limit === void 0 ? void 0 : model.contextWindow === void 0 ? limit : Math.min(limit, Math.floor(model.contextWindow / 4));
|
|
711
1691
|
}
|
|
712
|
-
function
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
const reserve = outputReserve(row);
|
|
1692
|
+
function metadata(provider, row, config) {
|
|
1693
|
+
const setting = config.modelSettings?.[row.id];
|
|
1694
|
+
const reserve = setting?.maxTokens ?? outputReserve(row, config.apiProtocol ?? "chat-completions");
|
|
1695
|
+
const efforts = row.reasoningEfforts ?? (setting?.reasoningEffort ? [setting.reasoningEffort] : void 0);
|
|
717
1696
|
return {
|
|
718
1697
|
provider,
|
|
719
1698
|
id: row.id,
|
|
@@ -721,7 +1700,10 @@ function metadata(provider, row) {
|
|
|
721
1700
|
...row.inputModalities ? { inputModalities: row.inputModalities } : {},
|
|
722
1701
|
...row.contextWindow === void 0 ? {} : { context: { contextWindow: row.contextWindow - (reserve ?? 0) } },
|
|
723
1702
|
...reserve === void 0 ? {} : { defaultMaxTokens: reserve },
|
|
724
|
-
...
|
|
1703
|
+
...efforts?.length ? { reasoning: {
|
|
1704
|
+
efforts: efforts.map((id) => ({ id: ReasoningEffortId(id), name: { none: "\u5173\u95ED", minimal: "\u6700\u4F4E", low: "\u4F4E", medium: "\u4E2D", high: "\u9AD8", xhigh: "\u8F83\u9AD8", ultra: "\u6781\u9AD8", max: "\u6700\u9AD8" }[id] ?? id })),
|
|
1705
|
+
...setting?.reasoningEffort ? { defaultEffort: ReasoningEffortId(setting.reasoningEffort) } : {}
|
|
1706
|
+
} } : {}
|
|
725
1707
|
};
|
|
726
1708
|
}
|
|
727
1709
|
var CoRouterAdapter = class extends LlmAdapter {
|
|
@@ -732,67 +1714,101 @@ var CoRouterAdapter = class extends LlmAdapter {
|
|
|
732
1714
|
options;
|
|
733
1715
|
#lifetime = new AbortController();
|
|
734
1716
|
dispose() {
|
|
735
|
-
this.#lifetime.abort(new
|
|
1717
|
+
this.#lifetime.abort(new LlmError10("CoRouter \u63D2\u4EF6\u5DF2\u5378\u8F7D", "ABORTED"));
|
|
736
1718
|
}
|
|
737
1719
|
providerInfo(provider) {
|
|
738
1720
|
return { id: provider, name: group(provider) === "plan" ? "OpenDesign CoRouter \xB7 \u5957\u9910\u8D60\u9001" : "OpenDesign CoRouter \xB7 \u4ED8\u8D39\u6A21\u578B" };
|
|
739
1721
|
}
|
|
1722
|
+
modelOf(provider, id, config) {
|
|
1723
|
+
const row = (this.options.models?.() ?? MODELS).find((candidate) => candidate.id === id);
|
|
1724
|
+
if (!row || row.group !== group(provider)) throw new LlmError10("\u8BE5\u6A21\u578B\u4E0D\u5C5E\u4E8E\u5F53\u524D CoRouter Provider\uFF1B\u8BF7\u5237\u65B0\u6A21\u578B\u5217\u8868\u5E76\u9009\u62E9\u5BF9\u5E94\u901A\u9053", "INVALID_REQUEST");
|
|
1725
|
+
if (!isModelEnabled(id, config.modelSettings)) throw new LlmError10("\u8BE5\u6A21\u578B\u5DF2\u5728 CoRouter \u8BBE\u7F6E\u4E2D\u505C\u7528", "MODEL_DISABLED");
|
|
1726
|
+
const configured = applyModelSettings(row, config.modelSettings?.[id]);
|
|
1727
|
+
const output = config.modelSettings?.[id]?.maxTokens;
|
|
1728
|
+
if (output !== void 0 && configured.contextWindow !== void 0 && output >= configured.contextWindow) throw new LlmError10("\u6A21\u578B\u6700\u5927\u8F93\u51FA\u5FC5\u987B\u5C0F\u4E8E\u4E0A\u4E0B\u6587\u9884\u7B97\uFF0C\u8BF7\u8C03\u6574\u6A21\u578B\u53C2\u6570", "INVALID_REQUEST");
|
|
1729
|
+
return configured;
|
|
1730
|
+
}
|
|
740
1731
|
async listModels(provider) {
|
|
741
|
-
|
|
1732
|
+
const config = this.options.config(), selectedGroup = group(provider);
|
|
1733
|
+
return (this.options.models?.() ?? MODELS).filter((row) => row.group === selectedGroup && isModelEnabled(row.id, config.modelSettings)).map((row) => ({ provider, id: row.id, name: row.name, ...row.inputModalities ? { inputModalities: row.inputModalities } : {} }));
|
|
742
1734
|
}
|
|
743
1735
|
async resolveModel(provider, model, signal) {
|
|
744
1736
|
signal?.throwIfAborted();
|
|
745
|
-
|
|
1737
|
+
const config = this.options.config();
|
|
1738
|
+
return metadata(provider, this.modelOf(provider, model, config), config);
|
|
746
1739
|
}
|
|
747
1740
|
async prepareCall(provider, model, signal) {
|
|
748
1741
|
signal?.throwIfAborted();
|
|
749
|
-
const config = this.options.config(), row = modelOf(provider, model);
|
|
750
|
-
return { model: metadata(provider, row), stream: (options) => this.#stream(options, config, row) };
|
|
1742
|
+
const config = this.options.config(), row = this.modelOf(provider, model, config);
|
|
1743
|
+
return { model: metadata(provider, row, config), stream: (options) => this.#stream(options, config, row) };
|
|
751
1744
|
}
|
|
752
1745
|
stream(options) {
|
|
753
|
-
|
|
1746
|
+
const config = this.options.config();
|
|
1747
|
+
return this.#stream(options, config, this.modelOf(options.provider, options.model, config));
|
|
754
1748
|
}
|
|
755
1749
|
async *#stream(options, config, row) {
|
|
756
1750
|
const signal = AbortSignal.any([combinedSignal(options.signal, config.requestTimeoutMs), this.#lifetime.signal]);
|
|
757
1751
|
signal.throwIfAborted();
|
|
758
1752
|
const raw = await this.options.apiKey(config.apiKeyEnv);
|
|
759
|
-
if (!raw) throw new
|
|
1753
|
+
if (!raw) throw new LlmError10("\u672A\u914D\u7F6E CoRouter API Key\uFF1B\u8BF7\u5728\u63D2\u4EF6\u8BBE\u7F6E\u4E2D\u4FDD\u5B58\u6216\u8BBE\u7F6E\u5BF9\u5E94\u73AF\u5883\u53D8\u91CF", "MISSING_CREDENTIAL");
|
|
760
1754
|
const key = assertUsableApiKey(raw, "dsh-opendesign-corouter", config.apiKeyEnv);
|
|
1755
|
+
const setting = config.modelSettings?.[row.id] ?? {};
|
|
1756
|
+
const protocolId = config.apiProtocol ?? "chat-completions";
|
|
761
1757
|
const images = /* @__PURE__ */ new Map();
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
if (
|
|
1758
|
+
const attemptedImages = /* @__PURE__ */ new Set();
|
|
1759
|
+
const unreadableImages = /* @__PURE__ */ new Set();
|
|
1760
|
+
if (row.inputModalities?.includes("image") && this.options.imageDataUrl) for (const message of options.messages.filter((item) => item.role === "user" || item.role === "tool")) for (const block of message.content) {
|
|
1761
|
+
if (block.type !== "image" || block.offloaded || attemptedImages.has(block.attachment.attachmentId)) continue;
|
|
1762
|
+
attemptedImages.add(block.attachment.attachmentId);
|
|
1763
|
+
try {
|
|
1764
|
+
const url = await this.options.imageDataUrl(block.attachment, signal);
|
|
1765
|
+
signal.throwIfAborted();
|
|
1766
|
+
if (url) images.set(block.attachment.attachmentId, url);
|
|
1767
|
+
else unreadableImages.add(block.attachment.attachmentId);
|
|
1768
|
+
} catch {
|
|
1769
|
+
signal.throwIfAborted();
|
|
1770
|
+
unreadableImages.add(block.attachment.attachmentId);
|
|
1771
|
+
}
|
|
766
1772
|
}
|
|
767
|
-
const
|
|
1773
|
+
const projectedOptions = unreadableImages.size === 0 ? options : {
|
|
1774
|
+
...options,
|
|
1775
|
+
messages: options.messages.map((message) => message.role !== "user" && message.role !== "tool" ? message : {
|
|
1776
|
+
...message,
|
|
1777
|
+
content: message.content.map((block) => block.type === "image" && !block.offloaded && unreadableImages.has(block.attachment.attachmentId) ? { type: "text", text: `[Image ${JSON.stringify(block.attachment.name ?? "attachment")} could not be read; attachment ${block.attachment.attachmentId}. The model's image capability has not changed.]` } : block)
|
|
1778
|
+
})
|
|
1779
|
+
};
|
|
1780
|
+
const messages2 = chatMessages(projectedOptions, (ref) => images.get(ref.attachmentId), row.id.startsWith("deepseek-") || row.id.startsWith("kimi-"));
|
|
768
1781
|
const tools = chatTools(options);
|
|
769
|
-
|
|
770
|
-
if (
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
1782
|
+
let maxTokens = options.maxTokens ?? setting.maxTokens ?? outputReserve(row, protocolId);
|
|
1783
|
+
if (maxTokens !== void 0) {
|
|
1784
|
+
if (!Number.isSafeInteger(maxTokens) || maxTokens <= 0) throw new LlmError10("maxTokens \u5FC5\u987B\u662F\u6B63\u6574\u6570", "INVALID_REQUEST");
|
|
1785
|
+
const capped = Math.min(maxTokens, row.maxOutputTokens ?? maxTokens);
|
|
1786
|
+
const room = row.contextWindow === void 0 ? capped : row.contextWindow - estimateTokens({ messages: messages2, tools });
|
|
1787
|
+
if (room < 1) throw new LlmError10("\u5F53\u524D\u8F93\u5165\u6CA1\u6709\u4E3A\u8F93\u51FA\u7559\u4E0B\u4E0A\u4E0B\u6587\u7A7A\u95F4", "CONTEXT_WINDOW_EXCEEDED");
|
|
1788
|
+
maxTokens = Math.min(capped, Math.floor(room));
|
|
1789
|
+
}
|
|
1790
|
+
const effort2 = options.reasoningEffort ?? (setting.reasoningEffort ? ReasoningEffortId(setting.reasoningEffort) : void 0);
|
|
1791
|
+
if (effort2 !== void 0 && row.reasoningEfforts && !row.reasoningEfforts.includes(effort2)) throw new LlmError10("\u8BE5\u6A21\u578B\u4E0D\u652F\u6301\u6240\u9009\u601D\u8003\u6DF1\u5EA6", "UNSUPPORTED_OPTION");
|
|
1792
|
+
const effective = {
|
|
1793
|
+
...projectedOptions,
|
|
1794
|
+
...(options.temperature ?? setting.temperature) === void 0 ? {} : { temperature: options.temperature ?? setting.temperature },
|
|
1795
|
+
...maxTokens === void 0 ? {} : { maxTokens },
|
|
1796
|
+
...effort2 === void 0 ? {} : { reasoningEffort: effort2 }
|
|
1797
|
+
};
|
|
1798
|
+
const protocol = protocolFor(protocolId);
|
|
1799
|
+
const request = protocol.build({ baseURL: config.baseURL, apiKey: key, model: row, options: effective, images, ...setting.topP === void 0 ? {} : { topP: setting.topP } });
|
|
784
1800
|
let response;
|
|
785
1801
|
try {
|
|
786
|
-
response = await (this.options.fetch ?? fetch)(
|
|
1802
|
+
response = await (this.options.fetch ?? fetch)(request.url, {
|
|
787
1803
|
method: "POST",
|
|
788
|
-
headers:
|
|
789
|
-
body: JSON.stringify(body),
|
|
1804
|
+
headers: request.headers,
|
|
1805
|
+
body: JSON.stringify(request.body),
|
|
790
1806
|
signal,
|
|
791
1807
|
redirect: "error"
|
|
792
1808
|
});
|
|
793
1809
|
} catch {
|
|
794
1810
|
if (options.signal?.aborted || this.#lifetime.signal.aborted) signal.throwIfAborted();
|
|
795
|
-
throw new
|
|
1811
|
+
throw new LlmError10(signal.aborted ? "CoRouter \u8BF7\u6C42\u8D85\u8FC7\u603B\u8D85\u65F6" : "\u65E0\u6CD5\u8FDE\u63A5 CoRouter \u63A8\u7406\u670D\u52A1", signal.aborted ? "TIMEOUT" : "SERVER");
|
|
796
1812
|
}
|
|
797
1813
|
if (!response.ok) {
|
|
798
1814
|
let error = {};
|
|
@@ -820,25 +1836,152 @@ var CoRouterAdapter = class extends LlmAdapter {
|
|
|
820
1836
|
}
|
|
821
1837
|
throw gatewayError(response.status, error);
|
|
822
1838
|
}
|
|
823
|
-
if (!response.body) throw new
|
|
1839
|
+
if (!response.body) throw new LlmError10("CoRouter \u8FD4\u56DE\u7A7A\u54CD\u5E94", "EMPTY_RESPONSE");
|
|
824
1840
|
const tracker = new EmptyTurnTracker({ enabled: row.group === "plan" && config.emptyTurnGuard && Boolean(options.tools?.length), onEmptyTurn: () => this.options.onEmptyTurn?.() });
|
|
825
1841
|
try {
|
|
826
|
-
for await (const chunk of
|
|
1842
|
+
for await (const chunk of protocol.stream(response.body, signal, config.streamIdleTimeoutMs)) {
|
|
827
1843
|
tracker.observe(chunk);
|
|
828
|
-
if (chunk.type === "finish" && row.group === "plan" && config.emptyTurnGuard) {
|
|
829
|
-
|
|
830
|
-
yield { ...chunk, reason };
|
|
831
|
-
} else yield chunk;
|
|
1844
|
+
if (chunk.type === "finish" && row.group === "plan" && config.emptyTurnGuard) yield { ...chunk, reason: tracker.resolve(chunk.reason) };
|
|
1845
|
+
else yield chunk;
|
|
832
1846
|
}
|
|
833
1847
|
} catch (error) {
|
|
834
1848
|
if (options.signal?.aborted || this.#lifetime.signal.aborted) signal.throwIfAborted();
|
|
835
|
-
if (signal.aborted) throw new
|
|
836
|
-
if (error instanceof
|
|
837
|
-
throw new
|
|
1849
|
+
if (signal.aborted) throw new LlmError10("CoRouter \u8BF7\u6C42\u8D85\u8FC7\u603B\u8D85\u65F6", "TIMEOUT");
|
|
1850
|
+
if (error instanceof LlmError10) throw error;
|
|
1851
|
+
throw new LlmError10("CoRouter \u54CD\u5E94\u6D41\u8FDE\u63A5\u4E2D\u65AD", "SERVER");
|
|
838
1852
|
}
|
|
839
1853
|
}
|
|
840
1854
|
};
|
|
841
1855
|
|
|
1856
|
+
// src/model-discovery.ts
|
|
1857
|
+
import { attributionHeaders as attributionHeaders2 } from "@deepseek-ai/dsh-llm";
|
|
1858
|
+
var DISCOVERY_TIMEOUT_MS = 2e4;
|
|
1859
|
+
var MAX_DISCOVERY_BODY_BYTES = 2 * 1024 * 1024;
|
|
1860
|
+
var MAX_MODELS = 512;
|
|
1861
|
+
var MAX_MODEL_NAME_LENGTH = 128;
|
|
1862
|
+
var MODEL_ID2 = /^[A-Za-z0-9._:/-]{1,128}$/u;
|
|
1863
|
+
var MEDIA_MODEL_ID = /^(?:gpt-image-|nano-banana-|seedream-|imagen-|veo-|doubao-seedance-|sora-)/u;
|
|
1864
|
+
var MESSAGES2 = {
|
|
1865
|
+
url: "\u6A21\u578B\u53D1\u73B0\u5730\u5740\u65E0\u6548\uFF1A\u5FC5\u987B\u662F\u7EDD\u5BF9 HTTP(S) \u5730\u5740\uFF0C\u4E14\u4E0D\u542B\u51ED\u636E\u3001\u67E5\u8BE2\u4E32\u6216\u7247\u6BB5",
|
|
1866
|
+
credential: "\u6A21\u578B\u53D1\u73B0\u51ED\u636E\u7F3A\u5931\uFF0C\u6216\u542B\u6709\u4E0D\u53EF\u7528\u4E8E HTTP \u8BF7\u6C42\u5934\u7684\u5B57\u7B26",
|
|
1867
|
+
network: "\u65E0\u6CD5\u8FDE\u63A5\u6A21\u578B\u53D1\u73B0\u670D\u52A1",
|
|
1868
|
+
timeout: "\u6A21\u578B\u53D1\u73B0\u8BF7\u6C42\u8D85\u8FC7 20 \u79D2\u65F6\u9650",
|
|
1869
|
+
aborted: "\u6A21\u578B\u53D1\u73B0\u8BF7\u6C42\u5DF2\u53D6\u6D88",
|
|
1870
|
+
auth: "\u6A21\u578B\u53D1\u73B0\u8BA4\u8BC1\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u51ED\u636E",
|
|
1871
|
+
permission: "\u6A21\u578B\u53D1\u73B0\u51ED\u636E\u65E0\u6743\u8BFB\u53D6\u6A21\u578B\u5217\u8868",
|
|
1872
|
+
"rate-limit": "\u6A21\u578B\u53D1\u73B0\u8BF7\u6C42\u88AB\u9650\u6D41\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",
|
|
1873
|
+
server: "\u6A21\u578B\u53D1\u73B0\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528",
|
|
1874
|
+
response: "\u6A21\u578B\u53D1\u73B0\u54CD\u5E94\u4E0D\u7B26\u5408 OpenAI \u6A21\u578B\u5217\u8868\u683C\u5F0F"
|
|
1875
|
+
};
|
|
1876
|
+
var ModelDiscoveryError = class extends Error {
|
|
1877
|
+
constructor(kind, status) {
|
|
1878
|
+
super(MESSAGES2[kind]);
|
|
1879
|
+
this.kind = kind;
|
|
1880
|
+
this.status = status;
|
|
1881
|
+
this.name = "ModelDiscoveryError";
|
|
1882
|
+
}
|
|
1883
|
+
kind;
|
|
1884
|
+
status;
|
|
1885
|
+
};
|
|
1886
|
+
function statusKind(status) {
|
|
1887
|
+
if (status === 401) return "auth";
|
|
1888
|
+
if (status === 403) return "permission";
|
|
1889
|
+
if (status === 429) return "rate-limit";
|
|
1890
|
+
if (status === 408 || status === 504) return "timeout";
|
|
1891
|
+
if (status >= 500) return "server";
|
|
1892
|
+
return "response";
|
|
1893
|
+
}
|
|
1894
|
+
async function boundedJson(response, signal, deadline) {
|
|
1895
|
+
const reader = response.body?.getReader();
|
|
1896
|
+
if (!reader) throw new ModelDiscoveryError("response");
|
|
1897
|
+
const declared = Number(response.headers.get("content-length"));
|
|
1898
|
+
if (Number.isFinite(declared) && declared > MAX_DISCOVERY_BODY_BYTES) {
|
|
1899
|
+
await reader.cancel().catch(() => void 0);
|
|
1900
|
+
reader.releaseLock();
|
|
1901
|
+
throw new ModelDiscoveryError("response");
|
|
1902
|
+
}
|
|
1903
|
+
let size = 0;
|
|
1904
|
+
let raw = "";
|
|
1905
|
+
const decoder = new TextDecoder();
|
|
1906
|
+
try {
|
|
1907
|
+
while (true) {
|
|
1908
|
+
const chunk = await reader.read();
|
|
1909
|
+
if (chunk.done) break;
|
|
1910
|
+
size += chunk.value.byteLength;
|
|
1911
|
+
if (size > MAX_DISCOVERY_BODY_BYTES) throw new ModelDiscoveryError("response");
|
|
1912
|
+
raw += decoder.decode(chunk.value, { stream: true });
|
|
1913
|
+
}
|
|
1914
|
+
raw += decoder.decode();
|
|
1915
|
+
} catch (error) {
|
|
1916
|
+
if (signal?.aborted) throw new ModelDiscoveryError("aborted");
|
|
1917
|
+
if (deadline.aborted) throw new ModelDiscoveryError("timeout");
|
|
1918
|
+
if (error instanceof ModelDiscoveryError) throw error;
|
|
1919
|
+
throw new ModelDiscoveryError("response");
|
|
1920
|
+
} finally {
|
|
1921
|
+
await reader.cancel().catch(() => void 0);
|
|
1922
|
+
reader.releaseLock();
|
|
1923
|
+
}
|
|
1924
|
+
try {
|
|
1925
|
+
return JSON.parse(raw);
|
|
1926
|
+
} catch {
|
|
1927
|
+
throw new ModelDiscoveryError("response");
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
function normalize(body) {
|
|
1931
|
+
if (!record(body) || !Array.isArray(body.data)) throw new ModelDiscoveryError("response");
|
|
1932
|
+
if (body.data.length > MAX_MODELS) throw new ModelDiscoveryError("response");
|
|
1933
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1934
|
+
const rows = [];
|
|
1935
|
+
for (const value of body.data) {
|
|
1936
|
+
if (!record(value) || typeof value.id !== "string" || !MODEL_ID2.test(value.id) || ["__proto__", "constructor", "prototype"].includes(value.id)) continue;
|
|
1937
|
+
if (MEDIA_MODEL_ID.test(value.id) || seen.has(value.id)) continue;
|
|
1938
|
+
seen.add(value.id);
|
|
1939
|
+
const known = findModel(value.id);
|
|
1940
|
+
if (known) {
|
|
1941
|
+
rows.push({ ...known });
|
|
1942
|
+
continue;
|
|
1943
|
+
}
|
|
1944
|
+
const offered = typeof value.name === "string" ? value.name.trim() : "";
|
|
1945
|
+
const name2 = offered !== "" && offered.length <= MAX_MODEL_NAME_LENGTH ? offered : value.id;
|
|
1946
|
+
rows.push({ id: value.id, name: name2, group: "paid" });
|
|
1947
|
+
}
|
|
1948
|
+
return rows;
|
|
1949
|
+
}
|
|
1950
|
+
async function fetchModelCatalog(openAiBaseURL2, key, signal, fetcher) {
|
|
1951
|
+
let base;
|
|
1952
|
+
try {
|
|
1953
|
+
base = openAiBaseURL(assertBaseURL(openAiBaseURL2));
|
|
1954
|
+
} catch {
|
|
1955
|
+
throw new ModelDiscoveryError("url");
|
|
1956
|
+
}
|
|
1957
|
+
let credential;
|
|
1958
|
+
try {
|
|
1959
|
+
credential = safeSecret(key);
|
|
1960
|
+
} catch {
|
|
1961
|
+
throw new ModelDiscoveryError("credential");
|
|
1962
|
+
}
|
|
1963
|
+
const request = fetcher ?? fetch;
|
|
1964
|
+
const deadline = combinedSignal(signal, DISCOVERY_TIMEOUT_MS);
|
|
1965
|
+
let response;
|
|
1966
|
+
try {
|
|
1967
|
+
response = await request(`${base}/models`, {
|
|
1968
|
+
method: "GET",
|
|
1969
|
+
headers: { ...attributionHeaders2(), authorization: `Bearer ${credential}`, accept: "application/json" },
|
|
1970
|
+
signal: deadline,
|
|
1971
|
+
redirect: "error"
|
|
1972
|
+
});
|
|
1973
|
+
} catch {
|
|
1974
|
+
if (signal?.aborted) throw new ModelDiscoveryError("aborted");
|
|
1975
|
+
if (deadline.aborted) throw new ModelDiscoveryError("timeout");
|
|
1976
|
+
throw new ModelDiscoveryError("network");
|
|
1977
|
+
}
|
|
1978
|
+
if (!response.ok) {
|
|
1979
|
+
await response.body?.cancel().catch(() => void 0);
|
|
1980
|
+
throw new ModelDiscoveryError(statusKind(response.status), response.status);
|
|
1981
|
+
}
|
|
1982
|
+
return normalize(await boundedJson(response, signal, deadline));
|
|
1983
|
+
}
|
|
1984
|
+
|
|
842
1985
|
// src/auth.ts
|
|
843
1986
|
import { readFile as fsReadFile } from "node:fs/promises";
|
|
844
1987
|
import { homedir as osHomedir } from "node:os";
|
|
@@ -1311,7 +2454,7 @@ async function defaultLaunch(signal) {
|
|
|
1311
2454
|
}
|
|
1312
2455
|
|
|
1313
2456
|
// src/quota.ts
|
|
1314
|
-
import { attributionHeaders as
|
|
2457
|
+
import { attributionHeaders as attributionHeaders3 } from "@deepseek-ai/dsh-llm";
|
|
1315
2458
|
var API_BASE = "https://amr-api.open-design.ai/api/v1";
|
|
1316
2459
|
var CURRENT_URL = `${API_BASE}/workspaces/current`;
|
|
1317
2460
|
var SUMMARY_URL = `${API_BASE}/billing/summary`;
|
|
@@ -1328,7 +2471,7 @@ var ConsoleError = class extends Error {
|
|
|
1328
2471
|
async function consoleJson(url, session, signal, fetcher = fetch) {
|
|
1329
2472
|
let response;
|
|
1330
2473
|
try {
|
|
1331
|
-
response = await fetcher(url, { headers: { ...
|
|
2474
|
+
response = await fetcher(url, { headers: { ...attributionHeaders3(), accept: "application/json", ...session.headers }, signal: combinedSignal(signal, 2e4), redirect: "error" });
|
|
1332
2475
|
} catch {
|
|
1333
2476
|
throw new ConsoleError("network");
|
|
1334
2477
|
}
|
|
@@ -1357,16 +2500,23 @@ async function consoleJson(url, session, signal, fetcher = fetch) {
|
|
|
1357
2500
|
await reader.cancel().catch(() => void 0);
|
|
1358
2501
|
reader.releaseLock();
|
|
1359
2502
|
}
|
|
2503
|
+
let body;
|
|
1360
2504
|
try {
|
|
1361
|
-
|
|
2505
|
+
body = JSON.parse(raw);
|
|
1362
2506
|
} catch {
|
|
1363
2507
|
throw new ConsoleError("response");
|
|
1364
2508
|
}
|
|
2509
|
+
if (record(body) && (body.code === 401 || body.code === "401")) throw new ConsoleError("auth", 401);
|
|
2510
|
+
if (record(body) && (body.code === 403 || body.code === "403")) throw new ConsoleError("permission", 403);
|
|
2511
|
+
return body;
|
|
1365
2512
|
}
|
|
1366
2513
|
function number(value, negative = false) {
|
|
1367
2514
|
const n = typeof value === "number" ? value : typeof value === "string" && /^-?\d+(\.\d+)?$/u.test(value.trim()) ? Number(value) : NaN;
|
|
1368
2515
|
return Number.isFinite(n) && (negative || n >= 0) ? n : void 0;
|
|
1369
2516
|
}
|
|
2517
|
+
function iso(value) {
|
|
2518
|
+
return typeof value === "string" && Number.isFinite(Date.parse(value)) ? new Date(value).toISOString() : void 0;
|
|
2519
|
+
}
|
|
1370
2520
|
function normalizeQuota(body, summary) {
|
|
1371
2521
|
if (!record(body) || !(typeof body.eligible === "boolean" || typeof body.tier === "string" || body.tier === null || Array.isArray(body.windows))) throw new ConsoleError("response");
|
|
1372
2522
|
const windows = [];
|
|
@@ -1374,9 +2524,19 @@ function normalizeQuota(body, summary) {
|
|
|
1374
2524
|
if (!record(row)) continue;
|
|
1375
2525
|
const seconds = number(row.durationSeconds), used = number(row.usedCredits), limit = number(row.limitCredits), remaining = number(row.remainingCredits);
|
|
1376
2526
|
if (seconds === void 0 || seconds <= 0 || used === void 0 || limit === void 0 || limit <= 0) continue;
|
|
1377
|
-
|
|
2527
|
+
const resetsAt = iso(row.resetsAt), windowStart = iso(row.windowStart);
|
|
2528
|
+
const resetMode = typeof row.resetMode === "string" && row.resetMode.trim() ? row.resetMode.trim() : void 0;
|
|
2529
|
+
windows.push({ durationSeconds: seconds, used, limit, percent: Math.min(100, used / limit * 100), ...remaining === void 0 ? {} : { remaining }, ...resetsAt === void 0 ? {} : { resetsAt }, ...windowStart === void 0 ? {} : { windowStart }, ...resetMode === void 0 ? {} : { resetMode } });
|
|
1378
2530
|
}
|
|
1379
|
-
|
|
2531
|
+
const membershipTier = record(summary) && typeof summary.membershipTier === "string" ? summary.membershipTier.trim() : "";
|
|
2532
|
+
const tier = typeof body.tier === "string" ? body.tier.trim() : body.tier === void 0 && body.eligible !== false ? membershipTier : "";
|
|
2533
|
+
const eligible = typeof body.eligible === "boolean" ? body.eligible : body.tier === null ? false : void 0;
|
|
2534
|
+
return { windows, balances: normalizeBalances(summary), ...normalizeSubscription(summary), ...tier ? { tier } : {}, ...eligible === void 0 ? {} : { eligible } };
|
|
2535
|
+
}
|
|
2536
|
+
function normalizeSubscription(summary) {
|
|
2537
|
+
if (!record(summary)) return {};
|
|
2538
|
+
const start = iso(summary.subscriptionCurrentPeriodStart), end = iso(summary.subscriptionCurrentPeriodEnd);
|
|
2539
|
+
return { ...start === void 0 ? {} : { subscriptionPeriodStart: start }, ...end === void 0 ? {} : { subscriptionPeriodEnd: end } };
|
|
1380
2540
|
}
|
|
1381
2541
|
function normalizeBalances(summary) {
|
|
1382
2542
|
if (!record(summary)) return {};
|
|
@@ -1404,12 +2564,14 @@ async function readQuota(session, signal, fetcher = fetch) {
|
|
|
1404
2564
|
} catch (error) {
|
|
1405
2565
|
const balances = normalizeBalances(summary);
|
|
1406
2566
|
if (!Object.keys(balances).length) throw error;
|
|
1407
|
-
return { ...common, state: "partial", windows: [], balances, note: "\u94B1\u5305\u5DF2\u8BFB\u53D6\uFF1B\u5957\u9910\u8D60\u9001\u989D\u5EA6\u6682\u4E0D\u53EF\u786E\u8BA4" };
|
|
2567
|
+
return { ...common, state: "partial", windows: [], balances, ...normalizeSubscription(summary), note: "\u94B1\u5305\u5DF2\u8BFB\u53D6\uFF1B\u5957\u9910\u8D60\u9001\u989D\u5EA6\u6682\u4E0D\u53EF\u786E\u8BA4" };
|
|
1408
2568
|
}
|
|
1409
|
-
const missingWindows = normalized.eligible
|
|
2569
|
+
const missingWindows = normalized.eligible !== false && !normalized.windows.length;
|
|
2570
|
+
const noSubscription = normalized.eligible === false && !normalized.windows.length;
|
|
1410
2571
|
const missingBalances = personal && Object.keys(normalized.balances).length < 3;
|
|
1411
2572
|
const partial = missingWindows || missingBalances || !personal;
|
|
1412
|
-
|
|
2573
|
+
const note = !personal ? "\u56E2\u961F\u6216\u672A\u77E5\u5DE5\u4F5C\u533A\uFF1A\u8D26\u53F7\u94B1\u5305\u53E3\u5F84\u672A\u6838\u5B9E\uFF0C\u6682\u4E0D\u663E\u793A" : missingWindows ? "\u5957\u9910\u989D\u5EA6\u7A97\u53E3\u6682\u4E0D\u53EF\u786E\u8BA4" : noSubscription ? "\u8BE5\u8D26\u53F7\u6CA1\u6709 Design Plan \u8BA2\u9605\uFF1B\u94B1\u5305\u4F59\u989D\u7167\u5E38\u663E\u793A" : missingBalances ? "\u90E8\u5206\u94B1\u5305\u4F59\u989D\u6682\u4E0D\u53EF\u786E\u8BA4" : void 0;
|
|
2574
|
+
return { ...common, ...normalized, state: partial ? "partial" : "ok", ...note === void 0 ? {} : { note } };
|
|
1413
2575
|
}
|
|
1414
2576
|
|
|
1415
2577
|
// src/desktop-quota.ts
|
|
@@ -1442,7 +2604,8 @@ async function readDesktopQuota(session, signal, runner = runVela, fetcher = fet
|
|
|
1442
2604
|
if (!record(workspace) || typeof workspace.workspaceId !== "string" || !/^[A-Za-z0-9_-]{1,64}$/u.test(workspace.workspaceId)) throw new ConsoleError("response");
|
|
1443
2605
|
const funding2 = await runner(["billing", "funding-check", "--workspace-id", workspace.workspaceId, "--format", "json"], deadline, session.profile);
|
|
1444
2606
|
if (!record(funding2) || !record(funding2.codingPlan)) throw new ConsoleError("response");
|
|
1445
|
-
|
|
2607
|
+
const generatedAt = typeof funding2.generatedAt === "string" && Number.isFinite(Date.parse(funding2.generatedAt)) ? new Date(funding2.generatedAt).toISOString() : void 0;
|
|
2608
|
+
return { ...normalizeQuota(funding2.codingPlan), ...typeof funding2.balanceUsd === "string" ? { balance: funding2.balanceUsd } : {}, ...generatedAt === void 0 ? {} : { generatedAt } };
|
|
1446
2609
|
})();
|
|
1447
2610
|
const [wallet, funding] = await Promise.allSettled([
|
|
1448
2611
|
consoleJson(`${session.apiUrl}/api/v1/wallet/balance`, session, deadline, fetcher),
|
|
@@ -1452,21 +2615,24 @@ async function readDesktopQuota(session, signal, runner = runVela, fetcher = fet
|
|
|
1452
2615
|
const cliBalances = funding.status === "fulfilled" && "balance" in funding.value ? normalizeBalances({ balanceUsd: funding.value.balance }) : {};
|
|
1453
2616
|
const windows = funding.status === "fulfilled" ? funding.value.windows : [];
|
|
1454
2617
|
const balances = { ...cliBalances, ...walletBalances };
|
|
1455
|
-
|
|
2618
|
+
const eligible = funding.status === "fulfilled" ? funding.value.eligible : void 0;
|
|
2619
|
+
const noSubscription = funding.status === "fulfilled" && eligible === false && windows.length === 0;
|
|
2620
|
+
if (!windows.length && !Object.keys(balances).length && !noSubscription) {
|
|
1456
2621
|
if (wallet.status === "rejected" && wallet.reason instanceof ConsoleError && (wallet.reason.kind === "auth" || wallet.reason.kind === "permission")) throw wallet.reason;
|
|
1457
2622
|
throw new ConsoleError("network");
|
|
1458
2623
|
}
|
|
1459
2624
|
const tier = (funding.status === "fulfilled" ? funding.value.tier : void 0) ?? session.plan;
|
|
1460
|
-
const
|
|
2625
|
+
const updatedAt = funding.status === "fulfilled" && funding.value.generatedAt || (/* @__PURE__ */ new Date()).toISOString();
|
|
2626
|
+
const note = noSubscription ? "\u684C\u9762\u767B\u5F55\u6001\u663E\u793A\u8BE5\u8D26\u53F7\u6CA1\u6709 Design Plan \u8BA2\u9605\uFF1B\u672A\u8BFB\u5230\u7684\u5206\u6C60\u4FDD\u6301\u672A\u77E5\u3002\u5982\u9700\u5B8C\u6574\u5206\u6C60\uFF0C\u8BF7\u5728\u8FDE\u63A5\u9875\u767B\u5F55\u5E76\u5173\u95ED\u201C\u4F18\u5148\u684C\u9762\u767B\u5F55\u6001\u201D\u3002" : !windows.length ? "\u684C\u9762\u767B\u5F55\u6001\u672A\u8FD4\u56DE\u5957\u9910\u7A97\u53E3\uFF1B\u5DF2\u8BFB\u5230\u7684\u94B1\u5305\u4F59\u989D\u7167\u5E38\u663E\u793A\uFF0C\u672A\u8BFB\u5230\u7684\u5206\u6C60\u4FDD\u6301\u672A\u77E5\u3002\u5982\u9700\u5B8C\u6574\u5206\u6C60\uFF0C\u8BF7\u5728\u8FDE\u63A5\u9875\u767B\u5F55\u5E76\u5173\u95ED\u201C\u4F18\u5148\u684C\u9762\u767B\u5F55\u6001\u201D\u3002" : "\u684C\u9762\u767B\u5F55\u6001\u90E8\u5206\u63A5\u53E3\u6743\u9650\u53D7\u9650\uFF1B\u672A\u8BFB\u5230\u7684\u5206\u6C60\u663E\u793A\u672A\u77E5\u3002\u5982\u9700\u5B8C\u6574\u5206\u6C60\uFF0C\u8BF7\u5728\u8FDE\u63A5\u9875\u767B\u5F55\u5E76\u5173\u95ED\u201C\u4F18\u5148\u684C\u9762\u767B\u5F55\u6001\u201D\u3002";
|
|
1461
2627
|
return {
|
|
1462
2628
|
state: "partial",
|
|
1463
2629
|
source: "desktop",
|
|
1464
|
-
updatedAt
|
|
2630
|
+
updatedAt,
|
|
1465
2631
|
windows,
|
|
1466
2632
|
balances,
|
|
1467
2633
|
...tier ? { tier } : {},
|
|
1468
2634
|
...eligible === void 0 ? {} : { eligible },
|
|
1469
|
-
note
|
|
2635
|
+
note
|
|
1470
2636
|
};
|
|
1471
2637
|
}
|
|
1472
2638
|
|
|
@@ -1478,12 +2644,12 @@ var MAX_ENV_REF = 128;
|
|
|
1478
2644
|
var MAX_TEXT = 4096;
|
|
1479
2645
|
var MAX_MESSAGE = 1024;
|
|
1480
2646
|
var MAX_TIER = 256;
|
|
1481
|
-
var
|
|
2647
|
+
var MAX_MODELS2 = 512;
|
|
1482
2648
|
var MAX_WINDOWS = 64;
|
|
1483
2649
|
var MAX_API_KEY = 16384;
|
|
1484
2650
|
var HTTP_ASCII = /^[\x21-\x7e]+$/u;
|
|
1485
2651
|
var ENV_REF = /^[A-Za-z_][A-Za-z0-9_]*$/u;
|
|
1486
|
-
var
|
|
2652
|
+
var MODEL_ID3 = /^[A-Za-z0-9._:/-]{1,128}$/u;
|
|
1487
2653
|
var LOGIN_PHASES = ["idle", "launching", "waiting", "verifying", "success", "cancelled", "error"];
|
|
1488
2654
|
var QUOTA_STATES = ["ok", "partial", "need-login", "error"];
|
|
1489
2655
|
var SESSION_SOURCES = ["desktop", "login", "environment"];
|
|
@@ -1497,7 +2663,7 @@ function isoTime(value, label) {
|
|
|
1497
2663
|
if (!Number.isFinite(Date.parse(time))) throw new Error(`${label} \u5FC5\u987B\u662F ISO \u65F6\u95F4\u5B57\u7B26\u4E32`);
|
|
1498
2664
|
return time;
|
|
1499
2665
|
}
|
|
1500
|
-
function
|
|
2666
|
+
function flag2(value, label) {
|
|
1501
2667
|
if (typeof value !== "boolean") throw new Error(`${label} \u5FC5\u987B\u662F\u5E03\u5C14\u503C`);
|
|
1502
2668
|
return value;
|
|
1503
2669
|
}
|
|
@@ -1509,6 +2675,15 @@ function amount(value, label, negative = false) {
|
|
|
1509
2675
|
if (typeof value !== "number" || !Number.isFinite(value) || !negative && value < 0) throw new Error(`${label} \u5FC5\u987B\u662F${negative ? "" : "\u975E\u8D1F"}\u6709\u9650\u6570\u5B57`);
|
|
1510
2676
|
return value;
|
|
1511
2677
|
}
|
|
2678
|
+
function positiveInteger(value, label, max) {
|
|
2679
|
+
const result = count(value, label);
|
|
2680
|
+
if (result === 0 || result > max) throw new Error(`${label} \u8D85\u51FA\u8303\u56F4`);
|
|
2681
|
+
return result;
|
|
2682
|
+
}
|
|
2683
|
+
function parseEfforts(value) {
|
|
2684
|
+
if (!Array.isArray(value) || value.length > 16) throw new Error("\u6A21\u578B\u601D\u8003\u6863\u4F4D\u683C\u5F0F\u65E0\u6548");
|
|
2685
|
+
return value.map((item) => text(item, "\u601D\u8003\u6863\u4F4D", 32));
|
|
2686
|
+
}
|
|
1512
2687
|
function member(value, allowed, label) {
|
|
1513
2688
|
const hit = allowed.find((candidate) => candidate === value);
|
|
1514
2689
|
if (hit === void 0) throw new Error(`${label} \u4E0D\u5728\u5141\u8BB8\u7684\u53D6\u503C\u5185`);
|
|
@@ -1526,7 +2701,9 @@ function parseQuotaWindow(value) {
|
|
|
1526
2701
|
limit,
|
|
1527
2702
|
...value.remaining === void 0 ? {} : { remaining: amount(value.remaining, "\u5269\u4F59\u989D\u5EA6") },
|
|
1528
2703
|
percent: Math.min(100, Math.max(0, amount(value.percent, "\u5DF2\u7528\u767E\u5206\u6BD4"))),
|
|
1529
|
-
...value.resetsAt === void 0 ? {} : { resetsAt: isoTime(value.resetsAt, "\u989D\u5EA6\u91CD\u7F6E\u65F6\u95F4") }
|
|
2704
|
+
...value.resetsAt === void 0 ? {} : { resetsAt: isoTime(value.resetsAt, "\u989D\u5EA6\u91CD\u7F6E\u65F6\u95F4") },
|
|
2705
|
+
...value.windowStart === void 0 ? {} : { windowStart: isoTime(value.windowStart, "\u989D\u5EA6\u7A97\u53E3\u5F00\u59CB\u65F6\u95F4") },
|
|
2706
|
+
...value.resetMode === void 0 ? {} : { resetMode: text(value.resetMode, "\u989D\u5EA6\u5237\u65B0\u65B9\u5F0F", 128) }
|
|
1530
2707
|
};
|
|
1531
2708
|
}
|
|
1532
2709
|
function parseQuotaSnapshot(value) {
|
|
@@ -1539,8 +2716,14 @@ function parseQuotaSnapshot(value) {
|
|
|
1539
2716
|
state: member(value.state, QUOTA_STATES, "\u989D\u5EA6\u72B6\u6001"),
|
|
1540
2717
|
...value.source === void 0 ? {} : { source: member(value.source, SESSION_SOURCES, "\u989D\u5EA6\u6765\u6E90") },
|
|
1541
2718
|
updatedAt: isoTime(value.updatedAt, "\u989D\u5EA6\u66F4\u65B0\u65F6\u95F4"),
|
|
2719
|
+
...value.subscriptionPeriodStart === void 0 ? {} : { subscriptionPeriodStart: isoTime(value.subscriptionPeriodStart, "\u8BA2\u9605\u5468\u671F\u5F00\u59CB\u65F6\u95F4") },
|
|
2720
|
+
...value.subscriptionPeriodEnd === void 0 ? {} : { subscriptionPeriodEnd: isoTime(value.subscriptionPeriodEnd, "\u8BA2\u9605\u5468\u671F\u7ED3\u675F\u65F6\u95F4") },
|
|
2721
|
+
...value.nextRefreshAt === void 0 ? {} : { nextRefreshAt: isoTime(value.nextRefreshAt, "\u4E0B\u6B21\u81EA\u52A8\u5237\u65B0\u65F6\u95F4") },
|
|
2722
|
+
...value.stale === void 0 ? {} : { stale: flag2(value.stale, "\u5386\u53F2\u989D\u5EA6\u8BFB\u6570") },
|
|
2723
|
+
...value.refreshFailures === void 0 ? {} : { refreshFailures: count(value.refreshFailures, "\u8FDE\u7EED\u5237\u65B0\u5931\u8D25\u6B21\u6570") },
|
|
2724
|
+
...value.autoRefreshPaused === void 0 ? {} : { autoRefreshPaused: flag2(value.autoRefreshPaused, "\u81EA\u52A8\u5237\u65B0\u6682\u505C\u72B6\u6001") },
|
|
1542
2725
|
...value.tier === void 0 ? {} : { tier: text(value.tier, "\u5957\u9910\u540D", MAX_TIER) },
|
|
1543
|
-
...value.eligible === void 0 ? {} : { eligible:
|
|
2726
|
+
...value.eligible === void 0 ? {} : { eligible: flag2(value.eligible, "\u5957\u9910\u8D44\u683C") },
|
|
1544
2727
|
windows: windows.map(parseQuotaWindow),
|
|
1545
2728
|
balances: {
|
|
1546
2729
|
...balances.subscriptionUsd === void 0 ? {} : { subscriptionUsd: amount(balances.subscriptionUsd, "\u8BA2\u9605\u4F59\u989D") },
|
|
@@ -1565,11 +2748,15 @@ function parseLoginState(value) {
|
|
|
1565
2748
|
function parseStatusModel(value) {
|
|
1566
2749
|
if (!record(value)) throw new Error("\u6A21\u578B\u6761\u76EE\u5FC5\u987B\u662F\u5BF9\u8C61");
|
|
1567
2750
|
const id = text(value.id, "\u6A21\u578B id", 128);
|
|
1568
|
-
if (!
|
|
2751
|
+
if (!MODEL_ID3.test(id)) throw new Error("\u6A21\u578B id \u542B\u4E0D\u5141\u8BB8\u7684\u5B57\u7B26");
|
|
1569
2752
|
return {
|
|
1570
2753
|
id,
|
|
1571
2754
|
name: text(value.name, "\u6A21\u578B\u540D", 128),
|
|
1572
|
-
group: member(value.group, MODEL_GROUPS, "\u6A21\u578B\u5206\u7EC4")
|
|
2755
|
+
group: member(value.group, MODEL_GROUPS, "\u6A21\u578B\u5206\u7EC4"),
|
|
2756
|
+
...value.enabled === void 0 ? {} : { enabled: flag2(value.enabled, "\u6A21\u578B\u542F\u7528\u72B6\u6001") },
|
|
2757
|
+
...value.contextWindow === void 0 ? {} : { contextWindow: positiveInteger(value.contextWindow, "\u4E0A\u4E0B\u6587\u9884\u7B97", 1e8) },
|
|
2758
|
+
...value.maxOutputTokens === void 0 ? {} : { maxOutputTokens: positiveInteger(value.maxOutputTokens, "\u6700\u5927\u8F93\u51FA", 1e7) },
|
|
2759
|
+
...value.reasoningEfforts === void 0 ? {} : { reasoningEfforts: parseEfforts(value.reasoningEfforts) }
|
|
1573
2760
|
};
|
|
1574
2761
|
}
|
|
1575
2762
|
function parseStatusConfig(value) {
|
|
@@ -1577,16 +2764,21 @@ function parseStatusConfig(value) {
|
|
|
1577
2764
|
const apiKeyEnv = text(value.apiKeyEnv, "\u51ED\u636E\u5F15\u7528\u540D", MAX_ENV_REF);
|
|
1578
2765
|
if (!ENV_REF.test(apiKeyEnv)) throw new Error("\u51ED\u636E\u5F15\u7528\u540D\u5FC5\u987B\u662F\u5408\u6CD5\u6807\u8BC6\u7B26");
|
|
1579
2766
|
return {
|
|
1580
|
-
emptyTurnGuard:
|
|
1581
|
-
useDesktopCredential:
|
|
2767
|
+
emptyTurnGuard: flag2(value.emptyTurnGuard, "\u7A7A\u8F6E\u4FDD\u62A4\u5F00\u5173"),
|
|
2768
|
+
useDesktopCredential: flag2(value.useDesktopCredential, "\u684C\u9762\u767B\u5F55\u6001\u5F00\u5173"),
|
|
1582
2769
|
baseURL: text(value.baseURL, "\u63A8\u7406\u5730\u5740", MAX_BASE_URL),
|
|
1583
|
-
apiKeyEnv
|
|
2770
|
+
apiKeyEnv,
|
|
2771
|
+
...value.apiProtocol === void 0 ? {} : { apiProtocol: member(value.apiProtocol, API_PROTOCOL_IDS, "\u63A8\u7406\u534F\u8BAE") },
|
|
2772
|
+
...value.modelSettings === void 0 ? {} : { modelSettings: parseModelSettingsMap(value.modelSettings) },
|
|
2773
|
+
...value.requestTimeoutMs === void 0 ? {} : { requestTimeoutMs: positiveInteger(value.requestTimeoutMs, "\u8BF7\u6C42\u8D85\u65F6", 36e5) },
|
|
2774
|
+
...value.streamIdleTimeoutMs === void 0 ? {} : { streamIdleTimeoutMs: positiveInteger(value.streamIdleTimeoutMs, "\u6D41\u7A7A\u95F2\u8D85\u65F6", 18e5) },
|
|
2775
|
+
...value.quotaRefreshSeconds === void 0 ? {} : { quotaRefreshSeconds: positiveInteger(value.quotaRefreshSeconds, "\u81EA\u52A8\u5237\u65B0\u95F4\u9694", 3600) }
|
|
1584
2776
|
};
|
|
1585
2777
|
}
|
|
1586
2778
|
function parseStatus(value) {
|
|
1587
2779
|
if (!record(value)) throw new Error("\u72B6\u6001\u5FC5\u987B\u662F\u5BF9\u8C61");
|
|
1588
2780
|
const models = value.models;
|
|
1589
|
-
if (!Array.isArray(models) || models.length >
|
|
2781
|
+
if (!Array.isArray(models) || models.length > MAX_MODELS2) throw new Error(`\u6A21\u578B\u5217\u8868\u5FC5\u987B\u662F\u6570\u7EC4\uFF08\u6700\u591A ${MAX_MODELS2} \u9879\uFF09`);
|
|
1590
2782
|
const ids = /* @__PURE__ */ new Set();
|
|
1591
2783
|
const parsed = models.map(parseStatusModel);
|
|
1592
2784
|
for (const model of parsed) {
|
|
@@ -1595,13 +2787,16 @@ function parseStatus(value) {
|
|
|
1595
2787
|
}
|
|
1596
2788
|
return {
|
|
1597
2789
|
revision: text(value.revision, "\u914D\u7F6E\u7248\u672C", MAX_REVISION),
|
|
1598
|
-
apiKeyConfigured:
|
|
2790
|
+
apiKeyConfigured: flag2(value.apiKeyConfigured, "\u51ED\u636E\u662F\u5426\u5C31\u7EEA"),
|
|
1599
2791
|
...value.sessionSource === void 0 ? {} : { sessionSource: member(value.sessionSource, SESSION_SOURCES, "\u767B\u5F55\u6001\u6765\u6E90") },
|
|
1600
2792
|
quota: parseQuotaSnapshot(value.quota),
|
|
1601
2793
|
login: parseLoginState(value.login),
|
|
1602
2794
|
config: parseStatusConfig(value.config),
|
|
1603
2795
|
guardedTurns: count(value.guardedTurns, "\u5DF2\u62E6\u622A\u7A7A\u8F6E\u6570"),
|
|
1604
|
-
models: parsed
|
|
2796
|
+
models: parsed,
|
|
2797
|
+
...value.modelsSource === void 0 ? {} : { modelsSource: member(value.modelsSource, ["builtin", "openai"], "\u6A21\u578B\u76EE\u5F55\u6765\u6E90") },
|
|
2798
|
+
...value.modelsUpdatedAt === void 0 ? {} : { modelsUpdatedAt: isoTime(value.modelsUpdatedAt, "\u6A21\u578B\u66F4\u65B0\u65F6\u95F4") },
|
|
2799
|
+
...value.modelsError === void 0 ? {} : { modelsError: text(value.modelsError, "\u6A21\u578B\u66F4\u65B0\u63D0\u793A", MAX_MESSAGE) }
|
|
1605
2800
|
};
|
|
1606
2801
|
}
|
|
1607
2802
|
function parseStartLoginRequest(value) {
|
|
@@ -1632,8 +2827,8 @@ function grantOf(value) {
|
|
|
1632
2827
|
if (value?.kind !== "grant" || !record(value.payload) || typeof value.payload.cookie !== "string") return;
|
|
1633
2828
|
return { cookie: value.payload.cookie, ...typeof value.payload.expiresAt === "number" ? { expiresAt: value.payload.expiresAt } : {} };
|
|
1634
2829
|
}
|
|
1635
|
-
var _saveApiKey_dec, _cancelLogin_dec, _loginStatus_dec, _startLogin_dec, _refreshQuota_dec, _status_dec, _a, _init;
|
|
1636
|
-
var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status_dec = [Remote("status")], _refreshQuota_dec = [Remote("refreshQuota")], _startLogin_dec = [Remote("startLogin")], _loginStatus_dec = [Remote("loginStatus")], _cancelLogin_dec = [Remote("cancelLogin")], _saveApiKey_dec = [Remote("saveApiKey")], _a) {
|
|
2830
|
+
var _saveApiKey_dec, _cancelLogin_dec, _loginStatus_dec, _startLogin_dec, _refreshQuotaNow_dec, _refreshQuota_dec, _refreshModels_dec, _status_dec, _a, _init;
|
|
2831
|
+
var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status_dec = [Remote("status")], _refreshModels_dec = [Remote("refreshModels")], _refreshQuota_dec = [Remote("refreshQuota")], _refreshQuotaNow_dec = [Remote("refreshQuotaNow")], _startLogin_dec = [Remote("startLogin")], _loginStatus_dec = [Remote("loginStatus")], _cancelLogin_dec = [Remote("cancelLogin")], _saveApiKey_dec = [Remote("saveApiKey")], _a) {
|
|
1637
2832
|
constructor(ctx, config, dependencies = { resolveSession, readQuota, readDesktopQuota }) {
|
|
1638
2833
|
super(ctx, "openDesignCoRouter");
|
|
1639
2834
|
this.dependencies = dependencies;
|
|
@@ -1646,10 +2841,18 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
|
|
|
1646
2841
|
__publicField(this, "_quota", needLogin());
|
|
1647
2842
|
__publicField(this, "_quotaAt", 0);
|
|
1648
2843
|
__publicField(this, "_quotaConfig", "");
|
|
2844
|
+
__publicField(this, "_quotaIdentity");
|
|
1649
2845
|
__publicField(this, "_quotaEpoch", 0);
|
|
1650
2846
|
__publicField(this, "_quotaPending");
|
|
1651
2847
|
__publicField(this, "_quotaController");
|
|
1652
2848
|
__publicField(this, "guardedTurns", 0);
|
|
2849
|
+
__publicField(this, "_models", MODELS);
|
|
2850
|
+
__publicField(this, "_modelsUpdatedAt");
|
|
2851
|
+
__publicField(this, "_modelsError");
|
|
2852
|
+
__publicField(this, "_modelsFingerprint", "");
|
|
2853
|
+
__publicField(this, "_modelsEpoch", 0);
|
|
2854
|
+
__publicField(this, "_modelsPending");
|
|
2855
|
+
__publicField(this, "_modelsController");
|
|
1653
2856
|
this._config = config;
|
|
1654
2857
|
this._login = new ConsoleLogin({
|
|
1655
2858
|
capture: async (expectedRevision) => {
|
|
@@ -1683,6 +2886,9 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
|
|
|
1683
2886
|
});
|
|
1684
2887
|
}
|
|
1685
2888
|
dependencies;
|
|
2889
|
+
modelCatalog() {
|
|
2890
|
+
return this._models;
|
|
2891
|
+
}
|
|
1686
2892
|
_credentials() {
|
|
1687
2893
|
const service = this.ctx.get("credentials");
|
|
1688
2894
|
if (!service) throw new RemoteError("opendesign/credentials-unavailable", "DSH \u51ED\u636E\u670D\u52A1\u4E0D\u53EF\u7528\uFF1B\u53EF\u5148\u4F7F\u7528\u542F\u52A8\u73AF\u5883\u53D8\u91CF", void 0);
|
|
@@ -1699,6 +2905,18 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
|
|
|
1699
2905
|
const credentials = this.ctx.get("credentials"), config = this._config();
|
|
1700
2906
|
const [apiRecord, sessionRecord, key] = await Promise.all([credentials?.readRecord(API_KEY), credentials?.readRecord(SESSION), this.resolveApiKey(config.apiKeyEnv)]);
|
|
1701
2907
|
const state = { apiRecord, sessionRecord, key, config };
|
|
2908
|
+
const modelsFingerprint = hash({ key, baseURL: config.baseURL });
|
|
2909
|
+
if (modelsFingerprint !== this._modelsFingerprint) {
|
|
2910
|
+
const changed = this._modelsUpdatedAt !== void 0;
|
|
2911
|
+
this._modelsFingerprint = modelsFingerprint;
|
|
2912
|
+
this._modelsEpoch++;
|
|
2913
|
+
this._modelsController?.abort();
|
|
2914
|
+
this._modelsPending = void 0;
|
|
2915
|
+
this._models = MODELS;
|
|
2916
|
+
this._modelsUpdatedAt = void 0;
|
|
2917
|
+
this._modelsError = void 0;
|
|
2918
|
+
if (changed) this.dependencies.modelsChanged?.();
|
|
2919
|
+
}
|
|
1702
2920
|
const fingerprint = hash(state);
|
|
1703
2921
|
if (fingerprint !== this._fingerprint) {
|
|
1704
2922
|
this._fingerprint = fingerprint;
|
|
@@ -1716,12 +2934,13 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
|
|
|
1716
2934
|
this._quotaAt = 0;
|
|
1717
2935
|
this._quotaEpoch++;
|
|
1718
2936
|
this._quota = needLogin();
|
|
2937
|
+
this._quotaIdentity = void 0;
|
|
1719
2938
|
this._quotaController?.abort();
|
|
1720
2939
|
this._quotaPending = void 0;
|
|
1721
2940
|
}
|
|
1722
2941
|
async status() {
|
|
1723
2942
|
const state = await this._state();
|
|
1724
|
-
const quotaConfig = hash({ desktop: state.config.useDesktopCredential, session: state.sessionRecord });
|
|
2943
|
+
const quotaConfig = hash({ desktop: state.config.useDesktopCredential, session: state.sessionRecord, refreshSeconds: state.config.quotaRefreshSeconds });
|
|
1725
2944
|
if (this._quotaConfig !== quotaConfig) {
|
|
1726
2945
|
this._quotaConfig = quotaConfig;
|
|
1727
2946
|
this._invalidateQuota();
|
|
@@ -1734,24 +2953,78 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
|
|
|
1734
2953
|
login: this._login.status(),
|
|
1735
2954
|
config: state.config,
|
|
1736
2955
|
guardedTurns: this.guardedTurns,
|
|
1737
|
-
models:
|
|
2956
|
+
models: this._models.map(({ id, name: name2, group: group2, contextWindow, maxOutputTokens, reasoningEfforts }) => ({
|
|
2957
|
+
id,
|
|
2958
|
+
name: name2,
|
|
2959
|
+
group: group2,
|
|
2960
|
+
enabled: isModelEnabled(id, state.config.modelSettings),
|
|
2961
|
+
...contextWindow === void 0 ? {} : { contextWindow },
|
|
2962
|
+
...maxOutputTokens === void 0 ? {} : { maxOutputTokens },
|
|
2963
|
+
...reasoningEfforts === void 0 ? {} : { reasoningEfforts }
|
|
2964
|
+
})),
|
|
2965
|
+
modelsSource: this._modelsUpdatedAt ? "openai" : "builtin",
|
|
2966
|
+
...this._modelsUpdatedAt ? { modelsUpdatedAt: this._modelsUpdatedAt } : {},
|
|
2967
|
+
...this._modelsError ? { modelsError: this._modelsError } : {}
|
|
1738
2968
|
});
|
|
1739
2969
|
}
|
|
2970
|
+
async refreshModels() {
|
|
2971
|
+
const state = await this._state();
|
|
2972
|
+
if (this._modelsPending) return this._modelsPending;
|
|
2973
|
+
const key = state.key;
|
|
2974
|
+
if (!key?.trim()) {
|
|
2975
|
+
this._modelsError = "\u8BF7\u5148\u914D\u7F6E API Key\uFF0C\u518D\u4ECE OpenAI \u7AEF\u53E3\u62C9\u53D6\u6A21\u578B\u3002";
|
|
2976
|
+
return this.status();
|
|
2977
|
+
}
|
|
2978
|
+
const epoch = this._modelsEpoch;
|
|
2979
|
+
const controller = new AbortController();
|
|
2980
|
+
this._modelsController = controller;
|
|
2981
|
+
const signal = AbortSignal.any([controller.signal, this._lifetime.signal]);
|
|
2982
|
+
const run = (async () => {
|
|
2983
|
+
try {
|
|
2984
|
+
const models = await (this.dependencies.fetchModels ?? fetchModelCatalog)(state.config.baseURL, key, signal);
|
|
2985
|
+
await this._state();
|
|
2986
|
+
if (signal.aborted || epoch !== this._modelsEpoch) return this.status();
|
|
2987
|
+
this._models = models;
|
|
2988
|
+
this._modelsUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2989
|
+
this._modelsError = void 0;
|
|
2990
|
+
this.dependencies.modelsChanged?.();
|
|
2991
|
+
} catch (error) {
|
|
2992
|
+
if (!signal.aborted && epoch === this._modelsEpoch) this._modelsError = error instanceof ModelDiscoveryError ? error.message : "\u6A21\u578B\u5217\u8868\u66F4\u65B0\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002";
|
|
2993
|
+
}
|
|
2994
|
+
return this.status();
|
|
2995
|
+
})();
|
|
2996
|
+
this._modelsPending = run;
|
|
2997
|
+
try {
|
|
2998
|
+
return await run;
|
|
2999
|
+
} finally {
|
|
3000
|
+
if (this._modelsPending === run) this._modelsPending = void 0;
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
1740
3003
|
async refreshQuota() {
|
|
3004
|
+
return this._refreshQuota(false);
|
|
3005
|
+
}
|
|
3006
|
+
async refreshQuotaNow() {
|
|
3007
|
+
return this._refreshQuota(true);
|
|
3008
|
+
}
|
|
3009
|
+
async _refreshQuota(force) {
|
|
1741
3010
|
const state = await this._state();
|
|
1742
|
-
const fingerprint = hash({ desktop: state.config.useDesktopCredential, session: state.sessionRecord });
|
|
3011
|
+
const fingerprint = hash({ desktop: state.config.useDesktopCredential, session: state.sessionRecord, refreshSeconds: state.config.quotaRefreshSeconds });
|
|
1743
3012
|
if (fingerprint !== this._quotaConfig) {
|
|
1744
3013
|
this._quotaConfig = fingerprint;
|
|
1745
3014
|
this._invalidateQuota();
|
|
1746
3015
|
}
|
|
1747
3016
|
if (this._quotaPending) return this._quotaPending;
|
|
1748
|
-
|
|
1749
|
-
|
|
3017
|
+
if (!force) {
|
|
3018
|
+
if (this._quota.autoRefreshPaused) return this._quota;
|
|
3019
|
+
const next = this._quota.nextRefreshAt ? Date.parse(this._quota.nextRefreshAt) : this._quotaAt + 5e3;
|
|
3020
|
+
if (Date.now() < next) return this._quota;
|
|
3021
|
+
}
|
|
1750
3022
|
const epoch = this._quotaEpoch;
|
|
1751
3023
|
const controller = new AbortController();
|
|
1752
3024
|
this._quotaController = controller;
|
|
1753
3025
|
const signal = AbortSignal.any([controller.signal, this._lifetime.signal]);
|
|
1754
|
-
const
|
|
3026
|
+
const failures = force ? 0 : this._quota.refreshFailures ?? 0;
|
|
3027
|
+
const run = this._readQuota(epoch, signal, failures);
|
|
1755
3028
|
this._quotaPending = run;
|
|
1756
3029
|
void run.finally(() => {
|
|
1757
3030
|
if (this._quotaPending === run) this._quotaPending = void 0;
|
|
@@ -1759,9 +3032,11 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
|
|
|
1759
3032
|
});
|
|
1760
3033
|
return run;
|
|
1761
3034
|
}
|
|
1762
|
-
async _readQuota(epoch, signal) {
|
|
3035
|
+
async _readQuota(epoch, signal, failures) {
|
|
1763
3036
|
const config = this._config();
|
|
1764
3037
|
let result;
|
|
3038
|
+
let identity;
|
|
3039
|
+
let refreshFailed = false;
|
|
1765
3040
|
try {
|
|
1766
3041
|
const authDeps = {
|
|
1767
3042
|
useDesktop: config.useDesktopCredential,
|
|
@@ -1772,11 +3047,23 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
|
|
|
1772
3047
|
}
|
|
1773
3048
|
};
|
|
1774
3049
|
const auth = await this.dependencies.resolveSession(authDeps);
|
|
3050
|
+
if (auth.state === "ready") identity = hash(auth.session);
|
|
1775
3051
|
if (auth.state === "need-login") result = needLogin();
|
|
1776
3052
|
else if (auth.session.source !== "desktop") result = await this.dependencies.readQuota(auth.session, signal);
|
|
1777
3053
|
else {
|
|
1778
3054
|
try {
|
|
1779
3055
|
result = await this.dependencies.readDesktopQuota(auth.session, signal);
|
|
3056
|
+
if (result.state === "partial") {
|
|
3057
|
+
const fallback = await this.dependencies.resolveSession({ ...authDeps, useDesktop: false });
|
|
3058
|
+
if (fallback.state === "ready") {
|
|
3059
|
+
try {
|
|
3060
|
+
const complete = await this.dependencies.readQuota(fallback.session, signal);
|
|
3061
|
+
if (complete.state === "ok" && (complete.windows.length > 0 || complete.eligible === false)) result = { ...complete, note: `\u684C\u9762\u767B\u5F55\u6001\u4EC5\u63D0\u4F9B\u90E8\u5206\u989D\u5EA6\uFF1B\u5F53\u524D\u663E\u793A\u72EC\u7ACB\u767B\u5F55\u8D26\u53F7\u7684\u5B8C\u6574\u989D\u5EA6\u3002${complete.note ? ` ${complete.note}` : ""}` };
|
|
3062
|
+
} catch (error) {
|
|
3063
|
+
if (signal.aborted) throw error;
|
|
3064
|
+
}
|
|
3065
|
+
}
|
|
3066
|
+
}
|
|
1780
3067
|
} catch (error) {
|
|
1781
3068
|
if (!(error instanceof ConsoleError) || !["auth", "permission"].includes(error.kind)) throw error;
|
|
1782
3069
|
const fallback = await this.dependencies.resolveSession({ ...authDeps, useDesktop: false });
|
|
@@ -1785,11 +3072,32 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
|
|
|
1785
3072
|
}
|
|
1786
3073
|
}
|
|
1787
3074
|
} catch (error) {
|
|
1788
|
-
|
|
3075
|
+
const authFailure = error instanceof ConsoleError && error.kind === "auth";
|
|
3076
|
+
refreshFailed = !authFailure;
|
|
3077
|
+
const hasReadings = this._quota.windows.length > 0 || Object.keys(this._quota.balances).length > 0;
|
|
3078
|
+
if (!authFailure && identity !== void 0 && identity === this._quotaIdentity && hasReadings) {
|
|
3079
|
+
result = { ...this._quota, state: "partial", stale: true, note: "\u989D\u5EA6\u5237\u65B0\u5931\u8D25\uFF0C\u5F53\u524D\u663E\u793A\u4E0A\u6B21\u6210\u529F\u8BFB\u6570\u3002" };
|
|
3080
|
+
} else {
|
|
3081
|
+
result = { ...needLogin(), state: authFailure ? "need-login" : "error", note: error instanceof ConsoleError ? error.message : "\u989D\u5EA6\u8BFB\u53D6\u6682\u65F6\u5931\u8D25\uFF1B\u8BF7\u68C0\u67E5\u8FDE\u63A5\u6216\u91CD\u65B0\u767B\u5F55" };
|
|
3082
|
+
}
|
|
1789
3083
|
}
|
|
1790
3084
|
if (!signal.aborted && epoch === this._quotaEpoch) {
|
|
1791
|
-
this._quota = result;
|
|
1792
3085
|
this._quotaAt = Date.now();
|
|
3086
|
+
if (refreshFailed) {
|
|
3087
|
+
const count2 = failures + 1;
|
|
3088
|
+
const paused = count2 >= 6;
|
|
3089
|
+
const { nextRefreshAt: _previousPlan, ...previous } = result;
|
|
3090
|
+
result = {
|
|
3091
|
+
...previous,
|
|
3092
|
+
refreshFailures: count2,
|
|
3093
|
+
autoRefreshPaused: paused,
|
|
3094
|
+
...paused ? { note: `${result.note ?? "\u989D\u5EA6\u5237\u65B0\u5931\u8D25"} \u81EA\u52A8\u91CD\u8BD5\u5DF2\u6682\u505C\uFF0C\u8BF7\u70B9\u51FB\u5237\u65B0\u989D\u5EA6\u91CD\u8BD5\u3002` } : { nextRefreshAt: new Date(this._quotaAt + Math.min(6e4, 5e3 * 2 ** (count2 - 1))).toISOString() }
|
|
3095
|
+
};
|
|
3096
|
+
} else if (result.state !== "need-login") {
|
|
3097
|
+
result = { ...result, nextRefreshAt: new Date(this._quotaAt + (config.quotaRefreshSeconds ?? 60) * 1e3).toISOString() };
|
|
3098
|
+
}
|
|
3099
|
+
this._quota = result;
|
|
3100
|
+
this._quotaIdentity = identity;
|
|
1793
3101
|
}
|
|
1794
3102
|
return epoch === this._quotaEpoch ? result : this._quota;
|
|
1795
3103
|
}
|
|
@@ -1819,7 +3127,9 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
|
|
|
1819
3127
|
};
|
|
1820
3128
|
_init = __decoratorStart(_a);
|
|
1821
3129
|
__decorateElement(_init, 1, "status", _status_dec, OpenDesignCoRouterService);
|
|
3130
|
+
__decorateElement(_init, 1, "refreshModels", _refreshModels_dec, OpenDesignCoRouterService);
|
|
1822
3131
|
__decorateElement(_init, 1, "refreshQuota", _refreshQuota_dec, OpenDesignCoRouterService);
|
|
3132
|
+
__decorateElement(_init, 1, "refreshQuotaNow", _refreshQuotaNow_dec, OpenDesignCoRouterService);
|
|
1823
3133
|
__decorateElement(_init, 1, "startLogin", _startLogin_dec, OpenDesignCoRouterService);
|
|
1824
3134
|
__decorateElement(_init, 1, "loginStatus", _loginStatus_dec, OpenDesignCoRouterService);
|
|
1825
3135
|
__decorateElement(_init, 1, "cancelLogin", _cancelLogin_dec, OpenDesignCoRouterService);
|
|
@@ -1829,9 +3139,12 @@ function apply(ctx, config) {
|
|
|
1829
3139
|
const current = () => readConfig(config);
|
|
1830
3140
|
current();
|
|
1831
3141
|
ctx.inject(["settings"], (scope) => scope.effect(() => scope.settings.configure({ auto: false }, ctx.fiber)));
|
|
1832
|
-
|
|
3142
|
+
let modelsChanged = () => {
|
|
3143
|
+
};
|
|
3144
|
+
const service = new OpenDesignCoRouterService(ctx, current, { resolveSession, readQuota, readDesktopQuota, modelsChanged: () => modelsChanged() });
|
|
1833
3145
|
const adapter = new CoRouterAdapter({
|
|
1834
3146
|
config: current,
|
|
3147
|
+
models: () => service.modelCatalog(),
|
|
1835
3148
|
apiKey: (ref) => service.resolveApiKey(ref),
|
|
1836
3149
|
onEmptyTurn: () => {
|
|
1837
3150
|
service.guardedTurns++;
|
|
@@ -1843,7 +3156,23 @@ function apply(ctx, config) {
|
|
|
1843
3156
|
return `data:${image.ref.mediaType};base64,${Buffer.from(image.data).toString("base64")}`;
|
|
1844
3157
|
}
|
|
1845
3158
|
});
|
|
1846
|
-
ctx.llm.registerAdapter([PLAN_PROVIDER, PAID_PROVIDER], adapter);
|
|
3159
|
+
const registration = ctx.llm.registerAdapter([PLAN_PROVIDER, PAID_PROVIDER], adapter);
|
|
3160
|
+
modelsChanged = () => registration.replace([PLAN_PROVIDER, PAID_PROVIDER]);
|
|
3161
|
+
let configFingerprint = hash(current());
|
|
3162
|
+
ctx.on("loader/volatile-update", () => {
|
|
3163
|
+
const next = hash(current());
|
|
3164
|
+
if (next === configFingerprint) return;
|
|
3165
|
+
configFingerprint = next;
|
|
3166
|
+
modelsChanged();
|
|
3167
|
+
});
|
|
3168
|
+
ctx.llm.registerModelDiscovery(SETTINGS_NS, async (request, signal) => {
|
|
3169
|
+
if (request.provider !== void 0 && ![PLAN_PROVIDER, PAID_PROVIDER].includes(request.provider)) throw new LlmError11("\u4E0D\u662F\u672C\u63D2\u4EF6\u7684\u6A21\u578B\u901A\u9053", "INVALID_REQUEST");
|
|
3170
|
+
const config2 = current();
|
|
3171
|
+
const key = request.apiKey ?? await service.resolveApiKey(config2.apiKeyEnv);
|
|
3172
|
+
if (!key) throw new LlmError11("\u8BF7\u5148\u914D\u7F6E CoRouter API Key", "MISSING_CREDENTIAL");
|
|
3173
|
+
const models = await fetchModelCatalog(config2.baseURL, key, signal);
|
|
3174
|
+
return models.map((row) => ({ id: row.id, name: row.name, ...row.contextWindow === void 0 ? {} : { contextWindow: row.contextWindow }, ...row.maxOutputTokens === void 0 ? {} : { maxTokens: row.maxOutputTokens }, ...row.inputModalities ? { inputModalities: row.inputModalities } : {} }));
|
|
3175
|
+
});
|
|
1847
3176
|
ctx.llm.registerConfigurableProviders([
|
|
1848
3177
|
{ provider: PLAN_PROVIDER, displayName: "OpenDesign CoRouter \xB7 \u5957\u9910\u8D60\u9001", settingsNs: SETTINGS_NS, settingsPath: [], defaultBaseURL: current().baseURL },
|
|
1849
3178
|
{ provider: PAID_PROVIDER, displayName: "OpenDesign CoRouter \xB7 \u4ED8\u8D39\u6A21\u578B", settingsNs: SETTINGS_NS, settingsPath: [], defaultBaseURL: current().baseURL }
|