dskcode 0.1.21 → 0.1.23

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/package.json CHANGED
@@ -1,11 +1,17 @@
1
1
  {
2
2
  "name": "dskcode",
3
- "version": "0.1.21",
3
+ "version": "0.1.23",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/Awu12277/deepseek-agent-cli/tree/main"
7
7
  },
8
- "keywords": ["cli","ink","commander","deepseek","asciichart"],
8
+ "keywords": [
9
+ "cli",
10
+ "ink",
11
+ "commander",
12
+ "deepseek",
13
+ "asciichart"
14
+ ],
9
15
  "description": "A DeepSeek-native AI coding agent for your terminal",
10
16
  "type": "module",
11
17
  "bin": {
@@ -39,6 +45,7 @@
39
45
  "asciichart": "^1.5.25",
40
46
  "chalk": "^5.6.2",
41
47
  "commander": "^13.1.0",
48
+ "handlebars": "^4.7.9",
42
49
  "ink": "^7.1.0",
43
50
  "ink-spinner": "^5.0.0",
44
51
  "ink-text-input": "^6.0.0",
@@ -1,83 +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
- function formatCost(cost) {
71
- return `\u2248\xA5${cost.totalCost.toFixed(4)}`;
72
- }
73
-
74
- export {
75
- SUPPORTED_MODELS,
76
- SUPPORTED_MODEL_IDS,
77
- isSupportedModel,
78
- getModelMeta,
79
- estimateTokens,
80
- calculateCost,
81
- formatCost
82
- };
83
- //# sourceMappingURL=chunk-7QS2XBBX.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/provider/models.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 * 将费用格式化为易读的字符串,保留4位小数。\r\n */\r\nexport function formatCost(cost: CostInfo): string {\r\n return `≈¥${cost.totalCost.toFixed(4)}`;\r\n}"],"mappings":";AAOO,IAAM,mBAA+C;AAAA,EAC1D,qBAAqB;AAAA,IACnB,IAAI;AAAA,IACJ,aAAa;AAAA;AAAA,IAEb,eAAe;AAAA;AAAA,IAEf,sBAAsB;AAAA;AAAA,IAEtB,uBAAuB;AAAA;AAAA,IAEvB,yBAAyB;AAAA,EAC3B;AAAA,EACA,mBAAmB;AAAA,IACjB,IAAI;AAAA,IACJ,aAAa;AAAA;AAAA,IAEb,eAAe;AAAA;AAAA,IAEf,sBAAsB;AAAA;AAAA,IAEtB,uBAAuB;AAAA;AAAA,IAEvB,yBAAyB;AAAA,EAC3B;AACF;AAGO,IAAM,sBAAgC,OAAO,KAAK,gBAAgB;AAMlE,SAAS,iBAAiB,OAAiC;AAChE,SAAO,SAAS;AAClB;AAGO,SAAS,aAAa,OAA2B;AACtD,SAAO,iBAAiB,KAAK;AAC/B;AAMA,SAAS,MAAM,MAAuB;AACpC,QAAM,OAAO,KAAK,YAAY,CAAC;AAE/B,SACG,QAAQ,SAAU,QAAQ;AAAA,EAE1B,QAAQ,SAAU,QAAQ;AAAA,EAE1B,QAAQ,UAAW,QAAQ;AAAA,EAE3B,QAAQ,SAAU,QAAQ;AAAA,EAE1B,QAAQ,SAAU,QAAQ;AAE/B;AAWO,SAAS,eAAe,MAAsB;AACnD,MAAI,WAAW;AACf,MAAI,aAAa;AACjB,aAAW,QAAQ,MAAM;AACvB,QAAI,MAAM,IAAI,GAAG;AACf;AAAA,IACF,OAAO;AACL;AAAA,IACF;AAAA,EACF;AAGA,QAAM,SAAS,WAAW,MAAM,aAAa;AAC7C,SAAO,KAAK,IAAI,GAAG,KAAK,KAAK,MAAM,CAAC;AACtC;AAqBO,SAAS,cAAc,OAAkB,OAA0B;AACxE,QAAM,OAAO,iBAAiB,KAAK;AACnC,QAAM,SAAS,MAAM,sBAAsB;AAC3C,QAAM,YAAY,MAAM,eAAe;AAEvC,QAAM,YAAa,YAAY,KAAK,uBAAwB;AAC5D,QAAM,eAAgB,SAAS,KAAK,0BAA2B;AAC/D,QAAM,aAAc,MAAM,mBAAmB,KAAK,wBAAyB;AAE3E,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,YAAY,eAAe;AAAA,EACxC;AACF;AAKO,SAAS,WAAW,MAAwB;AACjD,SAAO,aAAK,KAAK,UAAU,QAAQ,CAAC,CAAC;AACvC;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/provider/errors.ts","../src/provider/retry.ts","../src/provider/client.ts","../src/provider/sse.ts","../src/provider/deepseek.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\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 TimeoutError extends ProviderError {\r\n /** 触发超时的等待时间(毫秒) */\r\n public readonly timeoutMs: number;\r\n\r\n constructor(message: string, timeoutMs: number) {\r\n super(message, \"TIMEOUT\");\r\n this.name = \"TimeoutError\";\r\n this.timeoutMs = timeoutMs;\r\n }\r\n}\r\n\r\n/** 流式空闲超时错误——两个 SSE 数据块之间间隔过长 */\r\nexport class StreamIdleTimeoutError extends ProviderError {\r\n /** 触发空闲超时的最大间隔(毫秒) */\r\n public readonly idleMs: number;\r\n\r\n constructor(message: string, idleMs: number) {\r\n super(message, \"STREAM_IDLE_TIMEOUT\");\r\n this.name = \"StreamIdleTimeoutError\";\r\n this.idleMs = idleMs;\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\r\n/**\r\n * 判断一个 ProviderError 是否可重试。\r\n *\r\n * 可重试的错误类型:\r\n * - RateLimitError(429)——服从 Retry-After,但也可退避\r\n * - ServerError(5xx)\r\n * - NetworkError(连接失败、DNS 错误等)\r\n */\r\nexport function isRetryableError(err: ProviderError): boolean {\r\n return (\r\n err instanceof RateLimitError ||\r\n err instanceof ServerError ||\r\n err instanceof NetworkError\r\n );\r\n}","// ---------------------------------------------------------------------------\r\n// 指数退避重试策略\r\n// ---------------------------------------------------------------------------\r\n//\r\n// 针对可重试错误(RateLimitError / ServerError / NetworkError)自动重试,\r\n// 重试间隔按指数退避 + 抖动增长。429 错误优先服从 Retry-After 提示。\r\n\r\nimport {\r\n ProviderError,\r\n RateLimitError,\r\n isRetryableError,\r\n} from \"./errors.js\";\r\n\r\n/** 重试策略配置 */\r\nexport interface RetryOptions {\r\n /** 最大重试次数(不含首次),默认 3 */\r\n maxRetries?: number;\r\n /** 指数退避基准延迟(毫秒),默认 1000 */\r\n baseDelayMs?: number;\r\n /** 单次重试最大延迟上限(毫秒),默认 30000 */\r\n maxDelayMs?: number;\r\n /**\r\n * 重试前的回调,可用于日志输出。\r\n * 收到 (attempt, error, delayMs) 三个参数。\r\n */\r\n onRetry?: (attempt: number, error: Error, delayMs: number) => void;\r\n}\r\n\r\n/** 默认重试配置 */\r\nexport const DEFAULT_RETRY_OPTIONS: Required<\r\n Omit<RetryOptions, \"onRetry\">\r\n> = {\r\n maxRetries: 3,\r\n baseDelayMs: 1000,\r\n maxDelayMs: 30_000,\r\n};\r\n\r\n/**\r\n * 计算第 attempt 次重试的退避延迟(毫秒)。\r\n *\r\n * 公式:base * 2^(attempt-1) + jitter\r\n * 其中 jitter 为 [0, base/2) 的随机抖动,避免重试洪峰。\r\n * 最终结果被 maxDelayMs 截断。\r\n *\r\n * @param attempt 重试序号,从 1 开始(第 1 次重试)\r\n */\r\nexport function computeBackoffDelay(\r\n attempt: number,\r\n baseDelayMs: number,\r\n maxDelayMs: number,\r\n): number {\r\n // 指数部分:1, 2, 4, 8, 16 ...\r\n const exp = Math.pow(2, attempt - 1);\r\n const jitter = Math.random() * (baseDelayMs / 2);\r\n const delay = baseDelayMs * exp + jitter;\r\n return Math.min(delay, maxDelayMs);\r\n}\r\n\r\n/**\r\n * 对一个异步操作执行指数退避重试。\r\n *\r\n * - 操作成功(返回值)→ 立即返回结果\r\n * - 抛出非 ProviderError → 立即向上抛出(不可重试)\r\n * - 抛出 ProviderError 且 isRetryableError → 按配置重试\r\n * - 超过 maxRetries 仍未成功 → 抛出最后一次的错误\r\n *\r\n * 对于 RateLimitError,若携带 retryAfterMs,则优先使用该值\r\n * 作为延迟(但仍受 maxDelayMs 截断约束)。\r\n *\r\n * @param fn 待执行的重试操作\r\n * @param options 重试配置\r\n */\r\nexport async function withRetry<T>(\r\n fn: () => Promise<T>,\r\n options: RetryOptions = {},\r\n): Promise<T> {\r\n const maxRetries = options.maxRetries ?? DEFAULT_RETRY_OPTIONS.maxRetries;\r\n const baseDelayMs =\r\n options.baseDelayMs ?? DEFAULT_RETRY_OPTIONS.baseDelayMs;\r\n const maxDelayMs =\r\n options.maxDelayMs ?? DEFAULT_RETRY_OPTIONS.maxDelayMs;\r\n\r\n let lastError: unknown;\r\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\r\n try {\r\n return await fn();\r\n } catch (err: unknown) {\r\n lastError = err;\r\n\r\n // 非可重试错误:立即抛出\r\n if (!(err instanceof ProviderError) || !isRetryableError(err)) {\r\n throw err;\r\n }\r\n\r\n // 已达最大尝试次数\r\n if (attempt >= maxRetries) break;\r\n\r\n // 计算延迟\r\n let delayMs: number;\r\n if (err instanceof RateLimitError && err.retryAfterMs !== undefined) {\r\n // 429 优先服从 Retry-After,但仍受 maxDelayMs 截断\r\n delayMs = Math.min(err.retryAfterMs, maxDelayMs);\r\n } else {\r\n delayMs = computeBackoffDelay(attempt + 1, baseDelayMs, maxDelayMs);\r\n }\r\n\r\n options.onRetry?.(attempt + 1, err, delayMs);\r\n await sleep(delayMs);\r\n }\r\n }\r\n // 理论上不会到达;保险起见抛出最后一次错误\r\n throw lastError instanceof Error\r\n ? lastError\r\n : new Error(\"withRetry: 未知错误\");\r\n}\r\n\r\n/** 可被外部替换的 sleep 函数,便于测试中加速 */\r\nexport let sleep = (ms: number): Promise<void> =>\r\n new Promise((resolve) => setTimeout(resolve, ms));\r\n\r\n/**\r\n * 替换模块内的 sleep 实现(仅供测试使用)。\r\n *\r\n * @param impl 自定义的 sleep 实现\r\n * @returns 一个函数,调用后恢复默认 sleep\r\n */\r\nexport function overrideSleep(\r\n impl: (ms: number) => Promise<void>,\r\n): () => void {\r\n const original = sleep;\r\n sleep = impl;\r\n return () => {\r\n sleep = original;\r\n };\r\n}","// ---------------------------------------------------------------------------\r\n// HttpClient — 通用 HTTP 传输层\r\n// ---------------------------------------------------------------------------\r\n//\r\n// 封装原生 fetch,提供:\r\n// - 连接超时(通过 AbortSignal.timeout 或外部 signal 合并实现)\r\n// - 流式空闲超时(由 SSE 解析器负责)\r\n// - 指数退避重试(针对 429 / 5xx)\r\n//\r\n// 与具体 Provider 解耦:Provider 负责「请求构造 / 响应映射」,\r\n// HttpClient 负责「传输稳定性」。\r\n\r\nimport type { ClientOptions } from \"./types.js\";\r\nimport {\r\n NetworkError,\r\n ProviderError,\r\n TimeoutError,\r\n mapHttpError,\r\n} from \"./errors.js\";\r\nimport { withRetry, type RetryOptions } from \"./retry.js\";\r\n\r\n/** HttpClient 配置 */\r\nexport interface HttpClientConfig extends ClientOptions {}\r\n\r\n/** 请求选项(扩展标准 fetch init) */\r\nexport interface RequestOptions {\r\n /** 外部中止信号,与连接超时信号合并 */\r\n signal?: AbortSignal;\r\n /**\r\n * 本次请求的连接超时(毫秒),覆盖 HttpClient 默认值。\r\n * 设为 0 表示不超时。\r\n */\r\n connectTimeoutMs?: number;\r\n /**\r\n * 本次请求的重试配置;为 false 则禁用重试。\r\n */\r\n retry?: RetryOptions | false;\r\n}\r\n\r\n/** 默认连接超时(30s) */\r\nconst DEFAULT_CONNECT_TIMEOUT_MS = 30_000;\r\n\r\n/**\r\n * 通用 HTTP 客户端。\r\n *\r\n * 不持有任何状态,可被多个 Provider 共享。\r\n */\r\nexport class HttpClient {\r\n readonly #connectTimeoutMs: number;\r\n readonly #maxRetries: number;\r\n readonly #retryBaseDelayMs: number;\r\n readonly #retryMaxDelayMs: number;\r\n\r\n constructor(config: HttpClientConfig = {}) {\r\n this.#connectTimeoutMs =\r\n config.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;\r\n this.#maxRetries = config.maxRetries ?? 3;\r\n this.#retryBaseDelayMs = config.retryBaseDelayMs ?? 1000;\r\n this.#retryMaxDelayMs = config.retryMaxDelayMs ?? 30_000;\r\n }\r\n\r\n /**\r\n * 发起单次 HTTP 请求(带连接超时,不重试)。\r\n *\r\n * - 自动附加 `Content-Type: application/json`(若 body 是字符串)\r\n * - 非 2xx 响应:抛出 mapHttpError 映射后的 ProviderError\r\n * - 网络错误:抛出 NetworkError\r\n * - 超时:抛出 TimeoutError\r\n *\r\n * @param url 请求 URL\r\n * @param init fetch 初始化(method/headers/body)\r\n * @param options 请求级选项(超时、中止信号)\r\n * @returns 成功(2xx)的 Response\r\n */\r\n async request(\r\n url: string,\r\n init: RequestInit = {},\r\n options: RequestOptions = {},\r\n ): Promise<Response> {\r\n const timeoutMs =\r\n options.connectTimeoutMs === undefined\r\n ? this.#connectTimeoutMs\r\n : options.connectTimeoutMs;\r\n\r\n // 合并 headers\r\n const headers = new Headers(init.headers);\r\n if (init.body && typeof init.body === \"string\" && !headers.has(\"Content-Type\")) {\r\n headers.set(\"Content-Type\", \"application/json\");\r\n }\r\n\r\n const { signal, cleanup } = this.#mergeSignals(\r\n options.signal,\r\n timeoutMs,\r\n );\r\n\r\n let response: Response;\r\n try {\r\n response = await fetch(url, {\r\n ...init,\r\n headers,\r\n signal,\r\n });\r\n } catch (err: unknown) {\r\n // 外部中止优先视为用户主动取消\r\n if (options.signal?.aborted) {\r\n throw new ProviderError(\"请求已取消\", \"ABORTED\");\r\n }\r\n // 超时(本客户端内部的超时信号先于外信号触发)\r\n if (signal?.aborted && !options.signal?.aborted) {\r\n throw new TimeoutError(\r\n `连接超时(${timeoutMs}ms): ${url}`,\r\n timeoutMs,\r\n );\r\n }\r\n throw new NetworkError(\r\n `网络错误:无法连接到 ${url}`,\r\n err instanceof Error ? err : undefined,\r\n );\r\n } finally {\r\n cleanup();\r\n }\r\n\r\n if (!response.ok) {\r\n const body = await response.text().catch(() => \"\");\r\n throw mapHttpError(response.status, body);\r\n }\r\n\r\n return response;\r\n }\r\n\r\n /**\r\n * 发起带重试的 HTTP 请求。\r\n *\r\n * 等价于 request,但用 withRetry 包装:429 / 5xx / 网络错误会自动重试。\r\n * 流式响应一旦获得 2xx,重试即停止,剩余流式稳定性交给 SSE 解析器。\r\n */\r\n async requestWithRetry(\r\n url: string,\r\n init: RequestInit = {},\r\n options: RequestOptions = {},\r\n ): Promise<Response> {\r\n const retryOptions: RetryOptions | undefined =\r\n options.retry === false\r\n ? { maxRetries: 0 }\r\n : (options.retry ?? {\r\n maxRetries: this.#maxRetries,\r\n baseDelayMs: this.#retryBaseDelayMs,\r\n maxDelayMs: this.#retryMaxDelayMs,\r\n });\r\n\r\n return withRetry(() => this.request(url, init, options), retryOptions);\r\n }\r\n\r\n /**\r\n * 合并外部 signal 与连接超时 signal。\r\n *\r\n * 策略:\r\n * - 若 timeoutMs <= 0 且无外部 signal → 不使用 signal\r\n * - 若已有外部 signal 且无超时 → 直接使用外部 signal\r\n * - 否则用 AbortSignal.any([外部, 超时]) 合并(Node 20+ 原生支持)\r\n *\r\n * @returns { signal, cleanup } 调用方应在 finally 调用 cleanup 取消监听\r\n */\r\n #mergeSignals(\r\n external: AbortSignal | undefined,\r\n timeoutMs: number,\r\n ): { signal: AbortSignal | undefined; cleanup: () => void } {\r\n const useTimeout = timeoutMs > 0;\r\n\r\n if (!external && !useTimeout) {\r\n return { signal: undefined, cleanup: () => {} };\r\n }\r\n if (!useTimeout) {\r\n return { signal: external, cleanup: () => {} };\r\n }\r\n\r\n const controller = new AbortController();\r\n const timer = setTimeout(() => controller.abort(), timeoutMs);\r\n\r\n // 监听外部 signal:触发时同步中止本控制器\r\n const onExternalAbort = () => controller.abort();\r\n if (external) {\r\n if (external.aborted) controller.abort();\r\n else external.addEventListener(\"abort\", onExternalAbort, { once: true });\r\n }\r\n\r\n return {\r\n signal: controller.signal,\r\n cleanup: () => {\r\n clearTimeout(timer);\r\n if (external) {\r\n external.removeEventListener(\"abort\", onExternalAbort);\r\n }\r\n },\r\n };\r\n }\r\n}","// ---------------------------------------------------------------------------\r\n// SSE(Server-Sent Events)流式解析器\r\n// ---------------------------------------------------------------------------\r\n//\r\n// 独立于 HTTP 传输的 SSE 协议解析模块。按照\r\n// [SSE 规范](https://html.spec.whatwg.org/multipage/server-sent-events.html)\r\n// 逐步解析事件流。\r\n//\r\n// 关键设计:\r\n// - 按 \\n 拆行,保留跨 chunk 的不完整行\r\n// - 同一事件内的多个 data: 块用 \\n 拼接\r\n// - 空行触发事件 emit\r\n// - 支持 idleTimeoutMs:单次 reader.read() 等待超时即抛 StreamIdleTimeoutError\r\n// - 支持外部 AbortSignal 中止读取\r\n\r\nimport type { SSEEvent } from \"./types.js\";\r\nimport { StreamIdleTimeoutError } from \"./errors.js\";\r\n\r\n/** parseSSE 的可选配置 */\r\nexport interface ParseSSEOptions {\r\n /**\r\n * 流式空闲超时(毫秒)。\r\n * 一次 reader.read() 等待超过此时间即抛出 StreamIdleTimeoutError。\r\n * 默认 60000。\r\n */\r\n idleTimeoutMs?: number;\r\n /**\r\n * 外部中止信号。中止后立即停止读取并释放 reader。\r\n */\r\n signal?: AbortSignal;\r\n /**\r\n * 遇到 data: [DONE] 时是否自动停止迭代。\r\n * SSE 规范并不要求解析器处理 [DONE],但 OpenAI/DeepSeek\r\n * 等兼容 API 使用它标记流结束。默认 true。\r\n */\r\n stopOnDone?: boolean;\r\n}\r\n\r\n/**\r\n * 解析 SSE 响应流,逐步 yield 事件。\r\n *\r\n * 调用方需保证传入的 response 为成功的 2xx 响应且 body 可读。\r\n *\r\n * @param response 已返回 2xx 的 fetch 响应\r\n * @param options 解析选项(超时、中止信号)\r\n * @yields SSEEvent — 已按 SSE 协议拼装好的事件\r\n */\r\nexport async function* parseSSE(\r\n response: Response,\r\n options: ParseSSEOptions = {},\r\n): AsyncIterable<SSEEvent> {\r\n const reader = response.body?.getReader();\r\n if (!reader) {\r\n throw new Error(\"SSE 响应体为空,无法解析\");\r\n }\r\n\r\n const decoder = new TextDecoder();\r\n let buffer = \"\";\r\n\r\n // 当前正在拼装的事件字段累积器\r\n let pendingData: string[] = [];\r\n let pendingEvent: string | undefined;\r\n let pendingId: string | undefined;\r\n let pendingRetry: number | undefined;\r\n\r\n const idleMs = options.idleTimeoutMs ?? 60_000;\r\n\r\n // 是否在遇到 [DONE] 时停止迭代\r\n const shouldStopOnDone = options.stopOnDone !== false;\r\n\r\n // [DONE] 标记:由 flush 设置,外部循环检查后停止迭代\r\n let streamDone = false;\r\n\r\n // 将当前累积的事件 flush 为一个 SSEEvent\r\n function* flush(): Generator<SSEEvent> {\r\n if (pendingData.length === 0) {\r\n pendingEvent = undefined;\r\n pendingId = undefined;\r\n pendingRetry = undefined;\r\n return;\r\n }\r\n const evt: SSEEvent = { data: pendingData.join(\"\\n\") };\r\n if (pendingEvent) evt.event = pendingEvent;\r\n if (pendingId) evt.id = pendingId;\r\n if (pendingRetry !== undefined) evt.retry = pendingRetry;\r\n pendingData = [];\r\n pendingEvent = undefined;\r\n pendingId = undefined;\r\n pendingRetry = undefined;\r\n\r\n // 遇到 [DONE] 标记时设置流结束标志\r\n if (shouldStopOnDone && evt.data === \"[DONE]\") {\r\n streamDone = true;\r\n }\r\n\r\n yield evt;\r\n }\r\n\r\n // 处理单行 SSE 内容\r\n function* processLine(rawLine: string): Generator<SSEEvent> {\r\n // 去掉行尾 \\r(CRLF 场景)\r\n const line = rawLine.endsWith(\"\\r\") ? rawLine.slice(0, -1) : rawLine;\r\n\r\n // 空行:事件分隔符,触发 emit\r\n if (line === \"\") {\r\n yield* flush();\r\n return;\r\n }\r\n // 注释行(以 \":\" 开头),忽略\r\n if (line.startsWith(\":\")) return;\r\n\r\n // 字段行:field: value(冒号后允许一个可选空格)\r\n const colonIdx = line.indexOf(\":\");\r\n if (colonIdx === -1) {\r\n applyField(line, \"\");\r\n return;\r\n }\r\n const field = line.slice(0, colonIdx);\r\n let value = line.slice(colonIdx + 1);\r\n if (value.startsWith(\" \")) value = value.slice(1);\r\n applyField(field, value);\r\n }\r\n\r\n function applyField(field: string, value: string): void {\r\n switch (field) {\r\n case \"event\":\r\n pendingEvent = value;\r\n break;\r\n case \"data\":\r\n pendingData.push(value);\r\n break;\r\n case \"id\":\r\n pendingId = value;\r\n break;\r\n case \"retry\": {\r\n const n = Number(value);\r\n if (Number.isFinite(n)) pendingRetry = n;\r\n break;\r\n }\r\n default:\r\n // SSE 规范要求忽略未识别字段\r\n break;\r\n }\r\n }\r\n\r\n try {\r\n while (true) {\r\n if (options.signal?.aborted) return;\r\n\r\n const readResult = await readWithTimeout(reader, idleMs, options.signal);\r\n if (readResult === null) return; // 中止\r\n const { done, value } = readResult;\r\n if (done) {\r\n // flush 解码器残留\r\n buffer += decoder.decode();\r\n if (buffer.length > 0) {\r\n const line = buffer.endsWith(\"\\r\") ? buffer.slice(0, -1) : buffer;\r\n if (line !== \"\") yield* processLine(line);\r\n buffer = \"\";\r\n }\r\n yield* flush();\r\n if (streamDone) return;\r\n return;\r\n }\r\n\r\n buffer += decoder.decode(value, { stream: true });\r\n\r\n let nl: number;\r\n while ((nl = buffer.indexOf(\"\\n\")) !== -1) {\r\n const line = buffer.slice(0, nl);\r\n buffer = buffer.slice(nl + 1);\r\n yield* processLine(line);\r\n if (streamDone) return;\r\n }\r\n }\r\n } finally {\r\n reader.releaseLock();\r\n }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 带超时的 reader.read()\r\n// ---------------------------------------------------------------------------\r\n\r\ninterface TimedReadResult {\r\n done: boolean;\r\n value: Uint8Array;\r\n}\r\n\r\n/**\r\n * 在超时范围内等待 reader.read(),超时抛出 StreamIdleTimeoutError。\r\n *\r\n * - signal 中止时立即返回 null(让调用方优雅退出)\r\n * - 超时抛出 StreamIdleTimeoutError(reader 由外层 finally 释放)\r\n */\r\nfunction readWithTimeout(\r\n reader: ReadableStreamDefaultReader<Uint8Array>,\r\n idleMs: number,\r\n signal?: AbortSignal,\r\n): Promise<TimedReadResult | null> {\r\n if (signal?.aborted) return Promise.resolve(null);\r\n\r\n return new Promise<TimedReadResult | null>((resolve, reject) => {\r\n let settled = false;\r\n\r\n const timer = setTimeout(() => {\r\n if (settled) return;\r\n settled = true;\r\n reject(new StreamIdleTimeoutError(`流式空闲超时(${idleMs}ms 无数据)`, idleMs));\r\n }, idleMs);\r\n\r\n const onAbort = () => {\r\n if (settled) return;\r\n settled = true;\r\n clearTimeout(timer);\r\n resolve(null);\r\n };\r\n if (signal) signal.addEventListener(\"abort\", onAbort, { once: true });\r\n\r\n reader\r\n .read()\r\n .then((result) => {\r\n if (settled) return;\r\n settled = true;\r\n clearTimeout(timer);\r\n if (signal) signal.removeEventListener(\"abort\", onAbort);\r\n resolve(result as TimedReadResult);\r\n })\r\n .catch((err: unknown) => {\r\n if (settled) return;\r\n settled = true;\r\n clearTimeout(timer);\r\n if (signal) signal.removeEventListener(\"abort\", onAbort);\r\n reject(err);\r\n });\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 ClientOptions,\r\n} from \"./types.js\";\r\nimport { getModelMeta, estimateTokens } from \"./models.js\";\r\nimport { mapHttpError } from \"./errors.js\";\r\nimport { HttpClient, type RequestOptions } from \"./client.js\";\r\nimport { parseSSE } from \"./sse.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 extends ClientOptions {\r\n apiKey: string;\r\n baseUrl: string;\r\n model: ModelId;\r\n /** 流式空闲超时(毫秒),默认 60000 */\r\n idleTimeoutMs?: number;\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 * - 连接超时、流式空闲超时、指数退避重试(由 HttpClient 提供)\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 readonly #client: HttpClient;\r\n readonly #idleTimeoutMs: number;\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 this.#idleTimeoutMs = config.idleTimeoutMs ?? 60_000;\r\n this.#client = new HttpClient({\r\n connectTimeoutMs: config.connectTimeoutMs,\r\n maxRetries: config.maxRetries,\r\n retryBaseDelayMs: config.retryBaseDelayMs,\r\n retryMaxDelayMs: config.retryMaxDelayMs,\r\n });\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 // HttpClient 已封装超时与 4xx/5xx 映射,抛出的错误可直接上拋\r\n const response = await this.#client.request(url, {\r\n method: \"GET\",\r\n headers: { Authorization: `Bearer ${this.#apiKey}` },\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 * 通过 HttpClient 发起请求(自动连接超时 + 429/5xx 指数退避重试),\r\n * 用 parseSSE 解析事件流,逐块映射为 ChatChunk 并 yield。\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 // 如果传入了工具定义,加入请求体\r\n if (opts?.tools && opts.tools.length > 0) {\r\n body.tools = opts.tools;\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 const requestOptions: RequestOptions = {};\r\n if (opts?.signal) requestOptions.signal = opts.signal;\r\n\r\n const response = await this.#client.requestWithRetry(\r\n url,\r\n {\r\n method: \"POST\",\r\n headers: { Authorization: `Bearer ${this.#apiKey}` },\r\n body: JSON.stringify(body),\r\n },\r\n requestOptions,\r\n );\r\n\r\n // 解析 SSE 流(含工具调用累积)\r\n yield* this.#parseStream(response, opts?.signal);\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 事件流并映射为 ChatChunk。\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(\r\n response: Response,\r\n signal?: AbortSignal,\r\n ): AsyncIterable<ChatChunk> {\r\n // 工具调用累积器:key = index(流中用 index 标识不同的 tool call)\r\n const toolCallAccumulator = new Map<number, AccumulatedToolCall>();\r\n\r\n for await (const evt of parseSSE(response, {\r\n idleTimeoutMs: this.#idleTimeoutMs,\r\n signal,\r\n })) {\r\n // [DONE] 标记流结束\r\n if (evt.data === \"[DONE]\") return;\r\n\r\n let chunk: StreamChunk;\r\n try {\r\n chunk = JSON.parse(evt.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\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":[]}
@@ -1,8 +0,0 @@
1
- import {
2
- DeepSeekProvider
3
- } from "./chunk-BA7UJPKJ.js";
4
- import "./chunk-7QS2XBBX.js";
5
- export {
6
- DeepSeekProvider
7
- };
8
- //# sourceMappingURL=deepseek-UJXV2D6K.js.map
@@ -1,19 +0,0 @@
1
- import {
2
- SUPPORTED_MODELS,
3
- SUPPORTED_MODEL_IDS,
4
- calculateCost,
5
- estimateTokens,
6
- formatCost,
7
- getModelMeta,
8
- isSupportedModel
9
- } from "./chunk-7QS2XBBX.js";
10
- export {
11
- SUPPORTED_MODELS,
12
- SUPPORTED_MODEL_IDS,
13
- calculateCost,
14
- estimateTokens,
15
- formatCost,
16
- getModelMeta,
17
- isSupportedModel
18
- };
19
- //# sourceMappingURL=models-OL7DHYGK.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}