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