dsh-multi-router 0.6.12 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -430
- package/package.json +5 -81
- package/CHANGELOG.md +0 -243
- package/LICENSE +0 -21
- package/THIRD_PARTY_NOTICES.md +0 -171
- package/cordis.patch.yml +0 -3
- package/lib/accounts.js +0 -19
- package/lib/accounts.js.map +0 -7
- package/lib/api-service.js +0 -11
- package/lib/api-service.js.map +0 -7
- package/lib/chunk-2UM45OXY.js +0 -181
- package/lib/chunk-2UM45OXY.js.map +0 -7
- package/lib/chunk-4L2CBGXN.js +0 -674
- package/lib/chunk-4L2CBGXN.js.map +0 -7
- package/lib/chunk-4V52V5GO.js +0 -105
- package/lib/chunk-4V52V5GO.js.map +0 -7
- package/lib/chunk-DNQPZMPF.js +0 -33
- package/lib/chunk-DNQPZMPF.js.map +0 -7
- package/lib/chunk-FLL3DUVP.js +0 -2017
- package/lib/chunk-FLL3DUVP.js.map +0 -7
- package/lib/chunk-GEBZMIMA.js +0 -287
- package/lib/chunk-GEBZMIMA.js.map +0 -7
- package/lib/chunk-KWXLQJS5.js +0 -1102
- package/lib/chunk-KWXLQJS5.js.map +0 -7
- package/lib/chunk-NZF36O5I.js +0 -5421
- package/lib/chunk-NZF36O5I.js.map +0 -7
- package/lib/chunk-S5KMFAQM.js +0 -50
- package/lib/chunk-S5KMFAQM.js.map +0 -7
- package/lib/chunk-TM5XDMGY.js +0 -332
- package/lib/chunk-TM5XDMGY.js.map +0 -7
- package/lib/chunk-V37VG7MN.js +0 -2101
- package/lib/chunk-V37VG7MN.js.map +0 -7
- package/lib/chunk-WDIT4E2W.js +0 -120
- package/lib/chunk-WDIT4E2W.js.map +0 -7
- package/lib/cli.js +0 -103
- package/lib/cli.js.map +0 -7
- package/lib/client.js +0 -2805
- package/lib/client.js.map +0 -7
- package/lib/clients.js +0 -30
- package/lib/clients.js.map +0 -7
- package/lib/host-v0.6.12.js +0 -414
- package/lib/host.js +0 -414
- package/lib/index.js +0 -414
- package/lib/index.js.map +0 -7
- package/lib/manager.js +0 -27
- package/lib/manager.js.map +0 -7
- package/lib/region-DO3KAV7V.js +0 -15
- package/lib/region-DO3KAV7V.js.map +0 -7
- package/lib/router.js +0 -14
- package/lib/router.js.map +0 -7
- package/lib/shim.js +0 -10
- package/lib/shim.js.map +0 -7
- package/lib/upstream-VP6J7LU2.js +0 -25
- package/lib/upstream-VP6J7LU2.js.map +0 -7
- package/router.example.json +0 -289
- package/upstream-lock.json +0 -17
package/lib/chunk-S5KMFAQM.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
// upstream/trae/src/region.ts
|
|
2
|
-
var REGION_GATEWAYS = {
|
|
3
|
-
cn: {
|
|
4
|
-
chat: "https://trae-api-cn.mchost.guru",
|
|
5
|
-
remote: "https://solo.trae.cn/api/remote/v1",
|
|
6
|
-
pay: "https://api.trae.cn"
|
|
7
|
-
},
|
|
8
|
-
ai: {
|
|
9
|
-
chat: "https://coresg-normal.trae.ai",
|
|
10
|
-
remote: "https://coresg-normal.trae.ai/api/remote/v1",
|
|
11
|
-
pay: "https://growsg-normal.trae.ai"
|
|
12
|
-
}
|
|
13
|
-
};
|
|
14
|
-
function regionOfEdition(edition) {
|
|
15
|
-
return edition === "sg" || edition === "solo-sg" ? "ai" : "cn";
|
|
16
|
-
}
|
|
17
|
-
function regionOfUserRegion(value) {
|
|
18
|
-
const raw = typeof value === "object" && value !== null && !Array.isArray(value) ? value["region"] : value;
|
|
19
|
-
if (typeof raw !== "string") return void 0;
|
|
20
|
-
const lowered = raw.trim().toLowerCase();
|
|
21
|
-
if (lowered === "cn") return "cn";
|
|
22
|
-
if (lowered === "sg" || lowered === "ai") return "ai";
|
|
23
|
-
return void 0;
|
|
24
|
-
}
|
|
25
|
-
function regionOfHost(host) {
|
|
26
|
-
if (host === void 0) return void 0;
|
|
27
|
-
const trimmed = host.trim();
|
|
28
|
-
if (trimmed === "") return void 0;
|
|
29
|
-
let hostname;
|
|
30
|
-
try {
|
|
31
|
-
hostname = new URL(trimmed.startsWith("http") ? trimmed : `https://${trimmed}`).hostname;
|
|
32
|
-
} catch {
|
|
33
|
-
return void 0;
|
|
34
|
-
}
|
|
35
|
-
if (hostname === "trae.ai" || hostname.endsWith(".trae.ai")) return "ai";
|
|
36
|
-
if (hostname === "trae.cn" || hostname.endsWith(".trae.cn") || hostname.endsWith(".trae.com.cn")) return "cn";
|
|
37
|
-
return void 0;
|
|
38
|
-
}
|
|
39
|
-
function regionOfCredential(credential) {
|
|
40
|
-
return regionOfUserRegion(credential.userRegion) ?? regionOfHost(credential.host) ?? regionOfEdition(credential.edition);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export {
|
|
44
|
-
REGION_GATEWAYS,
|
|
45
|
-
regionOfEdition,
|
|
46
|
-
regionOfUserRegion,
|
|
47
|
-
regionOfHost,
|
|
48
|
-
regionOfCredential
|
|
49
|
-
};
|
|
50
|
-
//# sourceMappingURL=chunk-S5KMFAQM.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../upstream/trae/src/region.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Region model shared by every layer: two storage buckets (`cn` | `ai`) keyed\n * by the credential's own claim, plus the per-region gateway constants.\n *\n * Evidence: docs/INTL_SG_EVIDENCE.md (2026-09-15). The desktop installs of\n * both regions expose the same wire shapes; only the gateways, rosters, and\n * the refresh contract differ. `userRegion.region` ('CN' | 'SG', observed in\n * both cases; the desktop logs also spell it lowercase 'sg') is the\n * authoritative claim, the credential host suffix is the fallback, and the\n * edition label is the last resort.\n *\n * @module dsh-connect-trae/region\n */\n\nimport type { TraeEdition } from './paths.ts'\n\n/** Storage/routing bucket: the CN service or the international (ai) service. */\nexport type TraeRegion = 'cn' | 'ai'\n\n/** One region's upstream gateway bases. */\nexport interface TraeRegionGateways {\n /** Chat + agent API base (the `llm_utils_chat` / raw-chat family). */\n readonly chat: string\n /** SOLO remote model directory base (`/api/remote/v1`). */\n readonly remote: string\n /** Pay/status API base; the credential's own host is preferred when known. */\n readonly pay: string\n}\n\n/**\n * Verified gateway bases per region (docs/INTL_SG_EVIDENCE.md \u00A72).\n * The AI gateway is shared by both international installs (Trae desktop and\n * TRAE SOLO); its mchost shards (`api16/api22-normal-alisg.mchost.guru`)\n * answer the same bytes but stay internal \u2014 `coresg-normal.trae.ai` is the\n * single stable entry point.\n */\nexport const REGION_GATEWAYS: Readonly<Record<TraeRegion, TraeRegionGateways>> = {\n cn: {\n chat: 'https://trae-api-cn.mchost.guru',\n remote: 'https://solo.trae.cn/api/remote/v1',\n pay: 'https://api.trae.cn',\n },\n ai: {\n chat: 'https://coresg-normal.trae.ai',\n remote: 'https://coresg-normal.trae.ai/api/remote/v1',\n pay: 'https://growsg-normal.trae.ai',\n },\n}\n\n/** Region for an edition label: the international installs belong to `ai`. */\nexport function regionOfEdition(edition: TraeEdition): TraeRegion {\n return edition === 'sg' || edition === 'solo-sg' ? 'ai' : 'cn'\n}\n\n/**\n * Region from the credential's `userRegion` claim. The desktop storage spells\n * it as an object (`{\"region\":\"CN\",\"_aiRegion\":\"CN\"}`); the app logs also\n * spell the bare value lowercase (`\"sg\"`). Both are accepted, case-blind.\n */\nexport function regionOfUserRegion(value: unknown): TraeRegion | undefined {\n const raw = typeof value === 'object' && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)['region']\n : value\n if (typeof raw !== 'string') return undefined\n const lowered = raw.trim().toLowerCase()\n if (lowered === 'cn') return 'cn'\n if (lowered === 'sg' || lowered === 'ai') return 'ai'\n return undefined\n}\n\n/** Region from a credential host (`.trae.ai` \u2192 ai, `.trae.cn`/`.trae.com.cn` \u2192 cn). */\nexport function regionOfHost(host: string | undefined): TraeRegion | undefined {\n if (host === undefined) return undefined\n const trimmed = host.trim()\n if (trimmed === '') return undefined\n let hostname: string\n try {\n hostname = new URL(trimmed.startsWith('http') ? trimmed : `https://${trimmed}`).hostname\n } catch {\n return undefined\n }\n if (hostname === 'trae.ai' || hostname.endsWith('.trae.ai')) return 'ai'\n if (hostname === 'trae.cn' || hostname.endsWith('.trae.cn') || hostname.endsWith('.trae.com.cn')) return 'cn'\n return undefined\n}\n\n/**\n * Region of a credential: the `userRegion` claim wins, the host suffix is the\n * fallback, and the edition label is the last resort. Every level is derived\n * from data the credential itself carries, so no user configuration is needed.\n */\nexport function regionOfCredential(credential: {\n edition: TraeEdition\n host?: string\n userRegion?: string\n}): TraeRegion {\n return regionOfUserRegion(credential.userRegion)\n ?? regionOfHost(credential.host)\n ?? regionOfEdition(credential.edition)\n}\n"],
|
|
5
|
-
"mappings": ";AAoCO,IAAM,kBAAoE;AAAA,EAC/E,IAAI;AAAA,IACF,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,KAAK;AAAA,EACP;AAAA,EACA,IAAI;AAAA,IACF,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,KAAK;AAAA,EACP;AACF;AAGO,SAAS,gBAAgB,SAAkC;AAChE,SAAO,YAAY,QAAQ,YAAY,YAAY,OAAO;AAC5D;AAOO,SAAS,mBAAmB,OAAwC;AACzE,QAAM,MAAM,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK,IAC1E,MAAkC,QAAQ,IAC3C;AACJ,MAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,QAAM,UAAU,IAAI,KAAK,EAAE,YAAY;AACvC,MAAI,YAAY,KAAM,QAAO;AAC7B,MAAI,YAAY,QAAQ,YAAY,KAAM,QAAO;AACjD,SAAO;AACT;AAGO,SAAS,aAAa,MAAkD;AAC7E,MAAI,SAAS,OAAW,QAAO;AAC/B,QAAM,UAAU,KAAK,KAAK;AAC1B,MAAI,YAAY,GAAI,QAAO;AAC3B,MAAI;AACJ,MAAI;AACF,eAAW,IAAI,IAAI,QAAQ,WAAW,MAAM,IAAI,UAAU,WAAW,OAAO,EAAE,EAAE;AAAA,EAClF,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,aAAa,aAAa,SAAS,SAAS,UAAU,EAAG,QAAO;AACpE,MAAI,aAAa,aAAa,SAAS,SAAS,UAAU,KAAK,SAAS,SAAS,cAAc,EAAG,QAAO;AACzG,SAAO;AACT;AAOO,SAAS,mBAAmB,YAIpB;AACb,SAAO,mBAAmB,WAAW,UAAU,KAC1C,aAAa,WAAW,IAAI,KAC5B,gBAAgB,WAAW,OAAO;AACzC;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
package/lib/chunk-TM5XDMGY.js
DELETED
|
@@ -1,332 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
SseDecoder
|
|
3
|
-
} from "./chunk-4V52V5GO.js";
|
|
4
|
-
import {
|
|
5
|
-
hostIsLoopback
|
|
6
|
-
} from "./chunk-DNQPZMPF.js";
|
|
7
|
-
import {
|
|
8
|
-
RouteError,
|
|
9
|
-
abortable
|
|
10
|
-
} from "./chunk-4L2CBGXN.js";
|
|
11
|
-
import {
|
|
12
|
-
REASONING_EFFORTS,
|
|
13
|
-
beijingDay,
|
|
14
|
-
writePrivateJson
|
|
15
|
-
} from "./chunk-GEBZMIMA.js";
|
|
16
|
-
|
|
17
|
-
// src/api-service.mjs
|
|
18
|
-
import { createServer } from "node:http";
|
|
19
|
-
import { randomBytes, randomUUID, createHash, timingSafeEqual } from "node:crypto";
|
|
20
|
-
import { readFile } from "node:fs/promises";
|
|
21
|
-
import { networkInterfaces } from "node:os";
|
|
22
|
-
import { Readable } from "node:stream";
|
|
23
|
-
import { pipeline } from "node:stream/promises";
|
|
24
|
-
import { withFileLock } from "@deepseek-ai/dsh-atomic-write";
|
|
25
|
-
var defaults = () => ({ version: 1, enabled: false, port: 8787, allowLan: false, modelIds: null, key: null });
|
|
26
|
-
var revision = (value) => createHash("sha256").update(JSON.stringify(value)).digest("hex");
|
|
27
|
-
var fail = (status, code) => {
|
|
28
|
-
throw new RouteError(status, code);
|
|
29
|
-
};
|
|
30
|
-
var json = (res, status, value) => {
|
|
31
|
-
res.writeHead(status, { "Content-Type": "application/json; charset=utf-8", "Cache-Control": "no-store", "X-Content-Type-Options": "nosniff" });
|
|
32
|
-
res.end(JSON.stringify(value));
|
|
33
|
-
};
|
|
34
|
-
function validate(value) {
|
|
35
|
-
if (!value || value.version !== 1 || typeof value.enabled !== "boolean" || typeof value.allowLan !== "boolean" || !Number.isInteger(value.port) || value.port < 1024 || value.port > 65535 || value.modelIds !== null && (!Array.isArray(value.modelIds) || value.modelIds.length > 1e3 || value.modelIds.some((id) => typeof id !== "string" || !/^[\w.-]{1,128}$/.test(id))) || value.key !== null && !/^sk-dsh-[A-Za-z0-9_-]{43}$/.test(value.key) || value.enabled && !value.key) throw Error("API \u8BBE\u7F6E\u65E0\u6548\uFF1A\u7AEF\u53E3\u9700\u4E3A 1024\u201365535\uFF0C\u542F\u7528\u524D\u8BF7\u751F\u6210 API Key\u3002");
|
|
36
|
-
return value;
|
|
37
|
-
}
|
|
38
|
-
var ApiService = class {
|
|
39
|
-
constructor(file, manager) {
|
|
40
|
-
this.file = file;
|
|
41
|
-
this.manager = manager;
|
|
42
|
-
this.settings = defaults();
|
|
43
|
-
this.phase = "stopped";
|
|
44
|
-
this.controllers = /* @__PURE__ */ new Set();
|
|
45
|
-
this.queue = Promise.resolve();
|
|
46
|
-
}
|
|
47
|
-
async load() {
|
|
48
|
-
this.configError = false;
|
|
49
|
-
try {
|
|
50
|
-
this.settings = validate(JSON.parse(await readFile(this.file, "utf8")));
|
|
51
|
-
} catch (e) {
|
|
52
|
-
if (e.code !== "ENOENT") {
|
|
53
|
-
this.configError = true;
|
|
54
|
-
this.phase = "error";
|
|
55
|
-
this.error = "API \u914D\u7F6E\u65E0\u6CD5\u8BFB\u53D6\uFF0C\u670D\u52A1\u672A\u542F\u52A8\u3002\u8BF7\u91CD\u65B0\u4FDD\u5B58\u8BBE\u7F6E\u3002";
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
choices() {
|
|
60
|
-
return this.manager.adapterModels().filter((m) => !m.freeOnly || m.dynamicFree);
|
|
61
|
-
}
|
|
62
|
-
models() {
|
|
63
|
-
return this.choices().filter((m) => this.settings.modelIds === null ? m.visible !== false : this.settings.modelIds.includes(m.id));
|
|
64
|
-
}
|
|
65
|
-
state() {
|
|
66
|
-
const { key, ...settings } = this.settings;
|
|
67
|
-
const port = this.server?.address()?.port ?? settings.port;
|
|
68
|
-
const events = this.manager.telemetry.data.events.filter((e) => e.source === "api" && beijingDay(e.at) === beijingDay(Date.now()));
|
|
69
|
-
const addresses = settings.allowLan ? [...new Set(Object.values(networkInterfaces()).flat().filter((a) => a && a.family === "IPv4" && !a.internal).map((a) => `http://${a.address}:${port}/v1`))] : [];
|
|
70
|
-
return {
|
|
71
|
-
...settings,
|
|
72
|
-
revision: revision(this.settings),
|
|
73
|
-
phase: this.phase,
|
|
74
|
-
error: this.error,
|
|
75
|
-
hasKey: !!key,
|
|
76
|
-
keyHint: key ? `sk-dsh-\u2022\u2022\u2022\u2022${key.slice(-4)}` : null,
|
|
77
|
-
active: this.controllers.size,
|
|
78
|
-
baseUrl: `http://127.0.0.1:${port}/v1`,
|
|
79
|
-
lanUrls: addresses,
|
|
80
|
-
choices: this.choices().map((m) => ({ id: m.id, name: m.name, visible: m.visible !== false, reasoning: m.reasoning })),
|
|
81
|
-
models: this.models().map((m) => ({ id: m.id, name: m.name })),
|
|
82
|
-
today: { calls: new Set(events.map((e) => e.callId)).size, attempts: events.length, tokens: events.reduce((n, e) => n + (e.tokens ?? 0), 0) }
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
action(input) {
|
|
86
|
-
const task = this.queue.catch(() => {
|
|
87
|
-
}).then(() => withFileLock(this.file, async () => {
|
|
88
|
-
if (this.closed) throw Error("API \u670D\u52A1\u5DF2\u5173\u95ED\u3002");
|
|
89
|
-
let current = this.settings;
|
|
90
|
-
try {
|
|
91
|
-
current = validate(JSON.parse(await readFile(this.file, "utf8")));
|
|
92
|
-
} catch (e) {
|
|
93
|
-
if (e.code !== "ENOENT" && this.phase !== "error") throw e;
|
|
94
|
-
}
|
|
95
|
-
if (input.apiRevision !== revision(current)) throw Error("API \u914D\u7F6E\u5DF2\u66F4\u65B0\uFF0C\u8BF7\u5237\u65B0\u9762\u677F\u540E\u91CD\u8BD5\u3002");
|
|
96
|
-
const next = structuredClone(current);
|
|
97
|
-
let apiKey;
|
|
98
|
-
if (input.action === "api.key.reveal") return { apiKey: next.key };
|
|
99
|
-
if (input.action === "api.key.create") {
|
|
100
|
-
next.key = `sk-dsh-${randomBytes(32).toString("base64url")}`;
|
|
101
|
-
apiKey = next.key;
|
|
102
|
-
} else if (input.action === "api.key.revoke") {
|
|
103
|
-
next.key = null;
|
|
104
|
-
next.enabled = false;
|
|
105
|
-
} else if (input.action === "api.settings") {
|
|
106
|
-
for (const k of ["enabled", "port", "allowLan", "modelIds"]) if (input[k] !== void 0) next[k] = input[k];
|
|
107
|
-
if (next.modelIds?.some((id) => !this.choices().some((m) => m.id === id) && !current.modelIds?.includes(id))) throw Error("\u6A21\u578B\u76EE\u5F55\u5DF2\u53D8\u5316\uFF0C\u8BF7\u5237\u65B0\u9762\u677F\u540E\u91CD\u65B0\u9009\u62E9\u3002");
|
|
108
|
-
} else throw Error("\u672A\u77E5 API \u64CD\u4F5C\u3002");
|
|
109
|
-
validate(next);
|
|
110
|
-
await writePrivateJson(this.file, next);
|
|
111
|
-
this.configError = false;
|
|
112
|
-
const restart = next.enabled !== this.settings.enabled || next.port !== this.settings.port || next.allowLan !== this.settings.allowLan;
|
|
113
|
-
this.settings = next;
|
|
114
|
-
if (restart || this.phase === "error" || !this.server && next.enabled) await this.reconcile();
|
|
115
|
-
return { apiKey };
|
|
116
|
-
}, { waitMs: 1e4 }));
|
|
117
|
-
this.queue = task;
|
|
118
|
-
return task;
|
|
119
|
-
}
|
|
120
|
-
async reconcile() {
|
|
121
|
-
await this.stop();
|
|
122
|
-
if (this.configError) {
|
|
123
|
-
this.phase = "error";
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
this.error = void 0;
|
|
127
|
-
if (this.closed || !this.settings.enabled) {
|
|
128
|
-
this.phase = "stopped";
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
this.phase = "starting";
|
|
132
|
-
const server = createServer((req, res) => {
|
|
133
|
-
void this.handle(req, res);
|
|
134
|
-
});
|
|
135
|
-
server.requestTimeout = 3e4;
|
|
136
|
-
server.headersTimeout = 1e4;
|
|
137
|
-
server.keepAliveTimeout = 5e3;
|
|
138
|
-
this.server = server;
|
|
139
|
-
try {
|
|
140
|
-
await new Promise((resolve, reject) => {
|
|
141
|
-
server.once("error", reject);
|
|
142
|
-
server.listen(this.settings.port, this.settings.allowLan ? "0.0.0.0" : "127.0.0.1", resolve);
|
|
143
|
-
});
|
|
144
|
-
this.phase = "running";
|
|
145
|
-
server.on("error", () => {
|
|
146
|
-
this.phase = "error";
|
|
147
|
-
this.error = "API \u76D1\u542C\u5F02\u5E38\uFF0C\u8BF7\u91CD\u65B0\u4FDD\u5B58\u8BBE\u7F6E\u3002";
|
|
148
|
-
});
|
|
149
|
-
} catch (e) {
|
|
150
|
-
this.server = void 0;
|
|
151
|
-
this.phase = "error";
|
|
152
|
-
this.error = e.code === "EADDRINUSE" ? "\u7AEF\u53E3\u5DF2\u88AB\u5360\u7528\uFF0C\u8BF7\u4FEE\u6539\u7AEF\u53E3\u540E\u4FDD\u5B58\u3002" : e.code === "EACCES" ? "\u6CA1\u6709\u76D1\u542C\u6B64\u7AEF\u53E3\u7684\u6743\u9650\uFF0C\u8BF7\u4FEE\u6539\u7AEF\u53E3\u3002" : "API \u670D\u52A1\u542F\u52A8\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u7AEF\u53E3\u8BBE\u7F6E\u3002";
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
async stop() {
|
|
156
|
-
const server = this.server;
|
|
157
|
-
this.server = void 0;
|
|
158
|
-
for (const c of this.controllers) c.abort();
|
|
159
|
-
if (server) {
|
|
160
|
-
server.closeAllConnections();
|
|
161
|
-
await new Promise((resolve) => server.close(resolve));
|
|
162
|
-
}
|
|
163
|
-
this.phase = "stopped";
|
|
164
|
-
}
|
|
165
|
-
async close() {
|
|
166
|
-
this.closed = true;
|
|
167
|
-
await this.queue.catch(() => {
|
|
168
|
-
});
|
|
169
|
-
await this.stop();
|
|
170
|
-
}
|
|
171
|
-
async handle(req, res) {
|
|
172
|
-
const controller = new AbortController();
|
|
173
|
-
const signal = AbortSignal.any([controller.signal, AbortSignal.timeout(Math.min(this.manager.config.requestTimeoutMs, 2147483647))]);
|
|
174
|
-
req.on("aborted", () => controller.abort());
|
|
175
|
-
res.on("close", () => {
|
|
176
|
-
if (!res.writableEnded) controller.abort();
|
|
177
|
-
});
|
|
178
|
-
let selected;
|
|
179
|
-
try {
|
|
180
|
-
if (!this.settings.enabled || this.closed) fail(503, "service_disabled");
|
|
181
|
-
if (!this.settings.allowLan && !hostIsLoopback(req.headers.host)) fail(403, "loopback_required");
|
|
182
|
-
if (req.headers.origin || req.headers["sec-fetch-site"] === "cross-site") fail(403, "browser_origin_not_allowed");
|
|
183
|
-
const token = /^Bearer\s+(\S+)$/i.exec(req.headers.authorization ?? "")?.[1] ?? "";
|
|
184
|
-
const actual = Buffer.from(token), expected = Buffer.from(this.settings.key ?? "");
|
|
185
|
-
if (!expected.length || actual.length !== expected.length || !timingSafeEqual(actual, expected)) fail(401, "invalid_api_key");
|
|
186
|
-
if (req.method === "GET" && req.url === "/v1/models") {
|
|
187
|
-
json(res, 200, { object: "list", data: this.models().map((m) => ({ id: m.id, object: "model", created: 0, owned_by: "dsh-multi-router" })) });
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
if (req.method !== "POST" || req.url !== "/v1/chat/completions") fail(404, "not_found");
|
|
191
|
-
if (this.controllers.size >= 8) fail(429, "too_many_requests");
|
|
192
|
-
this.controllers.add(controller);
|
|
193
|
-
if (!/^application\/json(?:\s*;|$)/i.test(req.headers["content-type"] ?? "")) fail(415, "json_required");
|
|
194
|
-
const chunks = [];
|
|
195
|
-
let size = 0;
|
|
196
|
-
for await (const chunk of req) {
|
|
197
|
-
size += chunk.length;
|
|
198
|
-
if (size > 32 * 1024 * 1024) fail(413, "request_too_large");
|
|
199
|
-
signal.throwIfAborted();
|
|
200
|
-
chunks.push(chunk);
|
|
201
|
-
}
|
|
202
|
-
let input;
|
|
203
|
-
try {
|
|
204
|
-
input = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
205
|
-
} catch {
|
|
206
|
-
fail(400, "invalid_json");
|
|
207
|
-
}
|
|
208
|
-
if (!input || typeof input !== "object" || Array.isArray(input) || typeof input.model !== "string" || !Array.isArray(input.messages) || !input.messages.length || input.messages.some((m) => !m || typeof m !== "object" || !["system", "developer", "user", "assistant", "tool", "function"].includes(m.role)) || input.stream !== void 0 && typeof input.stream !== "boolean") fail(400, "invalid_request");
|
|
209
|
-
if (!this.models().some((m) => m.id === input.model)) fail(404, "model_not_exposed");
|
|
210
|
-
if (input.n !== void 0 && input.n !== 1) fail(400, "only_n_1_supported");
|
|
211
|
-
if (input.reasoning_effort != null && !REASONING_EFFORTS.includes(input.reasoning_effort)) fail(400, "invalid_reasoning_effort");
|
|
212
|
-
selected = await abortable(this.manager.router.chat({ ...input, stream: true }, signal, { callId: randomUUID(), source: "api" }), signal);
|
|
213
|
-
signal.throwIfAborted();
|
|
214
|
-
const frames = completionFrames(selected.response, input.model, signal);
|
|
215
|
-
if (input.stream === true) {
|
|
216
|
-
res.writeHead(200, { "Content-Type": "text/event-stream; charset=utf-8", "Cache-Control": "no-cache", "X-Accel-Buffering": "no" });
|
|
217
|
-
await pipeline(Readable.from((async function* () {
|
|
218
|
-
for await (const frame of frames) {
|
|
219
|
-
if (!input.stream_options?.include_usage && !frame.choices?.length && frame.usage) continue;
|
|
220
|
-
if (!input.stream_options?.include_usage) delete frame.usage;
|
|
221
|
-
yield `data: ${JSON.stringify(frame)}
|
|
222
|
-
|
|
223
|
-
`;
|
|
224
|
-
}
|
|
225
|
-
yield "data: [DONE]\n\n";
|
|
226
|
-
})()), res, { signal });
|
|
227
|
-
} else json(res, 200, await collectCompletion(frames, input.model));
|
|
228
|
-
} catch (e) {
|
|
229
|
-
if (res.destroyed) return;
|
|
230
|
-
if (res.headersSent) {
|
|
231
|
-
res.destroy();
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
const status = e instanceof RouteError ? e.status : signal.aborted ? 504 : 500;
|
|
235
|
-
const code = e instanceof RouteError ? e.code : signal.aborted ? "request_timeout" : "internal_error";
|
|
236
|
-
json(res, status, { error: { message: code, type: status === 401 ? "authentication_error" : status < 500 ? "invalid_request_error" : "server_error", code, param: null } });
|
|
237
|
-
} finally {
|
|
238
|
-
this.controllers.delete(controller);
|
|
239
|
-
if (selected?.response.body && !selected.response.body.locked) await selected.response.body.cancel().catch(() => {
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
};
|
|
244
|
-
async function* completionFrames(response, model, signal) {
|
|
245
|
-
if (!response.body) fail(502, "empty_upstream_response");
|
|
246
|
-
const reader = response.body.getReader(), decoder = new TextDecoder(), sse = new SseDecoder();
|
|
247
|
-
let finished = false, total = 0, frameBytes = 0;
|
|
248
|
-
const abort = () => {
|
|
249
|
-
void reader.cancel().catch(() => {
|
|
250
|
-
});
|
|
251
|
-
};
|
|
252
|
-
signal.addEventListener("abort", abort, { once: true });
|
|
253
|
-
function parse(event) {
|
|
254
|
-
if (event.data === "[DONE]") return null;
|
|
255
|
-
let value;
|
|
256
|
-
try {
|
|
257
|
-
value = JSON.parse(event.data);
|
|
258
|
-
} catch {
|
|
259
|
-
fail(502, "invalid_upstream_stream");
|
|
260
|
-
}
|
|
261
|
-
if (!value || typeof value !== "object" || Array.isArray(value) || value.error) fail(502, "upstream_stream_error");
|
|
262
|
-
if (value.choices !== void 0 && (!Array.isArray(value.choices) || value.choices.length > 32)) fail(502, "invalid_upstream_stream");
|
|
263
|
-
for (const c of value.choices ?? []) {
|
|
264
|
-
if (!c || !Number.isInteger(c.index) || c.index < 0 || c.index > 31) fail(502, "invalid_upstream_stream");
|
|
265
|
-
if (c.delta?.tool_calls && (!Array.isArray(c.delta.tool_calls) || c.delta.tool_calls.some((t) => !t || !Number.isInteger(t.index) || t.index < 0 || t.index > 127))) fail(502, "invalid_upstream_stream");
|
|
266
|
-
}
|
|
267
|
-
if (value.choices?.some((c) => c.finish_reason)) finished = true;
|
|
268
|
-
return { ...value, model };
|
|
269
|
-
}
|
|
270
|
-
try {
|
|
271
|
-
while (true) {
|
|
272
|
-
signal.throwIfAborted();
|
|
273
|
-
const chunk = await reader.read();
|
|
274
|
-
signal.throwIfAborted();
|
|
275
|
-
if (chunk.done) break;
|
|
276
|
-
total += chunk.value.byteLength;
|
|
277
|
-
frameBytes += chunk.value.byteLength;
|
|
278
|
-
if (total > 32 * 1024 * 1024 || frameBytes > 1024 * 1024) fail(502, "upstream_response_too_large");
|
|
279
|
-
const events = sse.push(decoder.decode(chunk.value, { stream: true }));
|
|
280
|
-
if (events.length) frameBytes = 0;
|
|
281
|
-
for (const event of events) {
|
|
282
|
-
const value = parse(event);
|
|
283
|
-
if (value) yield value;
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
for (const event of sse.finish()) {
|
|
287
|
-
const value = parse(event);
|
|
288
|
-
if (value) yield value;
|
|
289
|
-
}
|
|
290
|
-
if (!finished) fail(502, "upstream_stream_incomplete");
|
|
291
|
-
} finally {
|
|
292
|
-
signal.removeEventListener("abort", abort);
|
|
293
|
-
await reader.cancel().catch(() => {
|
|
294
|
-
});
|
|
295
|
-
reader.releaseLock();
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
async function collectCompletion(frames, model) {
|
|
299
|
-
const result = { id: `chatcmpl-${randomUUID()}`, object: "chat.completion", created: Math.floor(Date.now() / 1e3), model, choices: [] };
|
|
300
|
-
const choices = /* @__PURE__ */ new Map();
|
|
301
|
-
for await (const frame of frames) {
|
|
302
|
-
if (frame.id) result.id = frame.id;
|
|
303
|
-
if (frame.created != null) result.created = frame.created;
|
|
304
|
-
if (frame.usage) result.usage = frame.usage;
|
|
305
|
-
for (const part of frame.choices ?? []) {
|
|
306
|
-
const index = part.index ?? 0;
|
|
307
|
-
if (!choices.has(index)) choices.set(index, { index, message: { role: "assistant", content: null }, finish_reason: null, logprobs: null });
|
|
308
|
-
const choice = choices.get(index), delta = part.delta ?? {};
|
|
309
|
-
for (const k of ["content", "reasoning_content", "reasoning", "refusal"]) if (typeof delta[k] === "string") choice.message[k] = (choice.message[k] ?? "") + delta[k];
|
|
310
|
-
if (delta.function_call) {
|
|
311
|
-
choice.message.function_call ??= { name: "", arguments: "" };
|
|
312
|
-
for (const k of ["name", "arguments"]) if (typeof delta.function_call[k] === "string") choice.message.function_call[k] += delta.function_call[k];
|
|
313
|
-
}
|
|
314
|
-
if (delta.tool_calls) for (const tool of delta.tool_calls) {
|
|
315
|
-
choice.message.tool_calls ??= [];
|
|
316
|
-
const i = tool.index ?? 0;
|
|
317
|
-
const value = choice.message.tool_calls[i] ??= { id: "", type: "function", function: { name: "", arguments: "" } };
|
|
318
|
-
if (tool.id) value.id += tool.id;
|
|
319
|
-
if (tool.type) value.type = tool.type;
|
|
320
|
-
for (const k of ["name", "arguments"]) if (typeof tool.function?.[k] === "string") value.function[k] += tool.function[k];
|
|
321
|
-
}
|
|
322
|
-
if (part.finish_reason) choice.finish_reason = part.finish_reason;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
result.choices = [...choices.values()].sort((a, b) => a.index - b.index);
|
|
326
|
-
return result;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
export {
|
|
330
|
-
ApiService
|
|
331
|
-
};
|
|
332
|
-
//# sourceMappingURL=chunk-TM5XDMGY.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/api-service.mjs"],
|
|
4
|
-
"sourcesContent": ["import { createServer } from 'node:http';\nimport { randomBytes, randomUUID, createHash, timingSafeEqual } from 'node:crypto';\nimport { readFile } from 'node:fs/promises';\nimport { networkInterfaces } from 'node:os';\nimport { Readable } from 'node:stream';\nimport { pipeline } from 'node:stream/promises';\nimport { withFileLock } from '@deepseek-ai/dsh-atomic-write';\nimport { writePrivateJson, beijingDay } from './config.mjs';\nimport { RouteError, abortable } from './router.mjs';\nimport { REASONING_EFFORTS } from './reasoning.mjs';\nimport { hostIsLoopback } from '../upstream/workbuddy/src/loopback.ts';\nimport { SseDecoder } from '../upstream/trae/src/sse.ts';\n\nconst defaults = () => ({ version: 1, enabled: false, port: 8787, allowLan: false, modelIds: null, key: null });\nconst revision = value => createHash('sha256').update(JSON.stringify(value)).digest('hex');\nconst fail = (status, code) => { throw new RouteError(status, code); };\nconst json = (res, status, value) => {\n res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8', 'Cache-Control': 'no-store', 'X-Content-Type-Options': 'nosniff' });\n res.end(JSON.stringify(value));\n};\nfunction validate(value) {\n if (!value || value.version !== 1 || typeof value.enabled !== 'boolean' || typeof value.allowLan !== 'boolean'\n || !Number.isInteger(value.port) || value.port < 1024 || value.port > 65535\n || value.modelIds !== null && (!Array.isArray(value.modelIds) || value.modelIds.length > 1000 || value.modelIds.some(id => typeof id !== 'string' || !/^[\\w.-]{1,128}$/.test(id)))\n || value.key !== null && !/^sk-dsh-[A-Za-z0-9_-]{43}$/.test(value.key)\n || value.enabled && !value.key) throw Error('API \u8BBE\u7F6E\u65E0\u6548\uFF1A\u7AEF\u53E3\u9700\u4E3A 1024\u201365535\uFF0C\u542F\u7528\u524D\u8BF7\u751F\u6210 API Key\u3002');\n return value;\n}\n\n// The service owns only its listener and private access key. Routing, provider\n// credentials, failover and metering remain with the existing Manager/Router.\nexport class ApiService {\n constructor(file, manager) {\n this.file = file; this.manager = manager; this.settings = defaults();\n this.phase = 'stopped'; this.controllers = new Set(); this.queue = Promise.resolve();\n }\n async load() {\n this.configError = false;\n try { this.settings = validate(JSON.parse(await readFile(this.file, 'utf8'))); }\n catch (e) { if (e.code !== 'ENOENT') { this.configError = true; this.phase = 'error'; this.error = 'API \u914D\u7F6E\u65E0\u6CD5\u8BFB\u53D6\uFF0C\u670D\u52A1\u672A\u542F\u52A8\u3002\u8BF7\u91CD\u65B0\u4FDD\u5B58\u8BBE\u7F6E\u3002'; } }\n }\n choices() { return this.manager.adapterModels().filter(m => !m.freeOnly || m.dynamicFree); }\n models() { return this.choices().filter(m => this.settings.modelIds === null ? m.visible !== false : this.settings.modelIds.includes(m.id)); }\n state() {\n const { key, ...settings } = this.settings;\n const port = this.server?.address()?.port ?? settings.port;\n const events = this.manager.telemetry.data.events.filter(e => e.source === 'api' && beijingDay(e.at) === beijingDay(Date.now()));\n const addresses = settings.allowLan ? [...new Set(Object.values(networkInterfaces()).flat().filter(a => a && a.family === 'IPv4' && !a.internal).map(a => `http://${a.address}:${port}/v1`))] : [];\n return { ...settings, revision: revision(this.settings), phase: this.phase, error: this.error,\n hasKey: !!key, keyHint: key ? `sk-dsh-\u2022\u2022\u2022\u2022${key.slice(-4)}` : null, active: this.controllers.size,\n baseUrl: `http://127.0.0.1:${port}/v1`, lanUrls: addresses,\n choices: this.choices().map(m => ({ id: m.id, name: m.name, visible: m.visible !== false, reasoning: m.reasoning })),\n models: this.models().map(m => ({ id: m.id, name: m.name })),\n today: { calls: new Set(events.map(e => e.callId)).size, attempts: events.length, tokens: events.reduce((n, e) => n + (e.tokens ?? 0), 0) } };\n }\n action(input) {\n const task = this.queue.catch(() => {}).then(() => withFileLock(this.file, async () => {\n if (this.closed) throw Error('API \u670D\u52A1\u5DF2\u5173\u95ED\u3002');\n // Compare the on-disk revision too; two DSH instances must not overwrite a key.\n let current = this.settings;\n try { current = validate(JSON.parse(await readFile(this.file, 'utf8'))); } catch (e) { if (e.code !== 'ENOENT' && this.phase !== 'error') throw e; }\n if (input.apiRevision !== revision(current)) throw Error('API \u914D\u7F6E\u5DF2\u66F4\u65B0\uFF0C\u8BF7\u5237\u65B0\u9762\u677F\u540E\u91CD\u8BD5\u3002');\n const next = structuredClone(current);\n let apiKey;\n if (input.action === 'api.key.reveal') return { apiKey: next.key };\n if (input.action === 'api.key.create') { next.key = `sk-dsh-${randomBytes(32).toString('base64url')}`; apiKey = next.key; }\n else if (input.action === 'api.key.revoke') { next.key = null; next.enabled = false; }\n else if (input.action === 'api.settings') {\n for (const k of ['enabled', 'port', 'allowLan', 'modelIds']) if (input[k] !== undefined) next[k] = input[k];\n if (next.modelIds?.some(id => !this.choices().some(m => m.id === id) && !current.modelIds?.includes(id))) throw Error('\u6A21\u578B\u76EE\u5F55\u5DF2\u53D8\u5316\uFF0C\u8BF7\u5237\u65B0\u9762\u677F\u540E\u91CD\u65B0\u9009\u62E9\u3002');\n } else throw Error('\u672A\u77E5 API \u64CD\u4F5C\u3002');\n validate(next);\n await writePrivateJson(this.file, next);\n this.configError = false;\n const restart = next.enabled !== this.settings.enabled || next.port !== this.settings.port || next.allowLan !== this.settings.allowLan;\n this.settings = next;\n if (restart || this.phase === 'error' || !this.server && next.enabled) await this.reconcile();\n return { apiKey };\n }, { waitMs: 10000 }));\n this.queue = task; return task;\n }\n async reconcile() {\n await this.stop();\n if (this.configError) { this.phase = 'error'; return; }\n this.error = undefined;\n if (this.closed || !this.settings.enabled) { this.phase = 'stopped'; return; }\n this.phase = 'starting';\n const server = createServer((req, res) => { void this.handle(req, res); });\n server.requestTimeout = 30000; server.headersTimeout = 10000; server.keepAliveTimeout = 5000;\n this.server = server;\n try {\n await new Promise((resolve, reject) => {\n server.once('error', reject);\n server.listen(this.settings.port, this.settings.allowLan ? '0.0.0.0' : '127.0.0.1', resolve);\n });\n this.phase = 'running';\n server.on('error', () => { this.phase = 'error'; this.error = 'API \u76D1\u542C\u5F02\u5E38\uFF0C\u8BF7\u91CD\u65B0\u4FDD\u5B58\u8BBE\u7F6E\u3002'; });\n } catch (e) {\n this.server = undefined; this.phase = 'error';\n this.error = e.code === 'EADDRINUSE' ? '\u7AEF\u53E3\u5DF2\u88AB\u5360\u7528\uFF0C\u8BF7\u4FEE\u6539\u7AEF\u53E3\u540E\u4FDD\u5B58\u3002' : e.code === 'EACCES' ? '\u6CA1\u6709\u76D1\u542C\u6B64\u7AEF\u53E3\u7684\u6743\u9650\uFF0C\u8BF7\u4FEE\u6539\u7AEF\u53E3\u3002' : 'API \u670D\u52A1\u542F\u52A8\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u7AEF\u53E3\u8BBE\u7F6E\u3002';\n }\n }\n async stop() {\n const server = this.server; this.server = undefined;\n for (const c of this.controllers) c.abort();\n if (server) { server.closeAllConnections(); await new Promise(resolve => server.close(resolve)); }\n this.phase = 'stopped';\n }\n async close() { this.closed = true; await this.queue.catch(() => {}); await this.stop(); }\n async handle(req, res) {\n const controller = new AbortController();\n const signal = AbortSignal.any([controller.signal, AbortSignal.timeout(Math.min(this.manager.config.requestTimeoutMs, 2147483647))]);\n req.on('aborted', () => controller.abort());\n res.on('close', () => { if (!res.writableEnded) controller.abort(); });\n let selected;\n try {\n if (!this.settings.enabled || this.closed) fail(503, 'service_disabled');\n if (!this.settings.allowLan && !hostIsLoopback(req.headers.host)) fail(403, 'loopback_required');\n // No browser CORS access: an unrelated page must never drive the local service.\n if (req.headers.origin || req.headers['sec-fetch-site'] === 'cross-site') fail(403, 'browser_origin_not_allowed');\n const token = /^Bearer\\s+(\\S+)$/i.exec(req.headers.authorization ?? '')?.[1] ?? '';\n const actual = Buffer.from(token), expected = Buffer.from(this.settings.key ?? '');\n if (!expected.length || actual.length !== expected.length || !timingSafeEqual(actual, expected)) fail(401, 'invalid_api_key');\n if (req.method === 'GET' && req.url === '/v1/models') {\n json(res, 200, { object: 'list', data: this.models().map(m => ({ id: m.id, object: 'model', created: 0, owned_by: 'dsh-multi-router' })) }); return;\n }\n if (req.method !== 'POST' || req.url !== '/v1/chat/completions') fail(404, 'not_found');\n if (this.controllers.size >= 8) fail(429, 'too_many_requests');\n this.controllers.add(controller);\n if (!/^application\\/json(?:\\s*;|$)/i.test(req.headers['content-type'] ?? '')) fail(415, 'json_required');\n const chunks = []; let size = 0;\n for await (const chunk of req) { size += chunk.length; if (size > 32 * 1024 * 1024) fail(413, 'request_too_large'); signal.throwIfAborted(); chunks.push(chunk); }\n let input;\n try { input = JSON.parse(Buffer.concat(chunks).toString('utf8')); } catch { fail(400, 'invalid_json'); }\n if (!input || typeof input !== 'object' || Array.isArray(input) || typeof input.model !== 'string'\n || !Array.isArray(input.messages) || !input.messages.length || input.messages.some(m => !m || typeof m !== 'object' || !['system', 'developer', 'user', 'assistant', 'tool', 'function'].includes(m.role))\n || input.stream !== undefined && typeof input.stream !== 'boolean') fail(400, 'invalid_request');\n if (!this.models().some(m => m.id === input.model)) fail(404, 'model_not_exposed');\n if (input.n !== undefined && input.n !== 1) fail(400, 'only_n_1_supported');\n if (input.reasoning_effort != null && !REASONING_EFFORTS.includes(input.reasoning_effort)) fail(400, 'invalid_reasoning_effort');\n // Never accept internal account/session headers or persist caller user IDs.\n selected = await abortable(this.manager.router.chat({ ...input, stream: true }, signal, { callId: randomUUID(), source: 'api' }), signal);\n signal.throwIfAborted();\n const frames = completionFrames(selected.response, input.model, signal);\n if (input.stream === true) {\n res.writeHead(200, { 'Content-Type': 'text/event-stream; charset=utf-8', 'Cache-Control': 'no-cache', 'X-Accel-Buffering': 'no' });\n await pipeline(Readable.from((async function* () {\n for await (const frame of frames) {\n if (!input.stream_options?.include_usage && !frame.choices?.length && frame.usage) continue;\n if (!input.stream_options?.include_usage) delete frame.usage;\n yield `data: ${JSON.stringify(frame)}\\n\\n`;\n }\n yield 'data: [DONE]\\n\\n';\n })()), res, { signal });\n } else json(res, 200, await collectCompletion(frames, input.model));\n } catch (e) {\n if (res.destroyed) return;\n if (res.headersSent) { res.destroy(); return; }\n const status = e instanceof RouteError ? e.status : signal.aborted ? 504 : 500;\n const code = e instanceof RouteError ? e.code : signal.aborted ? 'request_timeout' : 'internal_error';\n // RouteError.message includes account IDs; upstream exception text can contain credentials.\n json(res, status, { error: { message: code, type: status === 401 ? 'authentication_error' : status < 500 ? 'invalid_request_error' : 'server_error', code, param: null } });\n } finally {\n this.controllers.delete(controller);\n if (selected?.response.body && !selected.response.body.locked) await selected.response.body.cancel().catch(() => {});\n }\n }\n}\n\nasync function* completionFrames(response, model, signal) {\n if (!response.body) fail(502, 'empty_upstream_response');\n const reader = response.body.getReader(), decoder = new TextDecoder(), sse = new SseDecoder();\n let finished = false, total = 0, frameBytes = 0;\n const abort = () => { void reader.cancel().catch(() => {}); };\n signal.addEventListener('abort', abort, { once: true });\n function parse(event) {\n if (event.data === '[DONE]') return null;\n let value;\n try { value = JSON.parse(event.data); } catch { fail(502, 'invalid_upstream_stream'); }\n if (!value || typeof value !== 'object' || Array.isArray(value) || value.error) fail(502, 'upstream_stream_error');\n if (value.choices !== undefined && (!Array.isArray(value.choices) || value.choices.length > 32)) fail(502, 'invalid_upstream_stream');\n for (const c of value.choices ?? []) {\n if (!c || !Number.isInteger(c.index) || c.index < 0 || c.index > 31) fail(502, 'invalid_upstream_stream');\n if (c.delta?.tool_calls && (!Array.isArray(c.delta.tool_calls) || c.delta.tool_calls.some(t => !t || !Number.isInteger(t.index) || t.index < 0 || t.index > 127))) fail(502, 'invalid_upstream_stream');\n }\n if (value.choices?.some(c => c.finish_reason)) finished = true;\n return { ...value, model };\n }\n try {\n while (true) {\n signal.throwIfAborted(); const chunk = await reader.read(); signal.throwIfAborted();\n if (chunk.done) break;\n total += chunk.value.byteLength; frameBytes += chunk.value.byteLength;\n if (total > 32 * 1024 * 1024 || frameBytes > 1024 * 1024) fail(502, 'upstream_response_too_large');\n const events = sse.push(decoder.decode(chunk.value, { stream: true }));\n if (events.length) frameBytes = 0;\n for (const event of events) { const value = parse(event); if (value) yield value; }\n }\n for (const event of sse.finish()) { const value = parse(event); if (value) yield value; }\n if (!finished) fail(502, 'upstream_stream_incomplete');\n } finally { signal.removeEventListener('abort', abort); await reader.cancel().catch(() => {}); reader.releaseLock(); }\n}\n\nasync function collectCompletion(frames, model) {\n const result = { id: `chatcmpl-${randomUUID()}`, object: 'chat.completion', created: Math.floor(Date.now() / 1000), model, choices: [] };\n const choices = new Map();\n for await (const frame of frames) {\n if (frame.id) result.id = frame.id;\n if (frame.created != null) result.created = frame.created;\n if (frame.usage) result.usage = frame.usage;\n for (const part of frame.choices ?? []) {\n const index = part.index ?? 0;\n if (!choices.has(index)) choices.set(index, { index, message: { role: 'assistant', content: null }, finish_reason: null, logprobs: null });\n const choice = choices.get(index), delta = part.delta ?? {};\n for (const k of ['content', 'reasoning_content', 'reasoning', 'refusal']) if (typeof delta[k] === 'string') choice.message[k] = (choice.message[k] ?? '') + delta[k];\n if (delta.function_call) {\n choice.message.function_call ??= { name: '', arguments: '' };\n for (const k of ['name', 'arguments']) if (typeof delta.function_call[k] === 'string') choice.message.function_call[k] += delta.function_call[k];\n }\n if (delta.tool_calls) for (const tool of delta.tool_calls) {\n choice.message.tool_calls ??= [];\n const i = tool.index ?? 0;\n const value = choice.message.tool_calls[i] ??= { id: '', type: 'function', function: { name: '', arguments: '' } };\n if (tool.id) value.id += tool.id;\n if (tool.type) value.type = tool.type;\n for (const k of ['name', 'arguments']) if (typeof tool.function?.[k] === 'string') value.function[k] += tool.function[k];\n }\n if (part.finish_reason) choice.finish_reason = part.finish_reason;\n }\n }\n result.choices = [...choices.values()].sort((a, b) => a.index - b.index);\n return result;\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|