dskcode 0.1.41 → 0.1.42
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/dist/chunk-CV3ZB5VV.js +1 -0
- package/dist/deepseek-J25J2QL5.js +2 -0
- package/dist/index.js +1 -10627
- package/package.json +7 -7
- package/dist/chunk-LWOBXEMJ.js +0 -707
- package/dist/chunk-LWOBXEMJ.js.map +0 -1
- package/dist/deepseek-WFSVA5UQ.js +0 -9
- package/dist/deepseek-WFSVA5UQ.js.map +0 -1
- package/dist/index.js.map +0 -1
package/dist/chunk-LWOBXEMJ.js
DELETED
|
@@ -1,707 +0,0 @@
|
|
|
1
|
-
// src/provider/models.ts
|
|
2
|
-
var SUPPORTED_MODELS = {
|
|
3
|
-
"deepseek-v4-flash": {
|
|
4
|
-
id: "deepseek-v4-flash",
|
|
5
|
-
displayName: "DeepSeek V4 Flash",
|
|
6
|
-
/** 上下文窗口大小(token) */
|
|
7
|
-
contextWindow: 1e6,
|
|
8
|
-
/** 输入价格(元 / 百万 token,缓存未命中) */
|
|
9
|
-
inputPricePerMillion: 1,
|
|
10
|
-
/** 输出价格(元 / 百万 token) */
|
|
11
|
-
outputPricePerMillion: 2,
|
|
12
|
-
/** 输入价格(元 / 百万 token,缓存命中) */
|
|
13
|
-
cacheHitPricePerMillion: 0.02
|
|
14
|
-
},
|
|
15
|
-
"deepseek-v4-pro": {
|
|
16
|
-
id: "deepseek-v4-pro",
|
|
17
|
-
displayName: "DeepSeek V4 Pro",
|
|
18
|
-
/** 上下文窗口大小(token) */
|
|
19
|
-
contextWindow: 1e6,
|
|
20
|
-
/** 输入价格(元 / 百万 token,缓存未命中) */
|
|
21
|
-
inputPricePerMillion: 3,
|
|
22
|
-
/** 输出价格(元 / 百万 token) */
|
|
23
|
-
outputPricePerMillion: 6,
|
|
24
|
-
/** 输入价格(元 / 百万 token,缓存命中) */
|
|
25
|
-
cacheHitPricePerMillion: 0.025
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
var SUPPORTED_MODEL_IDS = Object.keys(SUPPORTED_MODELS);
|
|
29
|
-
function isSupportedModel(model) {
|
|
30
|
-
return model in SUPPORTED_MODELS;
|
|
31
|
-
}
|
|
32
|
-
function getModelMeta(model) {
|
|
33
|
-
return SUPPORTED_MODELS[model];
|
|
34
|
-
}
|
|
35
|
-
function isCJK(char) {
|
|
36
|
-
const code = char.codePointAt(0);
|
|
37
|
-
return code >= 19968 && code <= 40959 || // CJK 扩展 A
|
|
38
|
-
code >= 13312 && code <= 19903 || // CJK 扩展 B 及其他
|
|
39
|
-
code >= 131072 && code <= 173791 || // CJK 兼容汉字
|
|
40
|
-
code >= 63744 && code <= 64255 || // 全角字符
|
|
41
|
-
code >= 65281 && code <= 65376;
|
|
42
|
-
}
|
|
43
|
-
function estimateTokens(text) {
|
|
44
|
-
let cjkCount = 0;
|
|
45
|
-
let otherCount = 0;
|
|
46
|
-
for (const char of text) {
|
|
47
|
-
if (isCJK(char)) {
|
|
48
|
-
cjkCount++;
|
|
49
|
-
} else {
|
|
50
|
-
otherCount++;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
const tokens = cjkCount * 0.6 + otherCount * 0.3;
|
|
54
|
-
return Math.max(1, Math.ceil(tokens));
|
|
55
|
-
}
|
|
56
|
-
function calculateCost(usage, model) {
|
|
57
|
-
const meta = SUPPORTED_MODELS[model];
|
|
58
|
-
const cached = usage.cachedPromptTokens ?? 0;
|
|
59
|
-
const nonCached = usage.promptTokens - cached;
|
|
60
|
-
const inputCost = nonCached * meta.inputPricePerMillion / 1e6;
|
|
61
|
-
const cacheHitCost = cached * meta.cacheHitPricePerMillion / 1e6;
|
|
62
|
-
const outputCost = usage.completionTokens * meta.outputPricePerMillion / 1e6;
|
|
63
|
-
return {
|
|
64
|
-
inputCost,
|
|
65
|
-
cacheHitCost,
|
|
66
|
-
outputCost,
|
|
67
|
-
totalCost: inputCost + cacheHitCost + outputCost
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// src/provider/errors.ts
|
|
72
|
-
var ProviderError = class extends Error {
|
|
73
|
-
constructor(message, code, statusCode) {
|
|
74
|
-
super(message);
|
|
75
|
-
this.code = code;
|
|
76
|
-
this.statusCode = statusCode;
|
|
77
|
-
this.name = "ProviderError";
|
|
78
|
-
}
|
|
79
|
-
code;
|
|
80
|
-
statusCode;
|
|
81
|
-
};
|
|
82
|
-
var AuthError = class extends ProviderError {
|
|
83
|
-
constructor(message, statusCode) {
|
|
84
|
-
super(message, "AUTH_ERROR", statusCode);
|
|
85
|
-
this.name = "AuthError";
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
var RateLimitError = class extends ProviderError {
|
|
89
|
-
/** 建议等待的毫秒数 */
|
|
90
|
-
retryAfterMs;
|
|
91
|
-
constructor(message, retryAfterMs) {
|
|
92
|
-
super(message, "RATE_LIMIT", 429);
|
|
93
|
-
this.name = "RateLimitError";
|
|
94
|
-
this.retryAfterMs = retryAfterMs;
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
var ServerError = class extends ProviderError {
|
|
98
|
-
constructor(message, statusCode) {
|
|
99
|
-
super(message, "SERVER_ERROR", statusCode);
|
|
100
|
-
this.name = "ServerError";
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
var NetworkError = class extends ProviderError {
|
|
104
|
-
/** 导致网络错误的底层错误 */
|
|
105
|
-
originalError;
|
|
106
|
-
constructor(message, originalError) {
|
|
107
|
-
super(message, "NETWORK_ERROR");
|
|
108
|
-
this.name = "NetworkError";
|
|
109
|
-
this.originalError = originalError;
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
|
-
var TimeoutError = class extends ProviderError {
|
|
113
|
-
/** 触发超时的等待时间(毫秒) */
|
|
114
|
-
timeoutMs;
|
|
115
|
-
constructor(message, timeoutMs) {
|
|
116
|
-
super(message, "TIMEOUT");
|
|
117
|
-
this.name = "TimeoutError";
|
|
118
|
-
this.timeoutMs = timeoutMs;
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
var StreamIdleTimeoutError = class extends ProviderError {
|
|
122
|
-
/** 触发空闲超时的最大间隔(毫秒) */
|
|
123
|
-
idleMs;
|
|
124
|
-
constructor(message, idleMs) {
|
|
125
|
-
super(message, "STREAM_IDLE_TIMEOUT");
|
|
126
|
-
this.name = "StreamIdleTimeoutError";
|
|
127
|
-
this.idleMs = idleMs;
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
var ModelNotSupportedError = class extends ProviderError {
|
|
131
|
-
constructor(model) {
|
|
132
|
-
super(
|
|
133
|
-
`\u4E0D\u652F\u6301\u7684\u6A21\u578B: "${model}"\u3002dskcode \u4EC5\u652F\u6301: deepseek-v4-flash, deepseek-v4-pro`,
|
|
134
|
-
"MODEL_NOT_SUPPORTED"
|
|
135
|
-
);
|
|
136
|
-
this.name = "ModelNotSupportedError";
|
|
137
|
-
}
|
|
138
|
-
};
|
|
139
|
-
function mapHttpError(status, body) {
|
|
140
|
-
switch (status) {
|
|
141
|
-
case 401:
|
|
142
|
-
case 403:
|
|
143
|
-
return new AuthError(
|
|
144
|
-
`\u8BA4\u8BC1\u5931\u8D25\uFF1A\u8BF7\u68C0\u67E5 API Key \u662F\u5426\u6B63\u786E\u3002(${status})`,
|
|
145
|
-
status
|
|
146
|
-
);
|
|
147
|
-
case 429: {
|
|
148
|
-
let retryAfterMs;
|
|
149
|
-
try {
|
|
150
|
-
const parsed = JSON.parse(body);
|
|
151
|
-
const msg = parsed.error?.message ?? "";
|
|
152
|
-
const match = /(\d+)\s*second/i.exec(msg);
|
|
153
|
-
if (match?.[1]) {
|
|
154
|
-
retryAfterMs = Number(match[1]) * 1e3;
|
|
155
|
-
}
|
|
156
|
-
} catch {
|
|
157
|
-
}
|
|
158
|
-
return new RateLimitError(
|
|
159
|
-
`\u8BF7\u6C42\u8FC7\u4E8E\u9891\u7E41\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5\u3002${retryAfterMs ? `\u5EFA\u8BAE\u7B49\u5F85 ${Math.ceil(retryAfterMs / 1e3)} \u79D2\u3002` : ""}`,
|
|
160
|
-
retryAfterMs
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
case 400: {
|
|
164
|
-
let detail = "";
|
|
165
|
-
try {
|
|
166
|
-
const parsed = JSON.parse(body);
|
|
167
|
-
detail = parsed.error?.message ?? "";
|
|
168
|
-
} catch {
|
|
169
|
-
}
|
|
170
|
-
return new ProviderError(
|
|
171
|
-
`\u8BF7\u6C42\u53C2\u6570\u9519\u8BEF${detail ? `: ${detail}` : ""}`,
|
|
172
|
-
"BAD_REQUEST",
|
|
173
|
-
status
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
default:
|
|
177
|
-
if (status >= 500) {
|
|
178
|
-
return new ServerError(
|
|
179
|
-
`\u670D\u52A1\u7AEF\u9519\u8BEF (${status})\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5\u3002`,
|
|
180
|
-
status
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
return new ProviderError(`\u8BF7\u6C42\u5931\u8D25 (${status})`, "UNKNOWN_ERROR", status);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
function isRetryableError(err) {
|
|
187
|
-
return err instanceof RateLimitError || err instanceof ServerError || err instanceof NetworkError;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// src/provider/retry.ts
|
|
191
|
-
var DEFAULT_RETRY_OPTIONS = {
|
|
192
|
-
maxRetries: 3,
|
|
193
|
-
baseDelayMs: 1e3,
|
|
194
|
-
maxDelayMs: 3e4
|
|
195
|
-
};
|
|
196
|
-
function computeBackoffDelay(attempt, baseDelayMs, maxDelayMs) {
|
|
197
|
-
const exp = Math.pow(2, attempt - 1);
|
|
198
|
-
const jitter = Math.random() * (baseDelayMs / 2);
|
|
199
|
-
const delay = baseDelayMs * exp + jitter;
|
|
200
|
-
return Math.min(delay, maxDelayMs);
|
|
201
|
-
}
|
|
202
|
-
async function withRetry(fn, options = {}) {
|
|
203
|
-
const maxRetries = options.maxRetries ?? DEFAULT_RETRY_OPTIONS.maxRetries;
|
|
204
|
-
const baseDelayMs = options.baseDelayMs ?? DEFAULT_RETRY_OPTIONS.baseDelayMs;
|
|
205
|
-
const maxDelayMs = options.maxDelayMs ?? DEFAULT_RETRY_OPTIONS.maxDelayMs;
|
|
206
|
-
let lastError;
|
|
207
|
-
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
208
|
-
try {
|
|
209
|
-
return await fn();
|
|
210
|
-
} catch (err) {
|
|
211
|
-
lastError = err;
|
|
212
|
-
if (!(err instanceof ProviderError) || !isRetryableError(err)) {
|
|
213
|
-
throw err;
|
|
214
|
-
}
|
|
215
|
-
if (attempt >= maxRetries) break;
|
|
216
|
-
let delayMs;
|
|
217
|
-
if (err instanceof RateLimitError && err.retryAfterMs !== void 0) {
|
|
218
|
-
delayMs = Math.min(err.retryAfterMs, maxDelayMs);
|
|
219
|
-
} else {
|
|
220
|
-
delayMs = computeBackoffDelay(attempt + 1, baseDelayMs, maxDelayMs);
|
|
221
|
-
}
|
|
222
|
-
options.onRetry?.(attempt + 1, err, delayMs);
|
|
223
|
-
await sleep(delayMs);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
throw lastError instanceof Error ? lastError : new Error("withRetry: \u672A\u77E5\u9519\u8BEF");
|
|
227
|
-
}
|
|
228
|
-
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
229
|
-
|
|
230
|
-
// src/provider/client.ts
|
|
231
|
-
var DEFAULT_CONNECT_TIMEOUT_MS = 3e4;
|
|
232
|
-
var HttpClient = class {
|
|
233
|
-
#connectTimeoutMs;
|
|
234
|
-
#maxRetries;
|
|
235
|
-
#retryBaseDelayMs;
|
|
236
|
-
#retryMaxDelayMs;
|
|
237
|
-
constructor(config = {}) {
|
|
238
|
-
this.#connectTimeoutMs = config.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;
|
|
239
|
-
this.#maxRetries = config.maxRetries ?? 3;
|
|
240
|
-
this.#retryBaseDelayMs = config.retryBaseDelayMs ?? 1e3;
|
|
241
|
-
this.#retryMaxDelayMs = config.retryMaxDelayMs ?? 3e4;
|
|
242
|
-
}
|
|
243
|
-
/**
|
|
244
|
-
* 发起单次 HTTP 请求(带连接超时,不重试)。
|
|
245
|
-
*
|
|
246
|
-
* - 自动附加 `Content-Type: application/json`(若 body 是字符串)
|
|
247
|
-
* - 非 2xx 响应:抛出 mapHttpError 映射后的 ProviderError
|
|
248
|
-
* - 网络错误:抛出 NetworkError
|
|
249
|
-
* - 超时:抛出 TimeoutError
|
|
250
|
-
*
|
|
251
|
-
* @param url 请求 URL
|
|
252
|
-
* @param init fetch 初始化(method/headers/body)
|
|
253
|
-
* @param options 请求级选项(超时、中止信号)
|
|
254
|
-
* @returns 成功(2xx)的 Response
|
|
255
|
-
*/
|
|
256
|
-
async request(url, init = {}, options = {}) {
|
|
257
|
-
const timeoutMs = options.connectTimeoutMs === void 0 ? this.#connectTimeoutMs : options.connectTimeoutMs;
|
|
258
|
-
const headers = new Headers(init.headers);
|
|
259
|
-
if (init.body && typeof init.body === "string" && !headers.has("Content-Type")) {
|
|
260
|
-
headers.set("Content-Type", "application/json");
|
|
261
|
-
}
|
|
262
|
-
const { signal, cleanup } = this.#mergeSignals(
|
|
263
|
-
options.signal,
|
|
264
|
-
timeoutMs
|
|
265
|
-
);
|
|
266
|
-
let response;
|
|
267
|
-
try {
|
|
268
|
-
response = await fetch(url, {
|
|
269
|
-
...init,
|
|
270
|
-
headers,
|
|
271
|
-
signal
|
|
272
|
-
});
|
|
273
|
-
} catch (err) {
|
|
274
|
-
if (options.signal?.aborted) {
|
|
275
|
-
throw new ProviderError("\u8BF7\u6C42\u5DF2\u53D6\u6D88", "ABORTED");
|
|
276
|
-
}
|
|
277
|
-
if (signal?.aborted && !options.signal?.aborted) {
|
|
278
|
-
throw new TimeoutError(
|
|
279
|
-
`\u8FDE\u63A5\u8D85\u65F6\uFF08${timeoutMs}ms\uFF09: ${url}`,
|
|
280
|
-
timeoutMs
|
|
281
|
-
);
|
|
282
|
-
}
|
|
283
|
-
throw new NetworkError(
|
|
284
|
-
`\u7F51\u7EDC\u9519\u8BEF\uFF1A\u65E0\u6CD5\u8FDE\u63A5\u5230 ${url}`,
|
|
285
|
-
err instanceof Error ? err : void 0
|
|
286
|
-
);
|
|
287
|
-
} finally {
|
|
288
|
-
cleanup();
|
|
289
|
-
}
|
|
290
|
-
if (!response.ok) {
|
|
291
|
-
const body = await response.text().catch(() => "");
|
|
292
|
-
throw mapHttpError(response.status, body);
|
|
293
|
-
}
|
|
294
|
-
return response;
|
|
295
|
-
}
|
|
296
|
-
/**
|
|
297
|
-
* 发起带重试的 HTTP 请求。
|
|
298
|
-
*
|
|
299
|
-
* 等价于 request,但用 withRetry 包装:429 / 5xx / 网络错误会自动重试。
|
|
300
|
-
* 流式响应一旦获得 2xx,重试即停止,剩余流式稳定性交给 SSE 解析器。
|
|
301
|
-
*/
|
|
302
|
-
async requestWithRetry(url, init = {}, options = {}) {
|
|
303
|
-
const retryOptions = options.retry === false ? { maxRetries: 0 } : options.retry ?? {
|
|
304
|
-
maxRetries: this.#maxRetries,
|
|
305
|
-
baseDelayMs: this.#retryBaseDelayMs,
|
|
306
|
-
maxDelayMs: this.#retryMaxDelayMs
|
|
307
|
-
};
|
|
308
|
-
return withRetry(() => this.request(url, init, options), retryOptions);
|
|
309
|
-
}
|
|
310
|
-
/**
|
|
311
|
-
* 合并外部 signal 与连接超时 signal。
|
|
312
|
-
*
|
|
313
|
-
* 策略:
|
|
314
|
-
* - 若 timeoutMs <= 0 且无外部 signal → 不使用 signal
|
|
315
|
-
* - 若已有外部 signal 且无超时 → 直接使用外部 signal
|
|
316
|
-
* - 否则用 AbortSignal.any([外部, 超时]) 合并(Node 20+ 原生支持)
|
|
317
|
-
*
|
|
318
|
-
* @returns { signal, cleanup } 调用方应在 finally 调用 cleanup 取消监听
|
|
319
|
-
*/
|
|
320
|
-
#mergeSignals(external, timeoutMs) {
|
|
321
|
-
const useTimeout = timeoutMs > 0;
|
|
322
|
-
if (!external && !useTimeout) {
|
|
323
|
-
return { signal: void 0, cleanup: () => {
|
|
324
|
-
} };
|
|
325
|
-
}
|
|
326
|
-
if (!useTimeout) {
|
|
327
|
-
return { signal: external, cleanup: () => {
|
|
328
|
-
} };
|
|
329
|
-
}
|
|
330
|
-
const controller = new AbortController();
|
|
331
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
332
|
-
const onExternalAbort = () => controller.abort();
|
|
333
|
-
if (external) {
|
|
334
|
-
if (external.aborted) controller.abort();
|
|
335
|
-
else external.addEventListener("abort", onExternalAbort, { once: true });
|
|
336
|
-
}
|
|
337
|
-
return {
|
|
338
|
-
signal: controller.signal,
|
|
339
|
-
cleanup: () => {
|
|
340
|
-
clearTimeout(timer);
|
|
341
|
-
if (external) {
|
|
342
|
-
external.removeEventListener("abort", onExternalAbort);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
};
|
|
346
|
-
}
|
|
347
|
-
};
|
|
348
|
-
|
|
349
|
-
// src/provider/sse.ts
|
|
350
|
-
async function* parseSSE(response, options = {}) {
|
|
351
|
-
const reader = response.body?.getReader();
|
|
352
|
-
if (!reader) {
|
|
353
|
-
throw new Error("SSE \u54CD\u5E94\u4F53\u4E3A\u7A7A\uFF0C\u65E0\u6CD5\u89E3\u6790");
|
|
354
|
-
}
|
|
355
|
-
const decoder = new TextDecoder();
|
|
356
|
-
let buffer = "";
|
|
357
|
-
let pendingData = [];
|
|
358
|
-
let pendingEvent;
|
|
359
|
-
let pendingId;
|
|
360
|
-
let pendingRetry;
|
|
361
|
-
const idleMs = options.idleTimeoutMs ?? 6e4;
|
|
362
|
-
const shouldStopOnDone = options.stopOnDone !== false;
|
|
363
|
-
let streamDone = false;
|
|
364
|
-
function* flush() {
|
|
365
|
-
if (pendingData.length === 0) {
|
|
366
|
-
pendingEvent = void 0;
|
|
367
|
-
pendingId = void 0;
|
|
368
|
-
pendingRetry = void 0;
|
|
369
|
-
return;
|
|
370
|
-
}
|
|
371
|
-
const evt = { data: pendingData.join("\n") };
|
|
372
|
-
if (pendingEvent) evt.event = pendingEvent;
|
|
373
|
-
if (pendingId) evt.id = pendingId;
|
|
374
|
-
if (pendingRetry !== void 0) evt.retry = pendingRetry;
|
|
375
|
-
pendingData = [];
|
|
376
|
-
pendingEvent = void 0;
|
|
377
|
-
pendingId = void 0;
|
|
378
|
-
pendingRetry = void 0;
|
|
379
|
-
if (shouldStopOnDone && evt.data === "[DONE]") {
|
|
380
|
-
streamDone = true;
|
|
381
|
-
}
|
|
382
|
-
yield evt;
|
|
383
|
-
}
|
|
384
|
-
function* processLine(rawLine) {
|
|
385
|
-
const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
|
|
386
|
-
if (line === "") {
|
|
387
|
-
yield* flush();
|
|
388
|
-
return;
|
|
389
|
-
}
|
|
390
|
-
if (line.startsWith(":")) return;
|
|
391
|
-
const colonIdx = line.indexOf(":");
|
|
392
|
-
if (colonIdx === -1) {
|
|
393
|
-
applyField(line, "");
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
396
|
-
const field = line.slice(0, colonIdx);
|
|
397
|
-
let value = line.slice(colonIdx + 1);
|
|
398
|
-
if (value.startsWith(" ")) value = value.slice(1);
|
|
399
|
-
applyField(field, value);
|
|
400
|
-
}
|
|
401
|
-
function applyField(field, value) {
|
|
402
|
-
switch (field) {
|
|
403
|
-
case "event":
|
|
404
|
-
pendingEvent = value;
|
|
405
|
-
break;
|
|
406
|
-
case "data":
|
|
407
|
-
pendingData.push(value);
|
|
408
|
-
break;
|
|
409
|
-
case "id":
|
|
410
|
-
pendingId = value;
|
|
411
|
-
break;
|
|
412
|
-
case "retry": {
|
|
413
|
-
const n = Number(value);
|
|
414
|
-
if (Number.isFinite(n)) pendingRetry = n;
|
|
415
|
-
break;
|
|
416
|
-
}
|
|
417
|
-
default:
|
|
418
|
-
break;
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
try {
|
|
422
|
-
while (true) {
|
|
423
|
-
if (options.signal?.aborted) return;
|
|
424
|
-
const readResult = await readWithTimeout(reader, idleMs, options.signal);
|
|
425
|
-
if (readResult === null) return;
|
|
426
|
-
const { done, value } = readResult;
|
|
427
|
-
if (done) {
|
|
428
|
-
buffer += decoder.decode();
|
|
429
|
-
if (buffer.length > 0) {
|
|
430
|
-
const line = buffer.endsWith("\r") ? buffer.slice(0, -1) : buffer;
|
|
431
|
-
if (line !== "") yield* processLine(line);
|
|
432
|
-
buffer = "";
|
|
433
|
-
}
|
|
434
|
-
yield* flush();
|
|
435
|
-
if (streamDone) return;
|
|
436
|
-
return;
|
|
437
|
-
}
|
|
438
|
-
buffer += decoder.decode(value, { stream: true });
|
|
439
|
-
let nl;
|
|
440
|
-
while ((nl = buffer.indexOf("\n")) !== -1) {
|
|
441
|
-
const line = buffer.slice(0, nl);
|
|
442
|
-
buffer = buffer.slice(nl + 1);
|
|
443
|
-
yield* processLine(line);
|
|
444
|
-
if (streamDone) return;
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
} finally {
|
|
448
|
-
reader.releaseLock();
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
function readWithTimeout(reader, idleMs, signal) {
|
|
452
|
-
if (signal?.aborted) return Promise.resolve(null);
|
|
453
|
-
return new Promise((resolve, reject) => {
|
|
454
|
-
let settled = false;
|
|
455
|
-
const timer = setTimeout(() => {
|
|
456
|
-
if (settled) return;
|
|
457
|
-
settled = true;
|
|
458
|
-
reject(new StreamIdleTimeoutError(`\u6D41\u5F0F\u7A7A\u95F2\u8D85\u65F6\uFF08${idleMs}ms \u65E0\u6570\u636E\uFF09`, idleMs));
|
|
459
|
-
}, idleMs);
|
|
460
|
-
const onAbort = () => {
|
|
461
|
-
if (settled) return;
|
|
462
|
-
settled = true;
|
|
463
|
-
clearTimeout(timer);
|
|
464
|
-
resolve(null);
|
|
465
|
-
};
|
|
466
|
-
if (signal) signal.addEventListener("abort", onAbort, { once: true });
|
|
467
|
-
reader.read().then((result) => {
|
|
468
|
-
if (settled) return;
|
|
469
|
-
settled = true;
|
|
470
|
-
clearTimeout(timer);
|
|
471
|
-
if (signal) signal.removeEventListener("abort", onAbort);
|
|
472
|
-
resolve(result);
|
|
473
|
-
}).catch((err) => {
|
|
474
|
-
if (settled) return;
|
|
475
|
-
settled = true;
|
|
476
|
-
clearTimeout(timer);
|
|
477
|
-
if (signal) signal.removeEventListener("abort", onAbort);
|
|
478
|
-
reject(err);
|
|
479
|
-
});
|
|
480
|
-
});
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
// src/provider/deepseek-protocol.ts
|
|
484
|
-
async function* streamCompletion(client, baseUrl, apiKey, request, opts) {
|
|
485
|
-
const url = `${baseUrl}/chat/completions`;
|
|
486
|
-
const idleTimeoutMs = opts?.idleTimeoutMs ?? 6e4;
|
|
487
|
-
const response = await client.requestWithRetry(
|
|
488
|
-
url,
|
|
489
|
-
{
|
|
490
|
-
method: "POST",
|
|
491
|
-
headers: {
|
|
492
|
-
Authorization: `Bearer ${apiKey}`
|
|
493
|
-
},
|
|
494
|
-
body: JSON.stringify(request)
|
|
495
|
-
},
|
|
496
|
-
{ signal: opts?.signal }
|
|
497
|
-
);
|
|
498
|
-
for await (const evt of parseSSE(response, { idleTimeoutMs, signal: opts?.signal })) {
|
|
499
|
-
if (evt.data === "[DONE]") return;
|
|
500
|
-
let chunk;
|
|
501
|
-
try {
|
|
502
|
-
chunk = JSON.parse(evt.data);
|
|
503
|
-
} catch {
|
|
504
|
-
continue;
|
|
505
|
-
}
|
|
506
|
-
yield chunk;
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
async function getBalance(client, baseUrl, apiKey) {
|
|
510
|
-
const response = await client.request(`${baseUrl}/user/balance`, {
|
|
511
|
-
method: "GET",
|
|
512
|
-
headers: { Authorization: `Bearer ${apiKey}` }
|
|
513
|
-
});
|
|
514
|
-
const data = await response.json();
|
|
515
|
-
return {
|
|
516
|
-
isAvailable: data.is_available,
|
|
517
|
-
balances: data.balance_infos.map((b) => ({
|
|
518
|
-
currency: b.currency,
|
|
519
|
-
totalBalance: Number(b.total_balance),
|
|
520
|
-
grantedBalance: Number(b.granted_balance),
|
|
521
|
-
toppedUpBalance: Number(b.topped_up_balance)
|
|
522
|
-
}))
|
|
523
|
-
};
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
// src/provider/deepseek.ts
|
|
527
|
-
function intoDeepSeekMessages(messages) {
|
|
528
|
-
return messages.map((msg) => {
|
|
529
|
-
const mapped = {
|
|
530
|
-
role: msg.role,
|
|
531
|
-
content: msg.content || null
|
|
532
|
-
};
|
|
533
|
-
if (msg.name) mapped.name = msg.name;
|
|
534
|
-
if (msg.toolCallId) mapped.tool_call_id = msg.toolCallId;
|
|
535
|
-
if (msg.toolCalls && msg.toolCalls.length > 0) {
|
|
536
|
-
mapped.tool_calls = msg.toolCalls.map((tc) => ({
|
|
537
|
-
id: tc.id,
|
|
538
|
-
type: "function",
|
|
539
|
-
function: {
|
|
540
|
-
name: tc.name,
|
|
541
|
-
arguments: tc.arguments
|
|
542
|
-
}
|
|
543
|
-
}));
|
|
544
|
-
}
|
|
545
|
-
return mapped;
|
|
546
|
-
});
|
|
547
|
-
}
|
|
548
|
-
var DeepSeekEventMapper = class {
|
|
549
|
-
#toolCallsByIndex = /* @__PURE__ */ new Map();
|
|
550
|
-
/**
|
|
551
|
-
* 重置内部状态(每次新请求前调用)。
|
|
552
|
-
*/
|
|
553
|
-
reset() {
|
|
554
|
-
this.#toolCallsByIndex.clear();
|
|
555
|
-
}
|
|
556
|
-
/**
|
|
557
|
-
* 将 DeepSeek 原始流式块映射为 ChatChunk 数组。
|
|
558
|
-
* 一个 SSE 数据块可能生成 0~N 个 ChatChunk 事件。
|
|
559
|
-
*/
|
|
560
|
-
mapEvent(chunk) {
|
|
561
|
-
const choice = chunk.choices?.[0];
|
|
562
|
-
if (!choice) return [];
|
|
563
|
-
const events = [];
|
|
564
|
-
const delta = choice.delta;
|
|
565
|
-
const content = delta?.content ?? "";
|
|
566
|
-
const reasoningContent = delta?.reasoning_content ?? "";
|
|
567
|
-
const finishReason = this.#mapFinishReason(choice.finish_reason);
|
|
568
|
-
if (delta?.tool_calls) {
|
|
569
|
-
for (const tc of delta.tool_calls) {
|
|
570
|
-
this.#accumulateToolCall(tc);
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
let usage;
|
|
574
|
-
if (chunk.usage) {
|
|
575
|
-
usage = {
|
|
576
|
-
promptTokens: chunk.usage.prompt_tokens,
|
|
577
|
-
completionTokens: chunk.usage.completion_tokens,
|
|
578
|
-
cachedPromptTokens: chunk.usage.prompt_cache_hit_tokens
|
|
579
|
-
};
|
|
580
|
-
}
|
|
581
|
-
const shouldYieldToolCalls = finishReason === "tool_calls" && this.#toolCallsByIndex.size > 0;
|
|
582
|
-
if (!content && !reasoningContent && finishReason === null && !shouldYieldToolCalls && !usage) {
|
|
583
|
-
return [];
|
|
584
|
-
}
|
|
585
|
-
events.push({
|
|
586
|
-
content,
|
|
587
|
-
...reasoningContent ? { reasoningContent } : {},
|
|
588
|
-
finishReason,
|
|
589
|
-
...shouldYieldToolCalls ? { toolCalls: [...this.#toolCallsByIndex.values()] } : {},
|
|
590
|
-
...usage ? { usage } : {}
|
|
591
|
-
});
|
|
592
|
-
if (shouldYieldToolCalls) {
|
|
593
|
-
this.#toolCallsByIndex.clear();
|
|
594
|
-
}
|
|
595
|
-
return events;
|
|
596
|
-
}
|
|
597
|
-
/** 将 DeepSeek 的 finish_reason 映射为内部类型 */
|
|
598
|
-
#mapFinishReason(reason) {
|
|
599
|
-
switch (reason) {
|
|
600
|
-
case "stop":
|
|
601
|
-
return "stop";
|
|
602
|
-
case "tool_calls":
|
|
603
|
-
return "tool_calls";
|
|
604
|
-
case "length":
|
|
605
|
-
return "length";
|
|
606
|
-
default:
|
|
607
|
-
return null;
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
/** 累积一个工具调用分块 */
|
|
611
|
-
#accumulateToolCall(tc) {
|
|
612
|
-
const idx = tc.index ?? 0;
|
|
613
|
-
const existing = this.#toolCallsByIndex.get(idx);
|
|
614
|
-
if (!existing) {
|
|
615
|
-
this.#toolCallsByIndex.set(idx, {
|
|
616
|
-
id: tc.id ?? "",
|
|
617
|
-
name: tc.function?.name ?? "",
|
|
618
|
-
arguments: tc.function?.arguments ?? ""
|
|
619
|
-
});
|
|
620
|
-
} else {
|
|
621
|
-
if (tc.id) existing.id = tc.id;
|
|
622
|
-
if (tc.function?.name) existing.name = tc.function.name;
|
|
623
|
-
if (tc.function?.arguments) {
|
|
624
|
-
existing.arguments += tc.function.arguments;
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
};
|
|
629
|
-
var DeepSeekProvider = class {
|
|
630
|
-
#apiKey;
|
|
631
|
-
#baseUrl;
|
|
632
|
-
#model;
|
|
633
|
-
#client;
|
|
634
|
-
#idleTimeoutMs;
|
|
635
|
-
#mapper = new DeepSeekEventMapper();
|
|
636
|
-
name = "deepseek";
|
|
637
|
-
constructor(config) {
|
|
638
|
-
this.#apiKey = config.apiKey;
|
|
639
|
-
this.#baseUrl = config.baseUrl.replace(/\/+$/, "");
|
|
640
|
-
this.#model = config.model;
|
|
641
|
-
this.#idleTimeoutMs = config.idleTimeoutMs ?? 6e4;
|
|
642
|
-
this.#client = new HttpClient({
|
|
643
|
-
connectTimeoutMs: config.connectTimeoutMs,
|
|
644
|
-
maxRetries: config.maxRetries,
|
|
645
|
-
retryBaseDelayMs: config.retryBaseDelayMs,
|
|
646
|
-
retryMaxDelayMs: config.retryMaxDelayMs
|
|
647
|
-
});
|
|
648
|
-
}
|
|
649
|
-
model() {
|
|
650
|
-
return this.#model;
|
|
651
|
-
}
|
|
652
|
-
countTokens(text) {
|
|
653
|
-
return estimateTokens(text);
|
|
654
|
-
}
|
|
655
|
-
/**
|
|
656
|
-
* 查询账户余额。
|
|
657
|
-
*/
|
|
658
|
-
async getBalance() {
|
|
659
|
-
return getBalance(this.#client, this.#baseUrl, this.#apiKey);
|
|
660
|
-
}
|
|
661
|
-
/**
|
|
662
|
-
* 发起聊天补全请求(流式)。
|
|
663
|
-
*/
|
|
664
|
-
async *chat(messages, opts) {
|
|
665
|
-
const apiMessages = intoDeepSeekMessages(messages);
|
|
666
|
-
const request = {
|
|
667
|
-
model: this.#model,
|
|
668
|
-
messages: apiMessages,
|
|
669
|
-
stream: true,
|
|
670
|
-
max_tokens: opts?.maxTokens,
|
|
671
|
-
temperature: opts?.temperature,
|
|
672
|
-
// 从 ChatOptions 映射 thinking / reasoning_effort
|
|
673
|
-
thinking: opts?.thinkingAllowed !== void 0 ? { type: opts.thinkingAllowed ? "enabled" : "disabled" } : void 0,
|
|
674
|
-
reasoning_effort: opts?.thinkingEffort,
|
|
675
|
-
// 从 ChatOptions 映射 response_format
|
|
676
|
-
response_format: opts?.responseFormat ? { type: opts.responseFormat } : void 0,
|
|
677
|
-
// 从 ChatOptions 映射 tool_choice
|
|
678
|
-
tool_choice: opts?.toolChoice
|
|
679
|
-
};
|
|
680
|
-
if (opts?.tools && opts.tools.length > 0) {
|
|
681
|
-
request.tools = opts.tools;
|
|
682
|
-
}
|
|
683
|
-
this.#mapper.reset();
|
|
684
|
-
const stream = streamCompletion(this.#client, this.#baseUrl, this.#apiKey, request, {
|
|
685
|
-
signal: opts?.signal,
|
|
686
|
-
idleTimeoutMs: this.#idleTimeoutMs
|
|
687
|
-
});
|
|
688
|
-
for await (const rawChunk of stream) {
|
|
689
|
-
const events = this.#mapper.mapEvent(rawChunk);
|
|
690
|
-
for (const event of events) {
|
|
691
|
-
yield event;
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
};
|
|
696
|
-
|
|
697
|
-
export {
|
|
698
|
-
SUPPORTED_MODELS,
|
|
699
|
-
isSupportedModel,
|
|
700
|
-
getModelMeta,
|
|
701
|
-
estimateTokens,
|
|
702
|
-
calculateCost,
|
|
703
|
-
ModelNotSupportedError,
|
|
704
|
-
DeepSeekEventMapper,
|
|
705
|
-
DeepSeekProvider
|
|
706
|
-
};
|
|
707
|
-
//# sourceMappingURL=chunk-LWOBXEMJ.js.map
|