dskcode 0.1.15 → 0.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -16
- package/dist/{chunk-HWZZJB6I.js → chunk-BA7UJPKJ.js} +4 -1
- package/dist/{chunk-HWZZJB6I.js.map → chunk-BA7UJPKJ.js.map} +1 -1
- package/dist/{deepseek-OUOEENTK.js → deepseek-UJXV2D6K.js} +2 -2
- package/dist/index.js +1733 -266
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- /package/dist/{deepseek-OUOEENTK.js.map → deepseek-UJXV2D6K.js.map} +0 -0
package/README.md
CHANGED
|
@@ -11,23 +11,25 @@
|
|
|
11
11
|
|
|
12
12
|
## 特性
|
|
13
13
|
|
|
14
|
-
- **终端原生交互** — `dskcode chat` 进入交互式对话,在终端中直接与 AI 协作编码
|
|
15
|
-
- **一次性任务执行** — `dskcode run
|
|
16
|
-
- **DeepSeek 深度集成** — 原生 DeepSeek API 支持,Prefix Cache 感知,成本透明
|
|
17
|
-
- **模型支持** — 仅支持 **DeepSeek-V4-Flash**(默认)和 **DeepSeek-V4-Pro** 两个模型
|
|
18
|
-
- **工具系统** —
|
|
19
|
-
- **MCP 插件** — 通过 Model Context Protocol 扩展任意外部工具
|
|
20
|
-
- **项目记忆** — AGENTS.md
|
|
21
|
-
- **权限控制** — 三级审批策略(Allow / Ask / Deny),安全可控
|
|
22
|
-
- **JSON 配置** — 多层级配置(全局 + 项目 + 环境变量 + CLI flag)
|
|
23
|
-
- **中文优先** — 界面提示、帮助信息、文档均为中文
|
|
24
|
-
-
|
|
14
|
+
- **终端原生交互** — `dskcode chat` 进入交互式对话,在终端中直接与 AI 协作编码 ✅
|
|
15
|
+
- **一次性任务执行** — `dskcode run` 让 AI 自动完成任务 ✅
|
|
16
|
+
- **DeepSeek 深度集成** — 原生 DeepSeek API 支持,Prefix Cache 感知,成本透明 ✅
|
|
17
|
+
- **模型支持** — 仅支持 **DeepSeek-V4-Flash**(默认)和 **DeepSeek-V4-Pro** 两个模型 ✅
|
|
18
|
+
- **工具系统** — 8 个内置工具(read_file / write_file / edit_file / bash / glob / grep / ls / fetch),Agent 可读写文件、执行命令、搜索代码 ✅
|
|
19
|
+
- **MCP 插件** — 通过 Model Context Protocol 扩展任意外部工具 ⚡ 骨架就绪
|
|
20
|
+
- **项目记忆** — AGENTS.md 注入系统提示词 ❌ 持久化未完成
|
|
21
|
+
- **权限控制** — 三级审批策略(Allow / Ask / Deny),安全可控 ❌ 未开始
|
|
22
|
+
- **JSON 配置** — 多层级配置(全局 + 项目 + 环境变量 + CLI flag) ✅
|
|
23
|
+
- **中文优先** — 界面提示、帮助信息、文档均为中文 ✅
|
|
24
|
+
- **Token 计价** — 会话级 / 日级 / 历史级三层成本统计,Prefix Cache 半价计费 ✅
|
|
25
|
+
- **流式渲染** — 打字机效果、spinner、代码块高亮、工具调用可视化 ✅
|
|
26
|
+
- **股票行情** — `dskcode stock` 交互式 A 股行情终端,键盘选择 + 详情折线图 + 每 10 秒自动刷新 ✅
|
|
25
27
|
|
|
26
28
|

|
|
27
29
|
|
|
28
30
|

|
|
29
31
|
|
|
30
|
-
- **内置小游戏** — `dskcode game` 启动游戏列表,打砖块、Coder Check 极速打字等内置游戏供休闲娱乐
|
|
32
|
+
- **内置小游戏** — `dskcode game` 启动游戏列表,打砖块、Coder Check 极速打字等内置游戏供休闲娱乐 ✅
|
|
31
33
|
|
|
32
34
|

|
|
33
35
|
|
|
@@ -215,6 +217,41 @@ dskcode stock 000001 399006
|
|
|
215
217
|
|
|
216
218
|
分时数据来自腾讯免费行情接口 `web.ifzq.gtimg.cn/appstock/app/minute/query`,全天 242 条分钟线(09:30~15:00)。
|
|
217
219
|
|
|
220
|
+
## 实现进度
|
|
221
|
+
|
|
222
|
+
15 章开发计划,当前进度:
|
|
223
|
+
|
|
224
|
+
| # | 模块 | 说明 | 状态 |
|
|
225
|
+
|---|------|------|------|
|
|
226
|
+
| 01 | 项目初始化与工程基建 | monorepo、tsconfig、ESM/CJS、lint、test | ✅ 已完成 |
|
|
227
|
+
| 02 | CLI 框架搭建与子命令路由 | commander + inquirer、chat/run/setup 子命令 | ✅ 已完成 |
|
|
228
|
+
| 03 | 配置系统 | JSON 多级配置、环境变量、CLI flag 覆盖 | ✅ 已完成 |
|
|
229
|
+
| 04 | Provider 抽象层 | Provider 接口、DeepSeek 适配器、工厂注册 | ✅ 已完成 |
|
|
230
|
+
| 05 | LLM API 客户端 | 流式补全(SSE)、AbortController、超时重试 | ✅ 已完成 |
|
|
231
|
+
| 06 | Token 计价与成本追踪 | CostTracker 三层统计、Prefix Cache 半价 | ✅ 已完成 |
|
|
232
|
+
| 07 | Agent 主循环 | 消息编排、工具调用循环、轮次控制 | ✅ 已完成 |
|
|
233
|
+
| 08 | 工具系统 | Tool 接口、Registry、8 个内置工具 | ✅ 已完成 |
|
|
234
|
+
| 09 | MCP 插件系统 | stdio JSON-RPC、子进程管理 | ⚡ 骨架就绪 |
|
|
235
|
+
| 10 | 权限控制 | Allow/Ask/Deny 三级策略、交互式审批 | ❌ 未开始 |
|
|
236
|
+
| 11 | 会话管理与项目记忆 | 对话持久化、AGENTS.md 注入 | ❌ 未开始 |
|
|
237
|
+
| 12 | 终端交互 | 打字机效果、spinner、代码块高亮 | ✅ 已完成 |
|
|
238
|
+
| 13 | 管道模式 | stdin/stdout 集成、非交互模式、JSON 输出 | ❌ 未开始 |
|
|
239
|
+
| 14 | 配置向导 | `dskcode setup` 交互式配置、API Key 管理 | ❌ 未开始 |
|
|
240
|
+
| 15 | 构建发布与 CI/CD | tsup 打包、npm publish、GitHub Actions | ✅ 已完成 |
|
|
241
|
+
|
|
242
|
+
### 内置工具详情(第 08 章)
|
|
243
|
+
|
|
244
|
+
| 工具 | 说明 | 状态 |
|
|
245
|
+
|------|------|------|
|
|
246
|
+
| `read_file` | 读取文件,支持行号范围 | ✅ |
|
|
247
|
+
| `write_file` | 写入/创建文件,自动创建中间目录 | ✅ |
|
|
248
|
+
| `edit_file` | 精确文本替换,唯一匹配校验 | ✅ |
|
|
249
|
+
| `bash` | 执行 shell 命令,超时控制 | ✅ |
|
|
250
|
+
| `glob` | 文件路径模式搜索(`*` / `**` / `?`) | ✅ |
|
|
251
|
+
| `grep` | 文件内容正则搜索,扩展名过滤 | ✅ |
|
|
252
|
+
| `ls` | 目录列表,类型标记,隐藏文件控制 | ✅ |
|
|
253
|
+
| `fetch` | HTTP 请求(GET/POST/PUT/DELETE) | ✅ |
|
|
254
|
+
|
|
218
255
|
## 架构
|
|
219
256
|
|
|
220
257
|
```
|
|
@@ -222,12 +259,13 @@ src/
|
|
|
222
259
|
├── index.ts # 入口,shebang + 异常处理
|
|
223
260
|
├── cli/ # commander 命令路由
|
|
224
261
|
├── config/ # JSON 配置加载与合并
|
|
225
|
-
├── provider/
|
|
226
|
-
├── tool/ #
|
|
227
|
-
├── plugin/ # MCP
|
|
262
|
+
├── provider/ # LLM Provider 接口(DeepSeek)
|
|
263
|
+
├── tool/ # 内置工具(Registry + 8 个内置工具)
|
|
264
|
+
├── plugin/ # MCP 插件管理器(骨架就绪)
|
|
228
265
|
├── agent/ # Agent 会话循环
|
|
229
|
-
├── stock/ #
|
|
266
|
+
├── stock/ # 股票行情(交互式终端 + asciichart)
|
|
230
267
|
├── game/ # 内置小游戏
|
|
268
|
+
├── types/ # 共享类型定义
|
|
231
269
|
└── ui/ # Ink 交互式终端 UI
|
|
232
270
|
```
|
|
233
271
|
|
|
@@ -481,6 +481,9 @@ var DeepSeekProvider = class {
|
|
|
481
481
|
max_tokens: opts?.maxTokens,
|
|
482
482
|
temperature: opts?.temperature
|
|
483
483
|
};
|
|
484
|
+
if (opts?.tools && opts.tools.length > 0) {
|
|
485
|
+
body.tools = opts.tools;
|
|
486
|
+
}
|
|
484
487
|
for (const key of Object.keys(body)) {
|
|
485
488
|
if (body[key] === void 0) {
|
|
486
489
|
delete body[key];
|
|
@@ -623,4 +626,4 @@ export {
|
|
|
623
626
|
ModelNotSupportedError,
|
|
624
627
|
DeepSeekProvider
|
|
625
628
|
};
|
|
626
|
-
//# sourceMappingURL=chunk-
|
|
629
|
+
//# sourceMappingURL=chunk-BA7UJPKJ.js.map
|
|
@@ -1 +1 @@
|
|
|
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 // 移除 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
|
+
{"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":[]}
|