@deepwhale/llm 1.0.11 → 1.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,457 +0,0 @@
1
- /**
2
- * DeepSeek 客户端 — OpenAI 兼容 HTTP 协议。
3
- *
4
- * Sprint 0.3 范围:非流式 chat + 5 个 LLMError 子类。
5
- * Sprint 1a 范围:
6
- * - 非流式 + 流式双 API(stream() 新增)
7
- * - retry/backoff:429/5xx/network 最多 3 次指数退避(200ms→400ms→800ms)
8
- * - tool_calls 支持(OpenAI function-calling 协议)
9
- * - **机制 2:content="" 永远序列化**(不带 omitempty — 防 wire-level 缓存 hash 变化)
10
- * - **机制 3:reasoning_content 不打 wire** — session 内部 thinking 保留,wire 不传
11
- * - usage 透传(DeepSeek V4 起带 cached_tokens)
12
- * - finish_reason 透传
13
- *
14
- * Sprint 1b 再加:canonical schema、cache_hit_rate 暴露。
15
- * Sprint 2+ 再加:Anthropic 兼容客户端、断点续传。
16
- */
17
- import { t } from '@deepwhale/core';
18
- import process from 'node:process';
19
- import { readFileSync } from 'node:fs';
20
- import { fileURLToPath } from 'node:url';
21
- import { dirname, resolve } from 'node:path';
22
- import { APIKeyMissingError, LLMAuthError, LLMNetworkError, LLMRateLimitError, LLMStreamError, LLMUnknownError, isLLMError, } from './types.js';
23
- import { parsePricingConfig, } from './pricing-config.js';
24
- import { isSseDoneSentinel, parseOaiChatCompletion, parseSseEvent, } from './parse.js';
25
- /** DeepSeek 的 OpenAI 兼容端点。 */
26
- export const DEEPSEEK_BASE_URL = 'https://api.deepseek.com/v1';
27
- /**
28
- * 默认模型:DeepSeek-V4-Flash(V4 系列速度/成本优先版本)。
29
- *
30
- * 迁移背景:
31
- * - V4 于 2026-04-24 preview 发布,2026-07-24 15:59 UTC 后 `deepseek-chat`
32
- * 和 `deepseek-reasoner` 旧 alias 完全失效(hard-fail,不再是 deprecation warning)。
33
- * - 官方推荐路径:保持 base_url 不变,把 model 字段改成 `deepseek-v4-flash` 或
34
- * `deepseek-v4-pro`。
35
- * - v4-flash 是 coding agentic 场景的官方推荐(速度/成本/工具调用优化)。
36
- * - ref: https://api-docs.deepseek.com/news/news260424
37
- */
38
- export const DEEPSEEK_DEFAULT_MODEL = 'deepseek-v4-flash';
39
- const DEFAULT_TIMEOUT_MS = 60_000;
40
- /** Sprint 1a:固定 3 次重试(429/5xx/network),指数退避 200ms → 400ms → 800ms。 */
41
- const RETRYABLE_STATUSES = new Set([408, 425, 429, 500, 502, 503, 504]);
42
- const MAX_RETRIES = 3;
43
- const BASE_BACKOFF_MS = 200;
44
- export class DeepSeekClient {
45
- model;
46
- apiKey;
47
- baseUrl;
48
- fetchImpl;
49
- timeoutMs;
50
- sleepFn;
51
- makeAbortController;
52
- /**
53
- * Sprint 1b.5: pricing config 注入. 缺省 = 启动期 sync 加载 ship-in `pricing.default.toml`.
54
- * 加载失败 (file not found / parse error) → `undefined` → 走 R7 中间路径
55
- * (base 2 字段, cost 字段 absent, 不静默 fallback).
56
- *
57
- * 用法: caller 显式 `pricing: await loadPricingConfig()` 可覆盖 ship-in (例如读用户 ~/.deepwhale/pricing.toml).
58
- * `DeepSeekClientOptions.pricing` 字段让 caller 控制 (不阻塞 constructor).
59
- */
60
- pricing;
61
- constructor(options = {}) {
62
- this.apiKey = options.apiKey ?? process.env['DEEPSEEK_API_KEY'];
63
- const rawModel = options.model ?? DEEPSEEK_DEFAULT_MODEL;
64
- this.model = rawModel;
65
- this.baseUrl = options.baseUrl ?? DEEPSEEK_BASE_URL;
66
- this.fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
67
- this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
68
- this.sleepFn = options.sleepFn ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
69
- this.makeAbortController = options.makeAbortController ?? (() => new AbortController());
70
- this.pricing = options.pricing ?? loadDefaultPricing();
71
- }
72
- // ==========================================================================
73
- // 公开 API
74
- // ==========================================================================
75
- async chat(messages, options = {}) {
76
- if (!this.apiKey) {
77
- throw new APIKeyMissingError(t('error.api_key_missing'));
78
- }
79
- const body = this.buildRequestBody(messages, options.tools, options.tool_choice, false);
80
- const res = await this.callWithRetry(body, options.signal);
81
- const json = await this.parseJsonResponse(res);
82
- return this.parseChatResponse(json, res.status);
83
- }
84
- async stream(messages, options) {
85
- if (!this.apiKey) {
86
- throw new APIKeyMissingError(t('error.api_key_missing'));
87
- }
88
- if (!options.onChunk) {
89
- throw new LLMUnknownError('stream() requires onChunk callback');
90
- }
91
- const body = this.buildRequestBody(messages, options.tools, options.tool_choice, true);
92
- const res = await this.callWithRetry(body, options.signal);
93
- if (!res.ok || !res.body) {
94
- await this.throwOnHttpError(res);
95
- }
96
- // 解析 SSE 流
97
- const reader = res.body.getReader();
98
- const decoder = new TextDecoder('utf-8');
99
- let buffer = '';
100
- let assembledContent = '';
101
- // Sprint 1c-revive-2-D-21.1 (2026-06-06, 修 DeepSeek V4 thinking 400 bug):
102
- // 累加 delta.reasoning_content 给 final ChatResult. DeepSeek V4 默认开
103
- // thinking, 多轮必须把上轮 reasoning 回传, 否则 400.
104
- let assembledReasoningContent = '';
105
- let assembledToolCalls = [];
106
- let usage;
107
- let finishReason;
108
- let lineCount = 0;
109
- let chunkCount = 0;
110
- let sseParseFailures = 0;
111
- try {
112
- while (true) {
113
- const { value, done } = await reader.read();
114
- if (done)
115
- break;
116
- lineCount += 1;
117
- buffer += decoder.decode(value, { stream: true });
118
- // SSE: event 间以双换行分隔。真实 SSE wire 可能是 LF 或 CRLF(部分 proxy / 旧服务端会发 CRLF)。
119
- // Sprint 1a 修 P2-D:用正则一次匹配两种分隔,避免 CRLF 流被延迟到 flush。
120
- // 注意:不能 split('\n\n') 然后用 '\r\n\r\n' split,否则会切错最后一个 event。
121
- const SSE_DELIM_RE = /\r?\n\r?\n/;
122
- let m;
123
- // lastIndex 在循环里持续推进,直到无匹配
124
- const delimRe = new RegExp(SSE_DELIM_RE.source, 'g');
125
- while ((m = delimRe.exec(buffer)) !== null) {
126
- const event = buffer.slice(0, m.index);
127
- buffer = buffer.slice(m.index + m[0].length);
128
- delimRe.lastIndex = 0; // 每次切完后 buffer 头部变了,从头再来
129
- // P2-D follow-up:[DONE] sentinel 是 OAI 协议正常终止标记,绝不能算 parse failure。
130
- // 必须在 parseSseEvent 之前拦截,否则正常流也会刷 warn,污染 stderr 日志。
131
- // 其他返回 null 的情况(heartbeat / comment / JSON 损坏)才是真正需要 warn 的失败。
132
- if (isSseDoneSentinel(event))
133
- continue;
134
- // Sprint 1b.5: 透传 pricing + model 给 module-level parse fn.
135
- const parsed = parseSseEvent(event, this.pricing, this.model);
136
- if (parsed === null) {
137
- sseParseFailures += 1;
138
- continue;
139
- }
140
- chunkCount += 1;
141
- // Sprint 1a:content 增量直接累加
142
- if (parsed.delta.content) {
143
- assembledContent += parsed.delta.content;
144
- }
145
- // Sprint 1c-revive-2-D-21.1 (2026-06-06): reasoning_content 增量累加.
146
- // thinking mode 期间逐 chunk 给一段 thinking, 关掉后 content 继续.
147
- // 不开 thinking 的 model (V3 旧 alias) 不带这字段, 累加永远 '' 不影响.
148
- if (parsed.delta.reasoning_content) {
149
- assembledReasoningContent += parsed.delta.reasoning_content;
150
- }
151
- if (parsed.delta.tool_calls) {
152
- // Sprint 1a 一次性返回完整,直接覆盖(DeepSeek V4 流式 tool_calls 也是完整结构)
153
- assembledToolCalls = [...parsed.delta.tool_calls];
154
- }
155
- if (parsed.usage)
156
- usage = parsed.usage;
157
- if (parsed.finish_reason)
158
- finishReason = parsed.finish_reason;
159
- options.onChunk(parsed);
160
- }
161
- // 切完后 buffer 还可能含一个未结束的 event(等下一个 chunk 或 flush)
162
- // lastIndex 已经被 reset 为 0;这里把 buffer 截到 delimRe.lastIndex(但 delimRe 已经停,buffer 已经是剩余)
163
- // 不需要额外处理,下一轮 read() 时再切。
164
- // 但如果 delimRe.exec 一次都没匹配,buffer 没变;下次 read 直接 append。
165
- }
166
- // 末尾 flush buffer
167
- if (buffer.trim().length > 0) {
168
- // 同样先看 [DONE] sentinel,再走 parseSseEvent
169
- // Sprint 1b.5 Step 2.5 (F6 拍板, review 2026-06-03 找到): 之前 flush 路径漏传
170
- // this.pricing + this.model, 最后一个 SSE usage event 没尾随双换行时会丢
171
- // cost_turn/cost_currency. 修法: 跟正常 path 一致传 this.pricing, this.model.
172
- if (!isSseDoneSentinel(buffer)) {
173
- const parsed = parseSseEvent(buffer, this.pricing, this.model);
174
- if (parsed) {
175
- chunkCount += 1;
176
- if (parsed.delta.content)
177
- assembledContent += parsed.delta.content;
178
- if (parsed.delta.reasoning_content) {
179
- assembledReasoningContent += parsed.delta.reasoning_content;
180
- }
181
- if (parsed.delta.tool_calls)
182
- assembledToolCalls = [...parsed.delta.tool_calls];
183
- if (parsed.usage)
184
- usage = parsed.usage;
185
- if (parsed.finish_reason)
186
- finishReason = parsed.finish_reason;
187
- options.onChunk(parsed);
188
- }
189
- else {
190
- sseParseFailures += 1;
191
- }
192
- }
193
- }
194
- // Sprint 1a 修 P2-D:JSON parse 失败不再静默,在 stderr 留一行 warn(运维排查用)。
195
- if (sseParseFailures > 0) {
196
- process.stderr.write(`[deepwhale] warn: SSE parse failures: ${sseParseFailures} (stream still completed)\n`);
197
- }
198
- }
199
- catch (err) {
200
- throw new LLMStreamError(`SSE stream interrupted after ${lineCount} lines / ${chunkCount} chunks: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
201
- }
202
- const result = {
203
- model: this.model,
204
- content: assembledContent,
205
- };
206
- // Sprint 1c-revive-2-D-21.1 (2026-06-06): reasoning_content 完整累加后透传.
207
- // 不开 thinking 时这里是 '', 省略字段 (避免污染 caller).
208
- if (assembledReasoningContent.length > 0) {
209
- result.reasoning_content = assembledReasoningContent;
210
- }
211
- if (assembledToolCalls.length > 0)
212
- result.tool_calls = assembledToolCalls;
213
- if (usage)
214
- result.usage = usage;
215
- if (finishReason)
216
- result.finish_reason = finishReason;
217
- return result;
218
- }
219
- // ==========================================================================
220
- // 内部:HTTP + retry
221
- // ==========================================================================
222
- buildRequestBody(messages, tools, toolChoice, stream) {
223
- return {
224
- model: this.model,
225
- messages: messages.map(toWireMessage),
226
- stream,
227
- // P1 fix (2026-06-03): OAI/DeepSeek 在 stream=true 时, 必须在 body 里
228
- // 显式带 stream_options.include_usage=true, 服务端才在最后一个 chunk
229
- // (可能带 choices 也可能不带) 上携带 usage 字段。
230
- // 不然 cache_hit_rate / cost_turn / tokens_uncached 在 stream 路径全部拿不到。
231
- // ref: https://api-docs.deepseek.com/api/create-chat-completion
232
- ...(stream ? { stream_options: { include_usage: true } } : {}),
233
- // 机制 2:content="" 永远序列化(OAI spec 允许,但 prefix-cache hash 会变,
234
- // 所以这里强制把空字符串序列化为 "",绝不带 omitempty)
235
- // 实际由 toWireMessage 完成 — 这里只是注释提醒调用者。
236
- ...(tools && tools.length > 0
237
- ? { tools: tools.map((t) => ({ type: 'function', function: t })) }
238
- : {}),
239
- ...(toolChoice ? { tool_choice: toolChoice } : {}),
240
- };
241
- }
242
- async callWithRetry(body, externalSignal) {
243
- const url = `${this.baseUrl}/chat/completions`;
244
- let lastError = null;
245
- for (let attempt = 0; attempt < MAX_RETRIES; attempt += 1) {
246
- // 每次重试一个独立 AbortController(timeout 重新计)
247
- const timeoutController = this.makeAbortController();
248
- const timer = setTimeout(() => timeoutController.abort(new Error('timeout')), this.timeoutMs);
249
- const combinedSignal = externalSignal !== undefined
250
- ? AbortSignal.any([externalSignal, timeoutController.signal])
251
- : timeoutController.signal;
252
- try {
253
- const res = await this.fetchImpl(url, {
254
- method: 'POST',
255
- headers: {
256
- 'Content-Type': 'application/json',
257
- Authorization: `Bearer ${this.apiKey}`,
258
- },
259
- body: JSON.stringify(body),
260
- signal: combinedSignal,
261
- });
262
- if (res.ok)
263
- return res;
264
- // 5xx/429 → retry;4xx 其他 → 立即抛
265
- if (RETRYABLE_STATUSES.has(res.status) && attempt < MAX_RETRIES - 1) {
266
- // 读 body 留作错误诊断(不 await 不算 leak,GC 会清)
267
- const errBody = await res.text().catch(() => '');
268
- lastError = new Error(`HTTP ${res.status}: ${errBody.slice(0, 200)}`);
269
- await this.sleepFn(BASE_BACKOFF_MS * 2 ** attempt);
270
- continue;
271
- }
272
- await this.throwOnHttpError(res);
273
- }
274
- catch (err) {
275
- // 4xx 已被 throwOnHttpError 处理,这里的 err 是 network/timeout/abort
276
- if (isLLMError(err))
277
- throw err; // 4xx 不重试
278
- lastError = err;
279
- // 已被外部 signal 取消 → 立即退出
280
- if (externalSignal?.aborted) {
281
- throw new LLMNetworkError('Request aborted by caller', { cause: err });
282
- }
283
- // timeout / network → retry
284
- if (attempt < MAX_RETRIES - 1) {
285
- await this.sleepFn(BASE_BACKOFF_MS * 2 ** attempt);
286
- continue;
287
- }
288
- throw this.wrapNetworkError(err);
289
- }
290
- finally {
291
- clearTimeout(timer);
292
- }
293
- }
294
- // 理论不会到这(最后一次循环要么 return 要么 throw)
295
- throw this.wrapNetworkError(lastError);
296
- }
297
- async parseJsonResponse(res) {
298
- try {
299
- return await res.json();
300
- }
301
- catch (err) {
302
- throw new LLMUnknownError('Failed to parse DeepSeek response as JSON', { cause: err });
303
- }
304
- }
305
- parseChatResponse(json, status) {
306
- // Sprint 1b.5: 透传 this.pricing 给 module-level parseOaiChatCompletion,
307
- // 让 computeCost 能找到 model pricing 算 cost. 不传 pricing 走 R7 中间路径
308
- // (cost 字段 absent, base 2 字段仍在).
309
- const parsed = parseOaiChatCompletion(json, this.model, this.pricing);
310
- if (parsed === null) {
311
- throw new LLMUnknownError('DeepSeek response missing choices[0].message', { status });
312
- }
313
- return parsed;
314
- }
315
- async throwOnHttpError(res) {
316
- // P2 稳定性债 (Sprint 1c-revive-2-D-21.1 P1 修复, 2026-06-06):
317
- // 假 key 时 DeepSeek 返 401 + JSON error body, 但 body 是 stream。
318
- // 之前直接 res.text() 读 stream, 跟调用方没建好的 reader 撞 libuv 状态机,
319
- // Windows + libuv 内部报 assertion 后 crash 进程。修法: 先 cancel 掉 body
320
- // (告诉 fetch "我不要了"), 再读 1 个 chunk 上限, 避免 await 死等。
321
- // ref: https://undici.nodejs.org/#/docs/api/Response.md (body.cancel)
322
- let text = '';
323
- if (res.body && !res.body.locked) {
324
- try {
325
- const reader = res.body.getReader();
326
- const { value } = await reader.read().catch(() => ({ value: undefined }));
327
- if (value)
328
- text = new TextDecoder('utf-8').decode(value);
329
- await reader.cancel().catch(() => { });
330
- }
331
- catch {
332
- // swallow — 我们只是想拿 status + 短 snippet, 不希望 network 异常覆盖
333
- }
334
- }
335
- const message = `DeepSeek API error ${res.status}: ${text.slice(0, 200)}`;
336
- if (res.status === 429)
337
- throw new LLMRateLimitError(message);
338
- if (res.status === 401 || res.status === 403)
339
- throw new LLMAuthError(res.status, message);
340
- throw new LLMUnknownError(message, { status: res.status });
341
- }
342
- wrapNetworkError(err) {
343
- if (isLLMError(err))
344
- return err;
345
- const msg = err instanceof Error ? err.message : String(err);
346
- if (msg.includes('abort') || msg.includes('AbortError')) {
347
- return new LLMNetworkError(`Request aborted: ${msg}`, { cause: err });
348
- }
349
- return new LLMNetworkError(`Network error: ${msg}`, { cause: err });
350
- }
351
- }
352
- // ============================================================================
353
- // 内部 helper: sync 加载 ship-in pricing.default.toml
354
- // ============================================================================
355
- /**
356
- * Sprint 1b.5: 启动期 sync 加载 ship-in `pricing.default.toml`.
357
- *
358
- * 设计:
359
- * - sync 加载 (1 次启动, 不在 hot path, 符合 R3)
360
- * - 加载失败 (file not found / parse error) → 返回 `undefined` → caller 走 R7 中间路径
361
- * (不静默 fallback 到硬编码, 不抛错, base 2 字段, cost 字段 absent)
362
- * - 文件路径解析: Use sibling pricing.default.toml relative to package location. (Q 拍板 2026-06-04, Task 2 Gap 5 留痕)
363
- * 走 `import.meta.url` → 当前文件 → 同目录 `pricing.default.toml`
364
- * (dev 走 src/, build 后走 dist/, 路径自动对齐;
365
- * **不**走 cwd / XDG config dir / env var, 避免 dev-vs-prod 不一致)
366
- *
367
- * 测试环境 (vitest) 时 pricing.default.toml 跟 src/ 在一起, 也能加载。
368
- */
369
- function loadDefaultPricing() {
370
- try {
371
- const here = dirname(fileURLToPath(import.meta.url));
372
- const defaultPath = resolve(here, 'pricing.default.toml');
373
- const tomlText = readFileSync(defaultPath, 'utf-8');
374
- return parsePricingConfig(tomlText);
375
- }
376
- catch {
377
- // 任何错误 (file not found / parse error / 权限) → 不让 client 启动失败.
378
- // 走 R7 中间路径: base 2 字段, cost 字段 absent.
379
- return undefined;
380
- }
381
- }
382
- // ============================================================================
383
- // 内部 helper:wire 消息转换 + 响应解析 + SSE 解析
384
- // ============================================================================
385
- /**
386
- * 把 ChatMessage 转成 OAI wire 格式。
387
- *
388
- * 关键不变量(Prefix-cache 机制 2/3):
389
- * - `content: ""` 永远序列化为 ""(不带 omitempty)
390
- * - reasoning_content 字段不打 wire(即使 LLM 返回了也丢掉 — Sprint 1a 简化)
391
- * - tool_calls 必带 type:'function' 包装
392
- *
393
- * Sprint 1c-revive-2-D-21.1 (2026-06-06, 修 DeepSeek V4 thinking 400 bug):
394
- * 取消 "机制 3 简化" — 现在 reasoning_content 完整透传. DeepSeek V4 默认开
395
- * thinking mode, 多轮必须回传上轮 reasoning, 否则 400 "reasoning_content
396
- * must be passed back to the API". 改动:
397
- * - assistant 消息 (无 tool_calls): wire 包含 reasoning_content (若有)
398
- * - assistant 消息 (有 tool_calls): wire 包含 reasoning_content (若有)
399
- * (DeepSeek 协议要求 tool_call 那个 turn 也要带 reasoning)
400
- * - 非 assistant 消息: 不带 reasoning_content 字段
401
- * - 空字符串 reasoning_content (thinking 关): 仍然序列化为 "" 字段, 跟
402
- * DeepSeek 服务端 round-trip 一致 (不依赖"字段缺失"判断). 拍板: thinking
403
- * 关时省掉字段 (omitempty 风格, 减少 wire 噪音), 见下行 guard.
404
- */
405
- function toWireMessage(m) {
406
- if (m.role === 'tool') {
407
- return {
408
- role: 'tool',
409
- content: m.content ?? '',
410
- tool_call_id: m.tool_call_id ?? '',
411
- };
412
- }
413
- if (m.role === 'assistant' && m.tool_calls && m.tool_calls.length > 0) {
414
- const wire = {
415
- role: 'assistant',
416
- content: m.content ?? '', // 机制 2:永远序列化
417
- tool_calls: m.tool_calls.map((tc) => ({
418
- id: tc.id,
419
- type: 'function',
420
- function: {
421
- name: tc.name,
422
- // OAI wire 上 args 是 string,我们这里已经是 object,JSON.stringify 转回去
423
- arguments: JSON.stringify(tc.args),
424
- },
425
- })),
426
- };
427
- if (m.reasoning_content !== undefined && m.reasoning_content.length > 0) {
428
- wire['reasoning_content'] = m.reasoning_content;
429
- }
430
- return wire;
431
- }
432
- if (m.role === 'assistant') {
433
- const wire = {
434
- role: m.role,
435
- content: m.content ?? '',
436
- };
437
- if (m.reasoning_content !== undefined && m.reasoning_content.length > 0) {
438
- wire['reasoning_content'] = m.reasoning_content;
439
- }
440
- return wire;
441
- }
442
- return { role: m.role, content: m.content ?? '' };
443
- }
444
- /**
445
- * 从 OAI chat/completions 非流式响应里提取内容。
446
- * 返回 null 表示结构异常。
447
- */
448
- /**
449
- * Step 2 起 parseOai* 3 个 module-level fn (parseOaiChatCompletion / parseSseEvent /
450
- * parseSseUsageField) + isSseDoneSentinel 已抽到 parse.ts. 旧 import 位置保留
451
- * 重导出给 1b 时代 test fixture 用 (deepseek-client.test.ts 没直接 import 它们,
452
- * 是通过 client 调用间接, 但保险起见保留 export shell).
453
- *
454
- * Sprint 1b.5: 留空注释, 避免误删. 真实代码在 parse.ts.
455
- */
456
- // (parseOaiChatCompletion / isSseDoneSentinel / parseSseEvent / parseSseUsageField) moved to ./parse.ts
457
- //# sourceMappingURL=deepseek-client.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"deepseek-client.js","sourceRoot":"","sources":["../src/deepseek-client.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts DELETED
@@ -1,26 +0,0 @@
1
- /**
2
- * @deepwhale/llm — OpenAI 兼容 LLM 客户端
3
- *
4
- * Sprint 0.3 落地:
5
- * - DeepSeek OpenAI 兼容 HTTP 客户端(chat/非流式/abort/timeout)
6
- * - 4 种 LLMError 子类(APIKeyMissing / RateLimit / Auth / Network / Unknown)
7
- * - fetch 注入 → 单测 100% mock
8
- *
9
- * Sprint 1a 扩展:流式 + tool_calls + retry/backoff + usage + 6 个 LLMError 子类
10
- * Sprint 1b 再加:cache_hit_rate、canonical schema
11
- * Sprint 1b.5: pricing config.toml 化 — CostBreakdown / computeCostBreakdown
12
- * 从 types.js 整体迁移到 pricing-config.js (per-model currency, R7 缺失定价中间路径).
13
- */
14
- export declare const DEEPWHALE_LLM_VERSION = "0.1.0";
15
- export { DeepSeekClient, DEEPSEEK_BASE_URL, DEEPSEEK_DEFAULT_MODEL } from './deepseek-client.js';
16
- export type { DeepSeekClientOptions } from './deepseek-client.js';
17
- export { AnthropicClient, DEEPSEEK_ANTHROPIC_BASE_URL, ANTHROPIC_DEFAULT_MODEL } from './anthropic-client.js';
18
- export type { AnthropicClientOptions } from './anthropic-client.js';
19
- export { canonicalizeSchema } from './canonicalize-schema.js';
20
- export { isSseDoneSentinel, parseOaiChatCompletion, parseSseEvent, parseSseUsageField } from './parse.js';
21
- export { parsePricingConfig, loadPricingConfig, computeCost, PricingConfigParseError, } from './pricing-config.js';
22
- export type { ModelPricing, PricingConfig, CostBreakdownResult, } from './pricing-config.js';
23
- export type { LLMClient, ChatMessage, ChatResult, ChatChunk, ToolCall, Usage, ModelId, Role, LLMError, LLMToolSchema, LLMToolParametersSchema, LLMToolParamSchema, } from './types.js';
24
- export { isLLMError } from './types.js';
25
- export { APIKeyMissingError, LLMRateLimitError, LLMAuthError, LLMNetworkError, LLMUnknownError, LLMStreamError, } from './types.js';
26
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,eAAO,MAAM,qBAAqB,UAAU,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AACjG,YAAY,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAIlE,OAAO,EAAE,eAAe,EAAE,2BAA2B,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAC9G,YAAY,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAG9D,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAC1G,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,WAAW,EACX,uBAAuB,GACxB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,YAAY,EACZ,aAAa,EACb,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,SAAS,EACT,WAAW,EACX,UAAU,EACV,SAAS,EACT,QAAQ,EACR,KAAK,EACL,OAAO,EACP,IAAI,EACJ,QAAQ,EACR,aAAa,EACb,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,EACZ,eAAe,EACf,eAAe,EACf,cAAc,GACf,MAAM,YAAY,CAAC"}
package/dist/index.js DELETED
@@ -1,27 +0,0 @@
1
- /**
2
- * @deepwhale/llm — OpenAI 兼容 LLM 客户端
3
- *
4
- * Sprint 0.3 落地:
5
- * - DeepSeek OpenAI 兼容 HTTP 客户端(chat/非流式/abort/timeout)
6
- * - 4 种 LLMError 子类(APIKeyMissing / RateLimit / Auth / Network / Unknown)
7
- * - fetch 注入 → 单测 100% mock
8
- *
9
- * Sprint 1a 扩展:流式 + tool_calls + retry/backoff + usage + 6 个 LLMError 子类
10
- * Sprint 1b 再加:cache_hit_rate、canonical schema
11
- * Sprint 1b.5: pricing config.toml 化 — CostBreakdown / computeCostBreakdown
12
- * 从 types.js 整体迁移到 pricing-config.js (per-model currency, R7 缺失定价中间路径).
13
- */
14
- export const DEEPWHALE_LLM_VERSION = '0.1.0';
15
- export { DeepSeekClient, DEEPSEEK_BASE_URL, DEEPSEEK_DEFAULT_MODEL } from './deepseek-client.js';
16
- // Sprint 1b.5 Step 2: Anthropic provider (走 /anthropic endpoint, DeepSeek shim)
17
- // - D1 拍板: 用官方 @anthropic-ai/sdk 实例发请求, 手写 parseAnthropic* 翻译
18
- // - X3 拍板: Step 2 不接真, 测试用 mock fetch
19
- export { AnthropicClient, DEEPSEEK_ANTHROPIC_BASE_URL, ANTHROPIC_DEFAULT_MODEL } from './anthropic-client.js';
20
- export { canonicalizeSchema } from './canonicalize-schema.js';
21
- // Sprint 1b.5 Step 2: 抽出来给 AnthropicClient 复用. parseOai* 处理 OAI-shape 协议
22
- // (DeepSeek 直接返 + DeepSeek shim 接 /anthropic 路径时也返 OAI-shape).
23
- export { isSseDoneSentinel, parseOaiChatCompletion, parseSseEvent, parseSseUsageField } from './parse.js';
24
- export { parsePricingConfig, loadPricingConfig, computeCost, PricingConfigParseError, } from './pricing-config.js';
25
- export { isLLMError } from './types.js';
26
- export { APIKeyMissingError, LLMRateLimitError, LLMAuthError, LLMNetworkError, LLMUnknownError, LLMStreamError, } from './types.js';
27
- //# sourceMappingURL=index.js.map
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,CAAC,MAAM,qBAAqB,GAAG,OAAO,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAEjG,gFAAgF;AAChF,8DAA8D;AAC9D,sCAAsC;AACtC,OAAO,EAAE,eAAe,EAAE,2BAA2B,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAE9G,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,yEAAyE;AACzE,+DAA+D;AAC/D,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAC1G,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,WAAW,EACX,uBAAuB,GACxB,MAAM,qBAAqB,CAAC;AAoB7B,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,EACZ,eAAe,EACf,eAAe,EACf,cAAc,GACf,MAAM,YAAY,CAAC"}
package/dist/parse.d.ts DELETED
@@ -1,53 +0,0 @@
1
- /**
2
- * @deepwhale/llm — OpenAI-shape 协议解析层
3
- *
4
- * Sprint 1b.5 Step 2 抽出来: 之前这些 module-level fn 在 deepseek-client.ts 里,
5
- * Step 2 起 AnthropicClient (走 /anthropic endpoint, DeepSeek shim 返 OpenAI-shape)
6
- * 也需要复用, 抽到 parse.ts 当共享解析层.
7
- *
8
- * 设计原则:
9
- * - 纯函数, 零副作用 (无 console / 无 I/O)
10
- * - pricing/model 都是 optional, 缺时走 R7 中间路径 (base 2 字段, cost 字段 absent)
11
- * - 不假设 caller 是 deepseek / anthropic, 任何返 OpenAI-shape JSON 的协议都走这层
12
- *
13
- * 不在这层的:
14
- * - SSE 协议层 (data:/event:/id:/retry: 协议) — 留在 deepseek-client.ts:isSseDoneSentinel
15
- * - HTTP / fetch / retry — caller 负责
16
- * - request 编组 (ChatMessage → wire JSON) — 留 caller (toWireMessage 是 deepseek 私货)
17
- */
18
- import type { ChatResult, ChatChunk, ModelId, Usage } from './types.js';
19
- import type { PricingConfig } from './pricing-config.js';
20
- /**
21
- * 解析 OAI chat completion 完整响应 JSON 为 ChatResult.
22
- * 返 null 表示响应结构无效 (缺 choices[0].message), caller 决定抛什么错.
23
- *
24
- * @param fallbackModel - 响应里没 model 字段时用的 fallback (避免 "unknown model" 传播)
25
- * @param pricing - 可选, 用于算 cost_turn + cost_currency. undefined 走 R7 中间路径.
26
- */
27
- export declare function parseOaiChatCompletion(json: unknown, fallbackModel: ModelId, pricing?: PricingConfig): ChatResult | null;
28
- /**
29
- * 判断 SSE event raw text 是否是 OAI 协议的 [DONE] 终止 marker.
30
- * 在 parseSseEvent 之前调用, 确保 [DONE] sentinel 不被算作 parse failure
31
- * (P2-D follow-up: 之前会被静默归入 sseParseFailures++, 正常流也刷 warn).
32
- *
33
- * 容忍: data: [DONE] / data:[DONE] / 多个 data: 行 / 前后空白 / CRLF.
34
- */
35
- export declare function isSseDoneSentinel(eventRaw: string): boolean;
36
- /**
37
- * 解析单个 SSE event (`data: {...}\n`).
38
- * 返回 null 表示跳过该 event (heartbeat / comment / 解析失败).
39
- *
40
- * 注意:[DONE] sentinel 由 isSseDoneSentinel 在调用方提前拦截,
41
- * 本函数不再处理,避免重复路径让 caller 误算 parse failure.
42
- */
43
- export declare function parseSseEvent(eventRaw: string, pricing?: PricingConfig, model?: ModelId): ChatChunk | null;
44
- /**
45
- * 从 SSE event JSON 顶层 usage 字段解析出标准化的 Usage 结构,
46
- * 顺手算 Sprint 1b 的 cache_hit_rate / cost_turn / tokens_uncached.
47
- *
48
- * 返回 undefined 表示该 event 不带 usage (OAI 标准: 只在 stream 末尾出现).
49
- *
50
- * P1 fix (2026-06-03): 抽出避免在 choices=[] 和 choices=[...] 两条路径上重复解析。
51
- */
52
- export declare function parseSseUsageField(obj: Record<string, unknown>, pricing?: PricingConfig, model?: ModelId): Usage | undefined;
53
- //# sourceMappingURL=parse.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAY,KAAK,EAAE,MAAM,YAAY,CAAC;AAElF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,OAAO,EACb,aAAa,EAAE,OAAO,EACtB,OAAO,CAAC,EAAE,aAAa,GACtB,UAAU,GAAG,IAAI,CAyFnB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAc3D;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,aAAa,EACvB,KAAK,CAAC,EAAE,OAAO,GACd,SAAS,GAAG,IAAI,CAsGlB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,OAAO,CAAC,EAAE,aAAa,EACvB,KAAK,CAAC,EAAE,OAAO,GACd,KAAK,GAAG,SAAS,CA+BnB"}