dskcode 0.1.10 → 0.1.12
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-DIZMSV5H.js +85 -0
- package/dist/chunk-DIZMSV5H.js.map +1 -0
- package/dist/chunk-FODNQIT4.js +626 -0
- package/dist/chunk-FODNQIT4.js.map +1 -0
- package/dist/deepseek-OIJOG3N5.js +8 -0
- package/dist/deepseek-OIJOG3N5.js.map +1 -0
- package/dist/index.js +1484 -181
- package/dist/index.js.map +1 -1
- package/dist/models-NQGNKB4T.js +19 -0
- package/dist/models-NQGNKB4T.js.map +1 -0
- package/package.json +1 -1
- package/dist/deepseek-7Y2G6EKM.js +0 -385
- package/dist/deepseek-7Y2G6EKM.js.map +0 -1
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SUPPORTED_MODELS,
|
|
3
|
+
SUPPORTED_MODEL_IDS,
|
|
4
|
+
calculateCost,
|
|
5
|
+
estimateTokens,
|
|
6
|
+
formatCost,
|
|
7
|
+
getModelMeta,
|
|
8
|
+
isSupportedModel
|
|
9
|
+
} from "./chunk-DIZMSV5H.js";
|
|
10
|
+
export {
|
|
11
|
+
SUPPORTED_MODELS,
|
|
12
|
+
SUPPORTED_MODEL_IDS,
|
|
13
|
+
calculateCost,
|
|
14
|
+
estimateTokens,
|
|
15
|
+
formatCost,
|
|
16
|
+
getModelMeta,
|
|
17
|
+
isSupportedModel
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=models-NQGNKB4T.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
CHANGED
|
@@ -1,385 +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 getModelMeta(model) {
|
|
30
|
-
return SUPPORTED_MODELS[model];
|
|
31
|
-
}
|
|
32
|
-
function isCJK(char) {
|
|
33
|
-
const code = char.codePointAt(0);
|
|
34
|
-
return code >= 19968 && code <= 40959 || // CJK 扩展 A
|
|
35
|
-
code >= 13312 && code <= 19903 || // CJK 扩展 B 及其他
|
|
36
|
-
code >= 131072 && code <= 173791 || // CJK 兼容汉字
|
|
37
|
-
code >= 63744 && code <= 64255 || // 全角字符
|
|
38
|
-
code >= 65281 && code <= 65376;
|
|
39
|
-
}
|
|
40
|
-
function estimateTokens(text) {
|
|
41
|
-
let cjkCount = 0;
|
|
42
|
-
let otherCount = 0;
|
|
43
|
-
for (const char of text) {
|
|
44
|
-
if (isCJK(char)) {
|
|
45
|
-
cjkCount++;
|
|
46
|
-
} else {
|
|
47
|
-
otherCount++;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
const tokens = cjkCount * 0.6 + otherCount * 0.3;
|
|
51
|
-
return Math.max(1, Math.ceil(tokens));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// src/provider/errors.ts
|
|
55
|
-
var ProviderError = class extends Error {
|
|
56
|
-
constructor(message, code, statusCode) {
|
|
57
|
-
super(message);
|
|
58
|
-
this.code = code;
|
|
59
|
-
this.statusCode = statusCode;
|
|
60
|
-
this.name = "ProviderError";
|
|
61
|
-
}
|
|
62
|
-
code;
|
|
63
|
-
statusCode;
|
|
64
|
-
};
|
|
65
|
-
var AuthError = class extends ProviderError {
|
|
66
|
-
constructor(message, statusCode) {
|
|
67
|
-
super(message, "AUTH_ERROR", statusCode);
|
|
68
|
-
this.name = "AuthError";
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
var RateLimitError = class extends ProviderError {
|
|
72
|
-
/** 建议等待的毫秒数 */
|
|
73
|
-
retryAfterMs;
|
|
74
|
-
constructor(message, retryAfterMs) {
|
|
75
|
-
super(message, "RATE_LIMIT", 429);
|
|
76
|
-
this.name = "RateLimitError";
|
|
77
|
-
this.retryAfterMs = retryAfterMs;
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
var ServerError = class extends ProviderError {
|
|
81
|
-
constructor(message, statusCode) {
|
|
82
|
-
super(message, "SERVER_ERROR", statusCode);
|
|
83
|
-
this.name = "ServerError";
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
var NetworkError = class extends ProviderError {
|
|
87
|
-
/** 导致网络错误的底层错误 */
|
|
88
|
-
originalError;
|
|
89
|
-
constructor(message, originalError) {
|
|
90
|
-
super(message, "NETWORK_ERROR");
|
|
91
|
-
this.name = "NetworkError";
|
|
92
|
-
this.originalError = originalError;
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
function mapHttpError(status, body) {
|
|
96
|
-
switch (status) {
|
|
97
|
-
case 401:
|
|
98
|
-
case 403:
|
|
99
|
-
return new AuthError(
|
|
100
|
-
`\u8BA4\u8BC1\u5931\u8D25\uFF1A\u8BF7\u68C0\u67E5 API Key \u662F\u5426\u6B63\u786E\u3002(${status})`,
|
|
101
|
-
status
|
|
102
|
-
);
|
|
103
|
-
case 429: {
|
|
104
|
-
let retryAfterMs;
|
|
105
|
-
try {
|
|
106
|
-
const parsed = JSON.parse(body);
|
|
107
|
-
const msg = parsed.error?.message ?? "";
|
|
108
|
-
const match = /(\d+)\s*second/i.exec(msg);
|
|
109
|
-
if (match?.[1]) {
|
|
110
|
-
retryAfterMs = Number(match[1]) * 1e3;
|
|
111
|
-
}
|
|
112
|
-
} catch {
|
|
113
|
-
}
|
|
114
|
-
return new RateLimitError(
|
|
115
|
-
`\u8BF7\u6C42\u8FC7\u4E8E\u9891\u7E41\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5\u3002${retryAfterMs ? `\u5EFA\u8BAE\u7B49\u5F85 ${Math.ceil(retryAfterMs / 1e3)} \u79D2\u3002` : ""}`,
|
|
116
|
-
retryAfterMs
|
|
117
|
-
);
|
|
118
|
-
}
|
|
119
|
-
case 400: {
|
|
120
|
-
let detail = "";
|
|
121
|
-
try {
|
|
122
|
-
const parsed = JSON.parse(body);
|
|
123
|
-
detail = parsed.error?.message ?? "";
|
|
124
|
-
} catch {
|
|
125
|
-
}
|
|
126
|
-
return new ProviderError(
|
|
127
|
-
`\u8BF7\u6C42\u53C2\u6570\u9519\u8BEF${detail ? `: ${detail}` : ""}`,
|
|
128
|
-
"BAD_REQUEST",
|
|
129
|
-
status
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
default:
|
|
133
|
-
if (status >= 500) {
|
|
134
|
-
return new ServerError(
|
|
135
|
-
`\u670D\u52A1\u7AEF\u9519\u8BEF (${status})\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5\u3002`,
|
|
136
|
-
status
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
return new ProviderError(`\u8BF7\u6C42\u5931\u8D25 (${status})`, "UNKNOWN_ERROR", status);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// src/provider/deepseek.ts
|
|
144
|
-
var DeepSeekProvider = class {
|
|
145
|
-
#apiKey;
|
|
146
|
-
#baseUrl;
|
|
147
|
-
#model;
|
|
148
|
-
name = "deepseek";
|
|
149
|
-
constructor(config) {
|
|
150
|
-
this.#apiKey = config.apiKey;
|
|
151
|
-
this.#baseUrl = config.baseUrl.replace(/\/+$/, "");
|
|
152
|
-
this.#model = config.model;
|
|
153
|
-
}
|
|
154
|
-
model() {
|
|
155
|
-
return this.#model;
|
|
156
|
-
}
|
|
157
|
-
countTokens(text) {
|
|
158
|
-
return estimateTokens(text);
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* 查询账户余额。
|
|
162
|
-
*
|
|
163
|
-
* 调用 DeepSeek /user/balance 接口,返回各币种的余额信息。
|
|
164
|
-
* 可用于在启动前检查 API Key 有效性和余额状态。
|
|
165
|
-
*/
|
|
166
|
-
async getBalance() {
|
|
167
|
-
const url = `${this.#baseUrl}/user/balance`;
|
|
168
|
-
let response;
|
|
169
|
-
try {
|
|
170
|
-
response = await fetch(url, {
|
|
171
|
-
method: "GET",
|
|
172
|
-
headers: {
|
|
173
|
-
Authorization: `Bearer ${this.#apiKey}`
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
} catch (err) {
|
|
177
|
-
throw new NetworkError(
|
|
178
|
-
`\u7F51\u7EDC\u9519\u8BEF\uFF1A\u65E0\u6CD5\u67E5\u8BE2\u4F59\u989D (${this.#baseUrl})`,
|
|
179
|
-
err instanceof Error ? err : void 0
|
|
180
|
-
);
|
|
181
|
-
}
|
|
182
|
-
if (!response.ok) {
|
|
183
|
-
const responseBody = await response.text().catch(() => "");
|
|
184
|
-
throw mapHttpError(response.status, responseBody);
|
|
185
|
-
}
|
|
186
|
-
const data = await response.json();
|
|
187
|
-
return {
|
|
188
|
-
isAvailable: data.is_available,
|
|
189
|
-
balances: data.balance_infos.map((b) => ({
|
|
190
|
-
currency: b.currency,
|
|
191
|
-
totalBalance: Number(b.total_balance),
|
|
192
|
-
grantedBalance: Number(b.granted_balance),
|
|
193
|
-
toppedUpBalance: Number(b.topped_up_balance)
|
|
194
|
-
}))
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* 发起聊天补全请求(流式)。
|
|
199
|
-
*
|
|
200
|
-
* 通过原生 fetch 调用 DeepSeek API,解析 SSE 事件流,
|
|
201
|
-
* 以 AsyncGenerator 的形式逐步 yield ChatChunk。
|
|
202
|
-
*/
|
|
203
|
-
async *chat(messages, opts) {
|
|
204
|
-
const url = `${this.#baseUrl}/chat/completions`;
|
|
205
|
-
const meta = getModelMeta(this.#model);
|
|
206
|
-
const apiMessages = this.#mapMessages(messages);
|
|
207
|
-
const body = {
|
|
208
|
-
model: this.#model,
|
|
209
|
-
messages: apiMessages,
|
|
210
|
-
stream: true,
|
|
211
|
-
max_tokens: opts?.maxTokens,
|
|
212
|
-
temperature: opts?.temperature
|
|
213
|
-
};
|
|
214
|
-
for (const key of Object.keys(body)) {
|
|
215
|
-
if (body[key] === void 0) {
|
|
216
|
-
delete body[key];
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
let response;
|
|
220
|
-
try {
|
|
221
|
-
response = await fetch(url, {
|
|
222
|
-
method: "POST",
|
|
223
|
-
headers: {
|
|
224
|
-
"Content-Type": "application/json",
|
|
225
|
-
Authorization: `Bearer ${this.#apiKey}`
|
|
226
|
-
},
|
|
227
|
-
body: JSON.stringify(body),
|
|
228
|
-
signal: opts?.signal
|
|
229
|
-
});
|
|
230
|
-
} catch (err) {
|
|
231
|
-
if (opts?.signal?.aborted) {
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
throw new NetworkError(
|
|
235
|
-
`\u7F51\u7EDC\u9519\u8BEF\uFF1A\u65E0\u6CD5\u8FDE\u63A5\u5230 DeepSeek API (${this.#baseUrl})`,
|
|
236
|
-
err instanceof Error ? err : void 0
|
|
237
|
-
);
|
|
238
|
-
}
|
|
239
|
-
if (!response.ok) {
|
|
240
|
-
const responseBody = await response.text().catch(() => "");
|
|
241
|
-
throw mapHttpError(response.status, responseBody);
|
|
242
|
-
}
|
|
243
|
-
yield* this.#parseStream(response);
|
|
244
|
-
}
|
|
245
|
-
// -----------------------------------------------------------------------
|
|
246
|
-
// 内部方法
|
|
247
|
-
// -----------------------------------------------------------------------
|
|
248
|
-
/**
|
|
249
|
-
* 将内部 ChatMessage 数组映射为 DeepSeek API 的请求消息格式。
|
|
250
|
-
*/
|
|
251
|
-
#mapMessages(messages) {
|
|
252
|
-
return messages.map((msg) => {
|
|
253
|
-
const mapped = {
|
|
254
|
-
role: msg.role,
|
|
255
|
-
content: msg.content || null
|
|
256
|
-
};
|
|
257
|
-
if (msg.name) {
|
|
258
|
-
mapped.name = msg.name;
|
|
259
|
-
}
|
|
260
|
-
if (msg.toolCallId) {
|
|
261
|
-
mapped.tool_call_id = msg.toolCallId;
|
|
262
|
-
}
|
|
263
|
-
if (msg.toolCalls && msg.toolCalls.length > 0) {
|
|
264
|
-
mapped.tool_calls = msg.toolCalls.map((tc) => ({
|
|
265
|
-
id: tc.id,
|
|
266
|
-
type: "function",
|
|
267
|
-
function: {
|
|
268
|
-
name: tc.name,
|
|
269
|
-
arguments: tc.arguments
|
|
270
|
-
}
|
|
271
|
-
}));
|
|
272
|
-
}
|
|
273
|
-
return mapped;
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
/**
|
|
277
|
-
* 解析 SSE 事件流。
|
|
278
|
-
*
|
|
279
|
-
* 策略:
|
|
280
|
-
* - 文本内容增量立即 yield(支持实时流式渲染)
|
|
281
|
-
* - 工具调用在多个 SSE 块中逐步到达,内部累积拼接
|
|
282
|
-
* - 当 finishReason 为 "tool_calls" 时,yield 完整的工具调用列表
|
|
283
|
-
* - 最后一个块通常包含 usage 统计信息
|
|
284
|
-
*/
|
|
285
|
-
async *#parseStream(response) {
|
|
286
|
-
const reader = response.body?.getReader();
|
|
287
|
-
if (!reader) {
|
|
288
|
-
throw new ProviderError("\u54CD\u5E94\u4F53\u4E3A\u7A7A", "EMPTY_RESPONSE");
|
|
289
|
-
}
|
|
290
|
-
const decoder = new TextDecoder();
|
|
291
|
-
let buffer = "";
|
|
292
|
-
const toolCallAccumulator = /* @__PURE__ */ new Map();
|
|
293
|
-
try {
|
|
294
|
-
while (true) {
|
|
295
|
-
const { done, value } = await reader.read();
|
|
296
|
-
if (done) break;
|
|
297
|
-
buffer += decoder.decode(value, { stream: true });
|
|
298
|
-
const lines = buffer.split("\n");
|
|
299
|
-
buffer = lines.pop() ?? "";
|
|
300
|
-
for (const line of lines) {
|
|
301
|
-
const trimmed = line.trim();
|
|
302
|
-
if (!trimmed || trimmed.startsWith(":")) continue;
|
|
303
|
-
if (!trimmed.startsWith("data: ")) continue;
|
|
304
|
-
const data = trimmed.slice(6);
|
|
305
|
-
if (data === "[DONE]") return;
|
|
306
|
-
let chunk;
|
|
307
|
-
try {
|
|
308
|
-
chunk = JSON.parse(data);
|
|
309
|
-
} catch {
|
|
310
|
-
continue;
|
|
311
|
-
}
|
|
312
|
-
const choice = chunk.choices?.[0];
|
|
313
|
-
if (!choice) continue;
|
|
314
|
-
const delta = choice.delta;
|
|
315
|
-
const content = delta?.content ?? "";
|
|
316
|
-
const finishReason = choice.finish_reason;
|
|
317
|
-
if (delta?.tool_calls) {
|
|
318
|
-
for (const tc of delta.tool_calls) {
|
|
319
|
-
const idx = tc.index ?? 0;
|
|
320
|
-
const existing = toolCallAccumulator.get(idx);
|
|
321
|
-
if (!existing) {
|
|
322
|
-
toolCallAccumulator.set(idx, {
|
|
323
|
-
id: tc.id ?? "",
|
|
324
|
-
name: tc.function?.name ?? "",
|
|
325
|
-
arguments: tc.function?.arguments ?? ""
|
|
326
|
-
});
|
|
327
|
-
} else {
|
|
328
|
-
if (tc.id) existing.id = tc.id;
|
|
329
|
-
if (tc.function?.name) existing.name = tc.function.name;
|
|
330
|
-
if (tc.function?.arguments) {
|
|
331
|
-
existing.arguments += tc.function.arguments;
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
let usage;
|
|
337
|
-
if (chunk.usage) {
|
|
338
|
-
usage = {
|
|
339
|
-
promptTokens: chunk.usage.prompt_tokens,
|
|
340
|
-
completionTokens: chunk.usage.completion_tokens,
|
|
341
|
-
cachedPromptTokens: chunk.usage.prompt_cache_hit_tokens
|
|
342
|
-
};
|
|
343
|
-
}
|
|
344
|
-
const mappedFinishReason = this.#mapFinishReason(finishReason);
|
|
345
|
-
const shouldYieldToolCalls = mappedFinishReason === "tool_calls" && toolCallAccumulator.size > 0;
|
|
346
|
-
if (!content && mappedFinishReason === null && !shouldYieldToolCalls && !usage) {
|
|
347
|
-
continue;
|
|
348
|
-
}
|
|
349
|
-
yield {
|
|
350
|
-
content,
|
|
351
|
-
finishReason: mappedFinishReason,
|
|
352
|
-
...shouldYieldToolCalls ? {
|
|
353
|
-
toolCalls: [...toolCallAccumulator.values()]
|
|
354
|
-
} : {},
|
|
355
|
-
...usage ? { usage } : {}
|
|
356
|
-
};
|
|
357
|
-
if (shouldYieldToolCalls) {
|
|
358
|
-
toolCallAccumulator.clear();
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
} finally {
|
|
363
|
-
reader.releaseLock();
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
/**
|
|
367
|
-
* 将 DeepSeek API 的 finish_reason 映射为内部类型。
|
|
368
|
-
*/
|
|
369
|
-
#mapFinishReason(reason) {
|
|
370
|
-
switch (reason) {
|
|
371
|
-
case "stop":
|
|
372
|
-
return "stop";
|
|
373
|
-
case "tool_calls":
|
|
374
|
-
return "tool_calls";
|
|
375
|
-
case "length":
|
|
376
|
-
return "length";
|
|
377
|
-
default:
|
|
378
|
-
return null;
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
};
|
|
382
|
-
export {
|
|
383
|
-
DeepSeekProvider
|
|
384
|
-
};
|
|
385
|
-
//# sourceMappingURL=deepseek-7Y2G6EKM.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/provider/models.ts","../src/provider/errors.ts","../src/provider/deepseek.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\r\n// 模型定义与校验\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type { ModelId, ModelMeta } from \"./types.js\";\r\n\r\n/** 支持的模型及其元数据 */\r\nexport const SUPPORTED_MODELS: Record<ModelId, ModelMeta> = {\r\n \"deepseek-v4-flash\": {\r\n id: \"deepseek-v4-flash\",\r\n displayName: \"DeepSeek V4 Flash\",\r\n /** 上下文窗口大小(token) */\r\n contextWindow: 1_000_000,\r\n /** 输入价格(元 / 百万 token,缓存未命中) */\r\n inputPricePerMillion: 1,\r\n /** 输出价格(元 / 百万 token) */\r\n outputPricePerMillion: 2,\r\n /** 输入价格(元 / 百万 token,缓存命中) */\r\n cacheHitPricePerMillion: 0.02,\r\n },\r\n \"deepseek-v4-pro\": {\r\n id: \"deepseek-v4-pro\",\r\n displayName: \"DeepSeek V4 Pro\",\r\n /** 上下文窗口大小(token) */\r\n contextWindow: 1_000_000,\r\n /** 输入价格(元 / 百万 token,缓存未命中) */\r\n inputPricePerMillion: 3,\r\n /** 输出价格(元 / 百万 token) */\r\n outputPricePerMillion: 6,\r\n /** 输入价格(元 / 百万 token,缓存命中) */\r\n cacheHitPricePerMillion: 0.025,\r\n },\r\n};\r\n\r\n/** 支持的模型 ID 列表 */\r\nexport const SUPPORTED_MODEL_IDS: string[] = Object.keys(SUPPORTED_MODELS);\r\n\r\n/**\r\n * 判断模型标识是否受支持。\r\n * dskcode 仅支持 deepseek-v4-flash 和 deepseek-v4-pro 两个模型。\r\n */\r\nexport function isSupportedModel(model: string): model is ModelId {\r\n return model in SUPPORTED_MODELS;\r\n}\r\n\r\n/** 获取模型元数据(已校验) */\r\nexport function getModelMeta(model: ModelId): ModelMeta {\r\n return SUPPORTED_MODELS[model];\r\n}\r\n\r\n/**\r\n * CJK 字符 Unicode 范围检测。\r\n * 覆盖 CJK 统一汉字、扩展 A/B、兼容汉字等。\r\n */\r\nfunction isCJK(char: string): boolean {\r\n const code = char.codePointAt(0)!;\r\n // CJK 统一汉字\r\n return (\r\n (code >= 0x4e00 && code <= 0x9fff) ||\r\n // CJK 扩展 A\r\n (code >= 0x3400 && code <= 0x4dbf) ||\r\n // CJK 扩展 B 及其他\r\n (code >= 0x20000 && code <= 0x2a6df) ||\r\n // CJK 兼容汉字\r\n (code >= 0xf900 && code <= 0xfaff) ||\r\n // 全角字符\r\n (code >= 0xff01 && code <= 0xff60)\r\n );\r\n}\r\n\r\n/**\r\n * 估算文本的 token 数量。\r\n *\r\n * 根据 DeepSeek 官方文档的换算比例:\r\n * - 1 个英文字符 ≈ 0.3 个 token(即约 3.3 个字符 ≈ 1 token)\r\n * - 1 个中文字符 ≈ 0.6 个 token(即约 1.7 个字符 ≈ 1 token)\r\n *\r\n * 对文本中的 CJK 和非 CJK 字符分别估算后求和。\r\n */\r\nexport function estimateTokens(text: string): number {\r\n let cjkCount = 0;\r\n let otherCount = 0;\r\n for (const char of text) {\r\n if (isCJK(char)) {\r\n cjkCount++;\r\n } else {\r\n otherCount++;\r\n }\r\n }\r\n // CJK: 1 字符 ≈ 0.6 token → token 数 ≈ cjkCount × 0.6\r\n // 其他: 1 字符 ≈ 0.3 token → token 数 ≈ otherCount × 0.3\r\n const tokens = cjkCount * 0.6 + otherCount * 0.3;\r\n return Math.max(1, Math.ceil(tokens));\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 费用计算\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type { UsageInfo, CostInfo } from \"./types.js\";\r\n\r\n/**\r\n * 根据模型和 Token 使用量计算费用。\r\n *\r\n * 计费公式:\r\n * 输入费用(缓存未命中) = (promptTokens - cachedPromptTokens) × inputPrice / 1,000,000\r\n * 缓存命中费用 = cachedPromptTokens × cacheHitPrice / 1,000,000\r\n * 输出费用 = completionTokens × outputPrice / 1,000,000\r\n * 总费用 = 输入费用 + 缓存命中费用 + 输出费用\r\n *\r\n * @param usage Token 使用统计\r\n * @param model 模型标识\r\n * @returns 费用明细(单位:元)\r\n */\r\nexport function calculateCost(usage: UsageInfo, model: ModelId): CostInfo {\r\n const meta = SUPPORTED_MODELS[model];\r\n const cached = usage.cachedPromptTokens ?? 0;\r\n const nonCached = usage.promptTokens - cached;\r\n\r\n const inputCost = (nonCached * meta.inputPricePerMillion) / 1_000_000;\r\n const cacheHitCost = (cached * meta.cacheHitPricePerMillion) / 1_000_000;\r\n const outputCost = (usage.completionTokens * meta.outputPricePerMillion) / 1_000_000;\r\n\r\n return {\r\n inputCost,\r\n cacheHitCost,\r\n outputCost,\r\n totalCost: inputCost + cacheHitCost + outputCost,\r\n };\r\n}\r\n\r\n/**\r\n * 将费用格式化为易读的字符串,只展示合计费用。\r\n *\r\n * - 小于 0.01 元时显示到小数点后 6 位(如 ¥0.000032)\r\n * - 大于等于 0.01 元时显示到小数点后 4 位(如 ¥0.1234)\r\n */\r\nexport function formatCost(cost: CostInfo): string {\r\n const total = cost.totalCost;\r\n const formatted = total < 0.01 ? total.toFixed(6) : total.toFixed(4);\r\n return `≈¥${formatted}`;\r\n}","// ---------------------------------------------------------------------------\r\n// Provider 错误类型与 HTTP 状态映射\r\n// ---------------------------------------------------------------------------\r\n\r\n/** Provider 错误基类 */\r\nexport class ProviderError extends Error {\r\n constructor(\r\n message: string,\r\n public readonly code: string,\r\n public readonly statusCode?: number,\r\n ) {\r\n super(message);\r\n this.name = \"ProviderError\";\r\n }\r\n}\r\n\r\n/** 认证失败(401 / 403) */\r\nexport class AuthError extends ProviderError {\r\n constructor(message: string, statusCode?: number) {\r\n super(message, \"AUTH_ERROR\", statusCode);\r\n this.name = \"AuthError\";\r\n }\r\n}\r\n\r\n/** 速率限制(429) */\r\nexport class RateLimitError extends ProviderError {\r\n /** 建议等待的毫秒数 */\r\n public readonly retryAfterMs?: number;\r\n\r\n constructor(message: string, retryAfterMs?: number) {\r\n super(message, \"RATE_LIMIT\", 429);\r\n this.name = \"RateLimitError\";\r\n this.retryAfterMs = retryAfterMs;\r\n }\r\n}\r\n\r\n/** 服务端错误(5xx) */\r\nexport class ServerError extends ProviderError {\r\n constructor(message: string, statusCode?: number) {\r\n super(message, \"SERVER_ERROR\", statusCode);\r\n this.name = \"ServerError\";\r\n }\r\n}\r\n\r\n/** 网络连接错误 */\r\nexport class NetworkError extends ProviderError {\r\n /** 导致网络错误的底层错误 */\r\n readonly originalError?: Error;\r\n\r\n constructor(message: string, originalError?: Error) {\r\n super(message, \"NETWORK_ERROR\");\r\n this.name = \"NetworkError\";\r\n this.originalError = originalError;\r\n }\r\n}\r\n\r\n/** 不支持的模型 */\r\nexport class ModelNotSupportedError extends ProviderError {\r\n constructor(model: string) {\r\n super(\r\n `不支持的模型: \"${model}\"。dskcode 仅支持: deepseek-v4-flash, deepseek-v4-pro`,\r\n \"MODEL_NOT_SUPPORTED\",\r\n );\r\n this.name = \"ModelNotSupportedError\";\r\n }\r\n}\r\n\r\n/**\r\n * 将 HTTP 状态码映射为结构化错误。\r\n *\r\n * 覆盖 DeepSeek API 常见的错误码:\r\n * - 400 → 请求参数错误\r\n * - 401/403 → 认证失败\r\n * - 429 → 速率限制\r\n * - 5xx → 服务端错误\r\n */\r\nexport function mapHttpError(status: number, body: string): ProviderError {\r\n switch (status) {\r\n case 401:\r\n case 403:\r\n return new AuthError(\r\n `认证失败:请检查 API Key 是否正确。(${status})`,\r\n status,\r\n );\r\n\r\n case 429: {\r\n // 尝试从响应体中解析重试等待时间\r\n let retryAfterMs: number | undefined;\r\n try {\r\n const parsed = JSON.parse(body) as { error?: { message?: string } };\r\n const msg = parsed.error?.message ?? \"\";\r\n const match = /(\\d+)\\s*second/i.exec(msg);\r\n if (match?.[1]) {\r\n retryAfterMs = Number(match[1]) * 1000;\r\n }\r\n } catch {\r\n // 解析失败 — 不提取重试时间\r\n }\r\n return new RateLimitError(\r\n `请求过于频繁,请稍后再试。${retryAfterMs ? `建议等待 ${Math.ceil(retryAfterMs / 1000)} 秒。` : \"\"}`,\r\n retryAfterMs,\r\n );\r\n }\r\n\r\n case 400: {\r\n let detail = \"\";\r\n try {\r\n const parsed = JSON.parse(body) as { error?: { message?: string } };\r\n detail = parsed.error?.message ?? \"\";\r\n } catch {\r\n // 解析失败 — 不提取详情\r\n }\r\n return new ProviderError(\r\n `请求参数错误${detail ? `: ${detail}` : \"\"}`,\r\n \"BAD_REQUEST\",\r\n status,\r\n );\r\n }\r\n\r\n default:\r\n if (status >= 500) {\r\n return new ServerError(\r\n `服务端错误 (${status}),请稍后再试。`,\r\n status,\r\n );\r\n }\r\n return new ProviderError(`请求失败 (${status})`, \"UNKNOWN_ERROR\", status);\r\n }\r\n}","// ---------------------------------------------------------------------------\r\n// DeepSeek Provider — 适配 DeepSeek Chat Completions API\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type {\r\n ChatMessage,\r\n ChatOptions,\r\n ChatChunk,\r\n Provider,\r\n ProviderToolCall,\r\n UsageInfo,\r\n ModelId,\r\n} from \"./types.js\";\r\nimport { getModelMeta, estimateTokens } from \"./models.js\";\r\nimport { ProviderError, NetworkError, mapHttpError } from \"./errors.js\";\r\n\r\n// ---------------------------------------------------------------------------\r\n// DeepSeek API 响应类型\r\n// ---------------------------------------------------------------------------\r\n\r\n/** SSE 流中的 delta 块 */\r\ninterface StreamDelta {\r\n content?: string;\r\n tool_calls?: Array<{\r\n index?: number;\r\n id?: string;\r\n function?: {\r\n name?: string;\r\n arguments?: string;\r\n };\r\n }>;\r\n}\r\n\r\n/** SSE 流中的单个数据块 */\r\ninterface StreamChunk {\r\n id: string;\r\n object: string;\r\n created: number;\r\n model: string;\r\n choices: Array<{\r\n index: number;\r\n delta: StreamDelta;\r\n finish_reason: string | null;\r\n }>;\r\n usage?: {\r\n prompt_tokens: number;\r\n completion_tokens: number;\r\n /** DeepSeek Prefix Cache 命中的 token 数 */\r\n prompt_cache_hit_tokens?: number;\r\n };\r\n}\r\n\r\n/** 被累积的工具调用(跨多个 SSE 块拼接) */\r\ninterface AccumulatedToolCall {\r\n id: string;\r\n name: string;\r\n arguments: string;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// DeepSeekProvider\r\n// ---------------------------------------------------------------------------\r\n\r\n/** DeepSeek Provider 创建配置 */\r\nexport interface DeepSeekProviderConfig {\r\n apiKey: string;\r\n baseUrl: string;\r\n model: ModelId;\r\n}\r\n\r\n/**\r\n * DeepSeek LLM Provider 实现。\r\n *\r\n * 适配 DeepSeek Chat Completions API(兼容 OpenAI 格式),支持:\r\n * - 流式响应(SSE)\r\n * - Prefix Cache(DeepSeek 特色,缓存命中 token 半价)\r\n * - 仅 deepseek-v4-flash / deepseek-v4-pro 两个模型\r\n */\r\nexport class DeepSeekProvider implements Provider {\r\n readonly #apiKey: string;\r\n readonly #baseUrl: string;\r\n readonly #model: ModelId;\r\n\r\n readonly name = \"deepseek\";\r\n\r\n constructor(config: DeepSeekProviderConfig) {\r\n this.#apiKey = config.apiKey;\r\n this.#baseUrl = config.baseUrl.replace(/\\/+$/, \"\"); // 去掉末尾斜杠\r\n this.#model = config.model;\r\n }\r\n\r\n model(): string {\r\n return this.#model;\r\n }\r\n\r\n countTokens(text: string): number {\r\n return estimateTokens(text);\r\n }\r\n\r\n /**\r\n * 查询账户余额。\r\n *\r\n * 调用 DeepSeek /user/balance 接口,返回各币种的余额信息。\r\n * 可用于在启动前检查 API Key 有效性和余额状态。\r\n */\r\n async getBalance(): Promise<import(\"./types.js\").BalanceResult> {\r\n const url = `${this.#baseUrl}/user/balance`;\r\n\r\n let response: Response;\r\n try {\r\n response = await fetch(url, {\r\n method: \"GET\",\r\n headers: {\r\n Authorization: `Bearer ${this.#apiKey}`,\r\n },\r\n });\r\n } catch (err: unknown) {\r\n throw new NetworkError(\r\n `网络错误:无法查询余额 (${this.#baseUrl})`,\r\n err instanceof Error ? err : undefined,\r\n );\r\n }\r\n\r\n if (!response.ok) {\r\n const responseBody = await response.text().catch(() => \"\");\r\n throw mapHttpError(response.status, responseBody);\r\n }\r\n\r\n const data = await response.json() as {\r\n is_available: boolean;\r\n balance_infos: Array<{\r\n currency: string;\r\n total_balance: string;\r\n granted_balance: string;\r\n topped_up_balance: string;\r\n }>;\r\n };\r\n\r\n return {\r\n isAvailable: data.is_available,\r\n balances: data.balance_infos.map((b) => ({\r\n currency: b.currency,\r\n totalBalance: Number(b.total_balance),\r\n grantedBalance: Number(b.granted_balance),\r\n toppedUpBalance: Number(b.topped_up_balance),\r\n })),\r\n };\r\n }\r\n\r\n /**\r\n * 发起聊天补全请求(流式)。\r\n *\r\n * 通过原生 fetch 调用 DeepSeek API,解析 SSE 事件流,\r\n * 以 AsyncGenerator 的形式逐步 yield ChatChunk。\r\n */\r\n async *chat(\r\n messages: ChatMessage[],\r\n opts?: ChatOptions,\r\n ): AsyncIterable<ChatChunk> {\r\n const url = `${this.#baseUrl}/chat/completions`;\r\n const meta = getModelMeta(this.#model);\r\n\r\n // 将内部消息格式映射为 DeepSeek API 的请求格式\r\n const apiMessages = this.#mapMessages(messages);\r\n\r\n const body: Record<string, unknown> = {\r\n model: this.#model,\r\n messages: apiMessages,\r\n stream: true,\r\n max_tokens: opts?.maxTokens,\r\n temperature: opts?.temperature,\r\n };\r\n\r\n // 移除 undefined 字段(DeepSeek API 不接受 undefined 值)\r\n for (const key of Object.keys(body) as Array<keyof typeof body>) {\r\n if (body[key] === undefined) {\r\n delete body[key];\r\n }\r\n }\r\n\r\n let response: Response;\r\n try {\r\n response = await fetch(url, {\r\n method: \"POST\",\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n Authorization: `Bearer ${this.#apiKey}`,\r\n },\r\n body: JSON.stringify(body),\r\n signal: opts?.signal,\r\n });\r\n } catch (err: unknown) {\r\n if (opts?.signal?.aborted) {\r\n return; // 用户手动取消\r\n }\r\n throw new NetworkError(\r\n `网络错误:无法连接到 DeepSeek API (${this.#baseUrl})`,\r\n err instanceof Error ? err : undefined,\r\n );\r\n }\r\n\r\n // 处理非成功状态码\r\n if (!response.ok) {\r\n const responseBody = await response.text().catch(() => \"\");\r\n throw mapHttpError(response.status, responseBody);\r\n }\r\n\r\n // 解析 SSE 流(含工具调用累积)\r\n yield* this.#parseStream(response);\r\n }\r\n\r\n // -----------------------------------------------------------------------\r\n // 内部方法\r\n // -----------------------------------------------------------------------\r\n\r\n /**\r\n * 将内部 ChatMessage 数组映射为 DeepSeek API 的请求消息格式。\r\n */\r\n #mapMessages(messages: ChatMessage[]): Record<string, unknown>[] {\r\n return messages.map((msg) => {\r\n const mapped: Record<string, unknown> = {\r\n role: msg.role,\r\n content: msg.content || null,\r\n };\r\n\r\n if (msg.name) {\r\n mapped.name = msg.name;\r\n }\r\n\r\n if (msg.toolCallId) {\r\n mapped.tool_call_id = msg.toolCallId;\r\n }\r\n\r\n if (msg.toolCalls && msg.toolCalls.length > 0) {\r\n mapped.tool_calls = msg.toolCalls.map((tc) => ({\r\n id: tc.id,\r\n type: \"function\",\r\n function: {\r\n name: tc.name,\r\n arguments: tc.arguments,\r\n },\r\n }));\r\n }\r\n\r\n return mapped;\r\n });\r\n }\r\n\r\n /**\r\n * 解析 SSE 事件流。\r\n *\r\n * 策略:\r\n * - 文本内容增量立即 yield(支持实时流式渲染)\r\n * - 工具调用在多个 SSE 块中逐步到达,内部累积拼接\r\n * - 当 finishReason 为 \"tool_calls\" 时,yield 完整的工具调用列表\r\n * - 最后一个块通常包含 usage 统计信息\r\n */\r\n async *#parseStream(response: Response): AsyncIterable<ChatChunk> {\r\n const reader = response.body?.getReader();\r\n if (!reader) {\r\n throw new ProviderError(\"响应体为空\", \"EMPTY_RESPONSE\");\r\n }\r\n\r\n const decoder = new TextDecoder();\r\n let buffer = \"\";\r\n\r\n // 工具调用累积器:key = index(流中用 index 标识不同的 tool call)\r\n const toolCallAccumulator = new Map<number, AccumulatedToolCall>();\r\n\r\n try {\r\n while (true) {\r\n const { done, value } = await reader.read();\r\n if (done) break;\r\n\r\n buffer += decoder.decode(value, { stream: true });\r\n\r\n // 按行拆分处理 SSE 数据\r\n const lines = buffer.split(\"\\n\");\r\n // 保留最后一行(可能不完整)\r\n buffer = lines.pop() ?? \"\";\r\n\r\n for (const line of lines) {\r\n const trimmed = line.trim();\r\n\r\n // 跳过空行和 SSE 注释行\r\n if (!trimmed || trimmed.startsWith(\":\")) continue;\r\n\r\n // 处理 data: 开头的行\r\n if (!trimmed.startsWith(\"data: \")) continue;\r\n\r\n const data = trimmed.slice(6);\r\n\r\n // [DONE] 标记流结束\r\n if (data === \"[DONE]\") return;\r\n\r\n let chunk: StreamChunk;\r\n try {\r\n chunk = JSON.parse(data) as StreamChunk;\r\n } catch {\r\n // 无法解析的 JSON 行,跳过\r\n continue;\r\n }\r\n\r\n const choice = chunk.choices?.[0];\r\n if (!choice) continue;\r\n\r\n const delta = choice.delta;\r\n const content = delta?.content ?? \"\";\r\n const finishReason = choice.finish_reason;\r\n\r\n // 累积工具调用片段\r\n if (delta?.tool_calls) {\r\n for (const tc of delta.tool_calls) {\r\n const idx = tc.index ?? 0;\r\n const existing = toolCallAccumulator.get(idx);\r\n\r\n if (!existing) {\r\n // 新的工具调用开始\r\n toolCallAccumulator.set(idx, {\r\n id: tc.id ?? \"\",\r\n name: tc.function?.name ?? \"\",\r\n arguments: tc.function?.arguments ?? \"\",\r\n });\r\n } else {\r\n // 追加参数片段\r\n if (tc.id) existing.id = tc.id;\r\n if (tc.function?.name) existing.name = tc.function.name;\r\n if (tc.function?.arguments) {\r\n existing.arguments += tc.function.arguments;\r\n }\r\n }\r\n }\r\n }\r\n\r\n // 构建 usage 信息\r\n let usage: UsageInfo | undefined;\r\n if (chunk.usage) {\r\n usage = {\r\n promptTokens: chunk.usage.prompt_tokens,\r\n completionTokens: chunk.usage.completion_tokens,\r\n cachedPromptTokens: chunk.usage.prompt_cache_hit_tokens,\r\n };\r\n }\r\n\r\n // 确定完成原因\r\n const mappedFinishReason = this.#mapFinishReason(finishReason);\r\n\r\n // 当 finishReason 为 \"tool_calls\" 时,yield 完整的工具调用列表\r\n const shouldYieldToolCalls =\r\n mappedFinishReason === \"tool_calls\" && toolCallAccumulator.size > 0;\r\n\r\n // 不包含有效数据的块,跳过\r\n if (\r\n !content &&\r\n mappedFinishReason === null &&\r\n !shouldYieldToolCalls &&\r\n !usage\r\n ) {\r\n continue;\r\n }\r\n\r\n yield {\r\n content,\r\n finishReason: mappedFinishReason,\r\n ...(shouldYieldToolCalls\r\n ? {\r\n toolCalls: [...toolCallAccumulator.values()],\r\n }\r\n : {}),\r\n ...(usage ? { usage } : {}),\r\n };\r\n\r\n // 清理已 yield 的工具调用累积器\r\n if (shouldYieldToolCalls) {\r\n toolCallAccumulator.clear();\r\n }\r\n }\r\n }\r\n } finally {\r\n reader.releaseLock();\r\n }\r\n }\r\n\r\n /**\r\n * 将 DeepSeek API 的 finish_reason 映射为内部类型。\r\n */\r\n #mapFinishReason(\r\n reason: string | null | undefined,\r\n ): \"stop\" | \"tool_calls\" | \"length\" | null {\r\n switch (reason) {\r\n case \"stop\":\r\n return \"stop\";\r\n case \"tool_calls\":\r\n return \"tool_calls\";\r\n case \"length\":\r\n return \"length\";\r\n default:\r\n return null;\r\n }\r\n }\r\n}"],"mappings":"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","names":[]}
|