@deepwhale/llm 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/anthropic-client.d.ts +147 -0
- package/dist/anthropic-client.d.ts.map +1 -0
- package/dist/anthropic-client.js +443 -0
- package/dist/anthropic-client.js.map +1 -0
- package/dist/canonicalize-schema.d.ts +40 -0
- package/dist/canonicalize-schema.d.ts.map +1 -0
- package/dist/canonicalize-schema.js +93 -0
- package/dist/canonicalize-schema.js.map +1 -0
- package/dist/deepseek-client.d.ts +106 -0
- package/dist/deepseek-client.d.ts.map +1 -0
- package/dist/deepseek-client.js +395 -0
- package/dist/deepseek-client.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/parse.d.ts +53 -0
- package/dist/parse.d.ts.map +1 -0
- package/dist/parse.js +285 -0
- package/dist/parse.js.map +1 -0
- package/dist/pricing-config.d.ts +104 -0
- package/dist/pricing-config.d.ts.map +1 -0
- package/dist/pricing-config.js +186 -0
- package/dist/pricing-config.js.map +1 -0
- package/dist/pricing.default.toml +46 -0
- package/dist/types.d.ts +255 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +70 -0
- package/dist/types.js.map +1 -0
- package/package.json +28 -0
- package/src/pricing.default.toml +46 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical Schema (Prefix-cache 机制 4)
|
|
3
|
+
*
|
|
4
|
+
* 借鉴自 Reasonix `schema_canonicalize.go:10-67` —— OAI function-calling
|
|
5
|
+
* tool schema 在 build 前必须 key 顺序稳定, 否则 LLM 端 hash cache 抖动 →
|
|
6
|
+
* prefix-cache 命中率归零。
|
|
7
|
+
*
|
|
8
|
+
* 关键不变量:
|
|
9
|
+
* - object properties 字母序
|
|
10
|
+
* - required 数组字母序
|
|
11
|
+
* - enum 数组保持原序(enum 是有限值集合, 顺序是协议语义, 不能动)
|
|
12
|
+
* - 递归: nested object / array of object / array items
|
|
13
|
+
* - 纯函数 + 0 副作用 + 0 外部依赖
|
|
14
|
+
*
|
|
15
|
+
* Sprint 1b 范围: LLM 层, 不依赖 Tool 运行时类型
|
|
16
|
+
* (canonicalize 是 LLM wire-level 概念, 归属 @deepwhale/llm)
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* 把任意顺序的 tool schema 变成 key 顺序稳定的等价 schema。
|
|
20
|
+
*
|
|
21
|
+
* 返回**新**对象(深拷贝), 不修改入参。
|
|
22
|
+
* 同一 input 跑 N 次, output 严格相等(稳定性是 prefix-cache 的命根子)。
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* canonicalizeSchema({
|
|
26
|
+
* name: 'read',
|
|
27
|
+
* description: '...',
|
|
28
|
+
* parameters: {
|
|
29
|
+
* type: 'object',
|
|
30
|
+
* properties: { path: {...}, encoding: {...} },
|
|
31
|
+
* required: ['encoding', 'path'], // 任意顺序
|
|
32
|
+
* },
|
|
33
|
+
* })
|
|
34
|
+
* // =>
|
|
35
|
+
* // properties: { encoding: {...}, path: {...} } 字母序
|
|
36
|
+
* // required: ['encoding', 'path'] 字母序
|
|
37
|
+
*/
|
|
38
|
+
export function canonicalizeSchema(schema) {
|
|
39
|
+
return {
|
|
40
|
+
name: schema.name,
|
|
41
|
+
description: schema.description,
|
|
42
|
+
parameters: canonicalizeParameters(schema.parameters),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function canonicalizeParameters(p) {
|
|
46
|
+
// properties 字母序
|
|
47
|
+
const sortedPropKeys = Object.keys(p.properties).sort();
|
|
48
|
+
const sortedProperties = {};
|
|
49
|
+
for (const k of sortedPropKeys) {
|
|
50
|
+
const v = p.properties[k];
|
|
51
|
+
if (v !== undefined) {
|
|
52
|
+
sortedProperties[k] = canonicalizeParam(v);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// required 字母序(如果存在)
|
|
56
|
+
// LLMToolParametersSchema.required 是 readonly, 必须构造新对象
|
|
57
|
+
const out = {
|
|
58
|
+
type: 'object',
|
|
59
|
+
properties: sortedProperties,
|
|
60
|
+
};
|
|
61
|
+
if (p.required !== undefined) {
|
|
62
|
+
out.required = [...p.required].sort();
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
function canonicalizeParam(p) {
|
|
67
|
+
// 联合 narrowing: LLMToolParamSchema 没有 type='object' (object 用 LLMToolParametersSchema)
|
|
68
|
+
// 这里只处理 string/number/boolean/array
|
|
69
|
+
if (p.type === 'array') {
|
|
70
|
+
return { type: 'array', description: p.description, items: canonicalizeParam(p.items) };
|
|
71
|
+
}
|
|
72
|
+
if (p.type === 'string') {
|
|
73
|
+
if (p.enum !== undefined) {
|
|
74
|
+
// enum 保持原序: enum 是有限集合, 顺序是协议语义(后端可能按 index dispatch)
|
|
75
|
+
return { type: 'string', description: p.description, enum: [...p.enum] };
|
|
76
|
+
}
|
|
77
|
+
return { type: 'string', description: p.description };
|
|
78
|
+
}
|
|
79
|
+
if (p.type === 'number') {
|
|
80
|
+
const out = {
|
|
81
|
+
type: 'number',
|
|
82
|
+
description: p.description,
|
|
83
|
+
};
|
|
84
|
+
if (p.minimum !== undefined)
|
|
85
|
+
out.minimum = p.minimum;
|
|
86
|
+
if (p.maximum !== undefined)
|
|
87
|
+
out.maximum = p.maximum;
|
|
88
|
+
return out;
|
|
89
|
+
}
|
|
90
|
+
// boolean
|
|
91
|
+
return { type: 'boolean', description: p.description };
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=canonicalize-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonicalize-schema.js","sourceRoot":"","sources":["../src/canonicalize-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAqB;IACtD,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,UAAU,EAAE,sBAAsB,CAAC,MAAM,CAAC,UAAU,CAAC;KACtD,CAAC;AACJ,CAAC;AAED,SAAS,sBAAsB,CAAC,CAA0B;IACxD,iBAAiB;IACjB,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,IAAI,EAAE,CAAC;IACxD,MAAM,gBAAgB,GAAuC,EAAE,CAAC;IAChE,KAAK,MAAM,CAAC,IAAI,cAAc,EAAE,CAAC;QAC/B,MAAM,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAC1B,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;YACpB,gBAAgB,CAAC,CAAC,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC;IAED,qBAAqB;IACrB,uDAAuD;IACvD,MAAM,GAAG,GAAyG;QAChH,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE,gBAAgB;KAC7B,CAAC;IACF,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC7B,GAAG,CAAC,QAAQ,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;IACxC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,iBAAiB,CAAC,CAAqB;IAC9C,uFAAuF;IACvF,oCAAoC;IACpC,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QACvB,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;IAC1F,CAAC;IACD,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACxB,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YACzB,uDAAuD;YACvD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3E,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IACxD,CAAC;IACD,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACxB,MAAM,GAAG,GAAgF;YACvF,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,CAAC,CAAC,WAAW;SAC3B,CAAC;QACF,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS;YAAE,GAAG,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC;QACrD,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS;YAAE,GAAG,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC;QACrD,OAAO,GAAG,CAAC;IACb,CAAC;IACD,UAAU;IACV,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;AACzD,CAAC"}
|
|
@@ -0,0 +1,106 @@
|
|
|
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 { type PricingConfig } from './pricing-config.js';
|
|
18
|
+
import type { ChatChunk, ChatMessage, ChatResult, LLMClient, LLMToolSchema, ModelId } from './types.js';
|
|
19
|
+
/** DeepSeek 的 OpenAI 兼容端点。 */
|
|
20
|
+
export declare const DEEPSEEK_BASE_URL = "https://api.deepseek.com/v1";
|
|
21
|
+
/**
|
|
22
|
+
* 默认模型:DeepSeek-V4-Flash(V4 系列速度/成本优先版本)。
|
|
23
|
+
*
|
|
24
|
+
* 迁移背景:
|
|
25
|
+
* - V4 于 2026-04-24 preview 发布,2026-07-24 15:59 UTC 后 `deepseek-chat`
|
|
26
|
+
* 和 `deepseek-reasoner` 旧 alias 完全失效(hard-fail,不再是 deprecation warning)。
|
|
27
|
+
* - 官方推荐路径:保持 base_url 不变,把 model 字段改成 `deepseek-v4-flash` 或
|
|
28
|
+
* `deepseek-v4-pro`。
|
|
29
|
+
* - v4-flash 是 coding agentic 场景的官方推荐(速度/成本/工具调用优化)。
|
|
30
|
+
* - ref: https://api-docs.deepseek.com/news/news260424
|
|
31
|
+
*/
|
|
32
|
+
export declare const DEEPSEEK_DEFAULT_MODEL = "deepseek-v4-flash";
|
|
33
|
+
export interface DeepSeekClientOptions {
|
|
34
|
+
/** API key。优先于 process.env.DEEPSEEK_API_KEY。 */
|
|
35
|
+
apiKey?: string;
|
|
36
|
+
/** 模型 ID,默认 deepseek-v4-flash。 */
|
|
37
|
+
model?: string;
|
|
38
|
+
/** Base URL,默认 https://api.deepseek.com/v1。Sprint 1+ 用 mock server 时可换。 */
|
|
39
|
+
baseUrl?: string;
|
|
40
|
+
/** fetch 实现(注入 mock)。默认全局 fetch。 */
|
|
41
|
+
fetchImpl?: typeof fetch;
|
|
42
|
+
/** 单次 HTTP 调用的超时毫秒,默认 60s。 */
|
|
43
|
+
timeoutMs?: number;
|
|
44
|
+
/**
|
|
45
|
+
* 退避函数(注入 mock)。默认 `setTimeout`-based。Sprint 1a 测试用 fake timers 时注入。
|
|
46
|
+
* 返回 Promise,resolve 时表示"已等够,可以重试"。
|
|
47
|
+
*/
|
|
48
|
+
sleepFn?: (ms: number) => Promise<void>;
|
|
49
|
+
/** 自定义 abort 工厂(测试用 fake abort)。 */
|
|
50
|
+
makeAbortController?: () => AbortController;
|
|
51
|
+
/**
|
|
52
|
+
* Sprint 1b.5: 注入 pricing config. 缺省 = undefined → 走 R7 中间路径
|
|
53
|
+
* (base 2 字段, cost 字段 absent). caller 启动期 `await loadPricingConfig()`
|
|
54
|
+
* 拿到 PricingConfig 后传入. 不阻塞 constructor.
|
|
55
|
+
*/
|
|
56
|
+
pricing?: PricingConfig;
|
|
57
|
+
}
|
|
58
|
+
export declare class DeepSeekClient implements LLMClient {
|
|
59
|
+
readonly model: ModelId;
|
|
60
|
+
private readonly apiKey;
|
|
61
|
+
private readonly baseUrl;
|
|
62
|
+
private readonly fetchImpl;
|
|
63
|
+
private readonly timeoutMs;
|
|
64
|
+
private readonly sleepFn;
|
|
65
|
+
private readonly makeAbortController;
|
|
66
|
+
/**
|
|
67
|
+
* Sprint 1b.5: pricing config 注入. 缺省 = 启动期 sync 加载 ship-in `pricing.default.toml`.
|
|
68
|
+
* 加载失败 (file not found / parse error) → `undefined` → 走 R7 中间路径
|
|
69
|
+
* (base 2 字段, cost 字段 absent, 不静默 fallback).
|
|
70
|
+
*
|
|
71
|
+
* 用法: caller 显式 `pricing: await loadPricingConfig()` 可覆盖 ship-in (例如读用户 ~/.deepwhale/pricing.toml).
|
|
72
|
+
* `DeepSeekClientOptions.pricing` 字段让 caller 控制 (不阻塞 constructor).
|
|
73
|
+
*/
|
|
74
|
+
private readonly pricing;
|
|
75
|
+
constructor(options?: DeepSeekClientOptions);
|
|
76
|
+
chat(messages: ChatMessage[], options?: {
|
|
77
|
+
signal?: AbortSignal;
|
|
78
|
+
tools?: ReadonlyArray<LLMToolSchema>;
|
|
79
|
+
tool_choice?: 'auto' | 'none' | 'required';
|
|
80
|
+
}): Promise<ChatResult>;
|
|
81
|
+
stream(messages: ChatMessage[], options: {
|
|
82
|
+
signal?: AbortSignal;
|
|
83
|
+
tools?: ReadonlyArray<LLMToolSchema>;
|
|
84
|
+
tool_choice?: 'auto' | 'none' | 'required';
|
|
85
|
+
onChunk: (chunk: ChatChunk) => void;
|
|
86
|
+
}): Promise<ChatResult>;
|
|
87
|
+
private buildRequestBody;
|
|
88
|
+
private callWithRetry;
|
|
89
|
+
private parseJsonResponse;
|
|
90
|
+
private parseChatResponse;
|
|
91
|
+
private throwOnHttpError;
|
|
92
|
+
private wrapNetworkError;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* 从 OAI chat/completions 非流式响应里提取内容。
|
|
96
|
+
* 返回 null 表示结构异常。
|
|
97
|
+
*/
|
|
98
|
+
/**
|
|
99
|
+
* Step 2 起 parseOai* 3 个 module-level fn (parseOaiChatCompletion / parseSseEvent /
|
|
100
|
+
* parseSseUsageField) + isSseDoneSentinel 已抽到 parse.ts. 旧 import 位置保留
|
|
101
|
+
* 重导出给 1b 时代 test fixture 用 (deepseek-client.test.ts 没直接 import 它们,
|
|
102
|
+
* 是通过 client 调用间接, 但保险起见保留 export shell).
|
|
103
|
+
*
|
|
104
|
+
* Sprint 1b.5: 留空注释, 避免误删. 真实代码在 parse.ts.
|
|
105
|
+
*/
|
|
106
|
+
//# sourceMappingURL=deepseek-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deepseek-client.d.ts","sourceRoot":"","sources":["../src/deepseek-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAgBH,OAAO,EAEL,KAAK,aAAa,EACnB,MAAM,qBAAqB,CAAC;AAM7B,OAAO,KAAK,EACV,SAAS,EACT,WAAW,EACX,UAAU,EACV,SAAS,EAET,aAAa,EACb,OAAO,EAGR,MAAM,YAAY,CAAC;AAEpB,8BAA8B;AAC9B,eAAO,MAAM,iBAAiB,gCAAgC,CAAC;AAE/D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB,sBAAsB,CAAC;AAQ1D,MAAM,WAAW,qBAAqB;IACpC,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oCAAoC;IACpC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,8BAA8B;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,oCAAoC;IACpC,mBAAmB,CAAC,EAAE,MAAM,eAAe,CAAC;IAC5C;;;;OAIG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAED,qBAAa,cAAe,YAAW,SAAS;IAC9C,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;IACxD,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAwB;IAC5D;;;;;;;OAOG;IACH,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;gBAExC,OAAO,GAAE,qBAA0B;IAgBzC,IAAI,CACR,QAAQ,EAAE,WAAW,EAAE,EACvB,OAAO,GAAE;QACP,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB,KAAK,CAAC,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;QACrC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC;KACvC,GACL,OAAO,CAAC,UAAU,CAAC;IAYhB,MAAM,CACV,QAAQ,EAAE,WAAW,EAAE,EACvB,OAAO,EAAE;QACP,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB,KAAK,CAAC,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;QACrC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC;QAC3C,OAAO,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;KACrC,GACA,OAAO,CAAC,UAAU,CAAC;IAwHtB,OAAO,CAAC,gBAAgB;YA0BV,aAAa;YA4Db,iBAAiB;IAQ/B,OAAO,CAAC,iBAAiB;YAWX,gBAAgB;IAQ9B,OAAO,CAAC,gBAAgB;CAQzB;AAuED;;;GAGG;AACH;;;;;;;GAOG"}
|
|
@@ -0,0 +1,395 @@
|
|
|
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
|
+
let assembledToolCalls = [];
|
|
102
|
+
let usage;
|
|
103
|
+
let finishReason;
|
|
104
|
+
let lineCount = 0;
|
|
105
|
+
let chunkCount = 0;
|
|
106
|
+
let sseParseFailures = 0;
|
|
107
|
+
try {
|
|
108
|
+
while (true) {
|
|
109
|
+
const { value, done } = await reader.read();
|
|
110
|
+
if (done)
|
|
111
|
+
break;
|
|
112
|
+
lineCount += 1;
|
|
113
|
+
buffer += decoder.decode(value, { stream: true });
|
|
114
|
+
// SSE: event 间以双换行分隔。真实 SSE wire 可能是 LF 或 CRLF(部分 proxy / 旧服务端会发 CRLF)。
|
|
115
|
+
// Sprint 1a 修 P2-D:用正则一次匹配两种分隔,避免 CRLF 流被延迟到 flush。
|
|
116
|
+
// 注意:不能 split('\n\n') 然后用 '\r\n\r\n' split,否则会切错最后一个 event。
|
|
117
|
+
const SSE_DELIM_RE = /\r?\n\r?\n/;
|
|
118
|
+
let m;
|
|
119
|
+
// lastIndex 在循环里持续推进,直到无匹配
|
|
120
|
+
const delimRe = new RegExp(SSE_DELIM_RE.source, 'g');
|
|
121
|
+
while ((m = delimRe.exec(buffer)) !== null) {
|
|
122
|
+
const event = buffer.slice(0, m.index);
|
|
123
|
+
buffer = buffer.slice(m.index + m[0].length);
|
|
124
|
+
delimRe.lastIndex = 0; // 每次切完后 buffer 头部变了,从头再来
|
|
125
|
+
// P2-D follow-up:[DONE] sentinel 是 OAI 协议正常终止标记,绝不能算 parse failure。
|
|
126
|
+
// 必须在 parseSseEvent 之前拦截,否则正常流也会刷 warn,污染 stderr 日志。
|
|
127
|
+
// 其他返回 null 的情况(heartbeat / comment / JSON 损坏)才是真正需要 warn 的失败。
|
|
128
|
+
if (isSseDoneSentinel(event))
|
|
129
|
+
continue;
|
|
130
|
+
// Sprint 1b.5: 透传 pricing + model 给 module-level parse fn.
|
|
131
|
+
const parsed = parseSseEvent(event, this.pricing, this.model);
|
|
132
|
+
if (parsed === null) {
|
|
133
|
+
sseParseFailures += 1;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
chunkCount += 1;
|
|
137
|
+
// Sprint 1a:content 增量直接累加
|
|
138
|
+
if (parsed.delta.content) {
|
|
139
|
+
assembledContent += parsed.delta.content;
|
|
140
|
+
}
|
|
141
|
+
if (parsed.delta.tool_calls) {
|
|
142
|
+
// Sprint 1a 一次性返回完整,直接覆盖(DeepSeek V4 流式 tool_calls 也是完整结构)
|
|
143
|
+
assembledToolCalls = [...parsed.delta.tool_calls];
|
|
144
|
+
}
|
|
145
|
+
if (parsed.usage)
|
|
146
|
+
usage = parsed.usage;
|
|
147
|
+
if (parsed.finish_reason)
|
|
148
|
+
finishReason = parsed.finish_reason;
|
|
149
|
+
options.onChunk(parsed);
|
|
150
|
+
}
|
|
151
|
+
// 切完后 buffer 还可能含一个未结束的 event(等下一个 chunk 或 flush)
|
|
152
|
+
// lastIndex 已经被 reset 为 0;这里把 buffer 截到 delimRe.lastIndex(但 delimRe 已经停,buffer 已经是剩余)
|
|
153
|
+
// 不需要额外处理,下一轮 read() 时再切。
|
|
154
|
+
// 但如果 delimRe.exec 一次都没匹配,buffer 没变;下次 read 直接 append。
|
|
155
|
+
}
|
|
156
|
+
// 末尾 flush buffer
|
|
157
|
+
if (buffer.trim().length > 0) {
|
|
158
|
+
// 同样先看 [DONE] sentinel,再走 parseSseEvent
|
|
159
|
+
// Sprint 1b.5 Step 2.5 (F6 拍板, review 2026-06-03 找到): 之前 flush 路径漏传
|
|
160
|
+
// this.pricing + this.model, 最后一个 SSE usage event 没尾随双换行时会丢
|
|
161
|
+
// cost_turn/cost_currency. 修法: 跟正常 path 一致传 this.pricing, this.model.
|
|
162
|
+
if (!isSseDoneSentinel(buffer)) {
|
|
163
|
+
const parsed = parseSseEvent(buffer, this.pricing, this.model);
|
|
164
|
+
if (parsed) {
|
|
165
|
+
chunkCount += 1;
|
|
166
|
+
if (parsed.delta.content)
|
|
167
|
+
assembledContent += parsed.delta.content;
|
|
168
|
+
if (parsed.delta.tool_calls)
|
|
169
|
+
assembledToolCalls = [...parsed.delta.tool_calls];
|
|
170
|
+
if (parsed.usage)
|
|
171
|
+
usage = parsed.usage;
|
|
172
|
+
if (parsed.finish_reason)
|
|
173
|
+
finishReason = parsed.finish_reason;
|
|
174
|
+
options.onChunk(parsed);
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
sseParseFailures += 1;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// Sprint 1a 修 P2-D:JSON parse 失败不再静默,在 stderr 留一行 warn(运维排查用)。
|
|
182
|
+
if (sseParseFailures > 0) {
|
|
183
|
+
process.stderr.write(`[deepwhale] warn: SSE parse failures: ${sseParseFailures} (stream still completed)\n`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
catch (err) {
|
|
187
|
+
throw new LLMStreamError(`SSE stream interrupted after ${lineCount} lines / ${chunkCount} chunks: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
188
|
+
}
|
|
189
|
+
const result = {
|
|
190
|
+
model: this.model,
|
|
191
|
+
content: assembledContent,
|
|
192
|
+
};
|
|
193
|
+
if (assembledToolCalls.length > 0)
|
|
194
|
+
result.tool_calls = assembledToolCalls;
|
|
195
|
+
if (usage)
|
|
196
|
+
result.usage = usage;
|
|
197
|
+
if (finishReason)
|
|
198
|
+
result.finish_reason = finishReason;
|
|
199
|
+
return result;
|
|
200
|
+
}
|
|
201
|
+
// ==========================================================================
|
|
202
|
+
// 内部:HTTP + retry
|
|
203
|
+
// ==========================================================================
|
|
204
|
+
buildRequestBody(messages, tools, toolChoice, stream) {
|
|
205
|
+
return {
|
|
206
|
+
model: this.model,
|
|
207
|
+
messages: messages.map(toWireMessage),
|
|
208
|
+
stream,
|
|
209
|
+
// P1 fix (2026-06-03): OAI/DeepSeek 在 stream=true 时, 必须在 body 里
|
|
210
|
+
// 显式带 stream_options.include_usage=true, 服务端才在最后一个 chunk
|
|
211
|
+
// (可能带 choices 也可能不带) 上携带 usage 字段。
|
|
212
|
+
// 不然 cache_hit_rate / cost_turn / tokens_uncached 在 stream 路径全部拿不到。
|
|
213
|
+
// ref: https://api-docs.deepseek.com/api/create-chat-completion
|
|
214
|
+
...(stream ? { stream_options: { include_usage: true } } : {}),
|
|
215
|
+
// 机制 2:content="" 永远序列化(OAI spec 允许,但 prefix-cache hash 会变,
|
|
216
|
+
// 所以这里强制把空字符串序列化为 "",绝不带 omitempty)
|
|
217
|
+
// 实际由 toWireMessage 完成 — 这里只是注释提醒调用者。
|
|
218
|
+
...(tools && tools.length > 0
|
|
219
|
+
? { tools: tools.map((t) => ({ type: 'function', function: t })) }
|
|
220
|
+
: {}),
|
|
221
|
+
...(toolChoice ? { tool_choice: toolChoice } : {}),
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
async callWithRetry(body, externalSignal) {
|
|
225
|
+
const url = `${this.baseUrl}/chat/completions`;
|
|
226
|
+
let lastError = null;
|
|
227
|
+
for (let attempt = 0; attempt < MAX_RETRIES; attempt += 1) {
|
|
228
|
+
// 每次重试一个独立 AbortController(timeout 重新计)
|
|
229
|
+
const timeoutController = this.makeAbortController();
|
|
230
|
+
const timer = setTimeout(() => timeoutController.abort(new Error('timeout')), this.timeoutMs);
|
|
231
|
+
const combinedSignal = externalSignal !== undefined
|
|
232
|
+
? AbortSignal.any([externalSignal, timeoutController.signal])
|
|
233
|
+
: timeoutController.signal;
|
|
234
|
+
try {
|
|
235
|
+
const res = await this.fetchImpl(url, {
|
|
236
|
+
method: 'POST',
|
|
237
|
+
headers: {
|
|
238
|
+
'Content-Type': 'application/json',
|
|
239
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
240
|
+
},
|
|
241
|
+
body: JSON.stringify(body),
|
|
242
|
+
signal: combinedSignal,
|
|
243
|
+
});
|
|
244
|
+
if (res.ok)
|
|
245
|
+
return res;
|
|
246
|
+
// 5xx/429 → retry;4xx 其他 → 立即抛
|
|
247
|
+
if (RETRYABLE_STATUSES.has(res.status) && attempt < MAX_RETRIES - 1) {
|
|
248
|
+
// 读 body 留作错误诊断(不 await 不算 leak,GC 会清)
|
|
249
|
+
const errBody = await res.text().catch(() => '');
|
|
250
|
+
lastError = new Error(`HTTP ${res.status}: ${errBody.slice(0, 200)}`);
|
|
251
|
+
await this.sleepFn(BASE_BACKOFF_MS * 2 ** attempt);
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
await this.throwOnHttpError(res);
|
|
255
|
+
}
|
|
256
|
+
catch (err) {
|
|
257
|
+
// 4xx 已被 throwOnHttpError 处理,这里的 err 是 network/timeout/abort
|
|
258
|
+
if (isLLMError(err))
|
|
259
|
+
throw err; // 4xx 不重试
|
|
260
|
+
lastError = err;
|
|
261
|
+
// 已被外部 signal 取消 → 立即退出
|
|
262
|
+
if (externalSignal?.aborted) {
|
|
263
|
+
throw new LLMNetworkError('Request aborted by caller', { cause: err });
|
|
264
|
+
}
|
|
265
|
+
// timeout / network → retry
|
|
266
|
+
if (attempt < MAX_RETRIES - 1) {
|
|
267
|
+
await this.sleepFn(BASE_BACKOFF_MS * 2 ** attempt);
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
throw this.wrapNetworkError(err);
|
|
271
|
+
}
|
|
272
|
+
finally {
|
|
273
|
+
clearTimeout(timer);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
// 理论不会到这(最后一次循环要么 return 要么 throw)
|
|
277
|
+
throw this.wrapNetworkError(lastError);
|
|
278
|
+
}
|
|
279
|
+
async parseJsonResponse(res) {
|
|
280
|
+
try {
|
|
281
|
+
return await res.json();
|
|
282
|
+
}
|
|
283
|
+
catch (err) {
|
|
284
|
+
throw new LLMUnknownError('Failed to parse DeepSeek response as JSON', { cause: err });
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
parseChatResponse(json, status) {
|
|
288
|
+
// Sprint 1b.5: 透传 this.pricing 给 module-level parseOaiChatCompletion,
|
|
289
|
+
// 让 computeCost 能找到 model pricing 算 cost. 不传 pricing 走 R7 中间路径
|
|
290
|
+
// (cost 字段 absent, base 2 字段仍在).
|
|
291
|
+
const parsed = parseOaiChatCompletion(json, this.model, this.pricing);
|
|
292
|
+
if (parsed === null) {
|
|
293
|
+
throw new LLMUnknownError('DeepSeek response missing choices[0].message', { status });
|
|
294
|
+
}
|
|
295
|
+
return parsed;
|
|
296
|
+
}
|
|
297
|
+
async throwOnHttpError(res) {
|
|
298
|
+
const text = await res.text().catch(() => '');
|
|
299
|
+
const message = `DeepSeek API error ${res.status}: ${text.slice(0, 200)}`;
|
|
300
|
+
if (res.status === 429)
|
|
301
|
+
throw new LLMRateLimitError(message);
|
|
302
|
+
if (res.status === 401 || res.status === 403)
|
|
303
|
+
throw new LLMAuthError(res.status, message);
|
|
304
|
+
throw new LLMUnknownError(message, { status: res.status });
|
|
305
|
+
}
|
|
306
|
+
wrapNetworkError(err) {
|
|
307
|
+
if (isLLMError(err))
|
|
308
|
+
return err;
|
|
309
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
310
|
+
if (msg.includes('abort') || msg.includes('AbortError')) {
|
|
311
|
+
return new LLMNetworkError(`Request aborted: ${msg}`, { cause: err });
|
|
312
|
+
}
|
|
313
|
+
return new LLMNetworkError(`Network error: ${msg}`, { cause: err });
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
// ============================================================================
|
|
317
|
+
// 内部 helper: sync 加载 ship-in pricing.default.toml
|
|
318
|
+
// ============================================================================
|
|
319
|
+
/**
|
|
320
|
+
* Sprint 1b.5: 启动期 sync 加载 ship-in `pricing.default.toml`.
|
|
321
|
+
*
|
|
322
|
+
* 设计:
|
|
323
|
+
* - sync 加载 (1 次启动, 不在 hot path, 符合 R3)
|
|
324
|
+
* - 加载失败 (file not found / parse error) → 返回 `undefined` → caller 走 R7 中间路径
|
|
325
|
+
* (不静默 fallback 到硬编码, 不抛错, base 2 字段, cost 字段 absent)
|
|
326
|
+
* - 文件路径解析: Use sibling pricing.default.toml relative to package location. (Q 拍板 2026-06-04, Task 2 Gap 5 留痕)
|
|
327
|
+
* 走 `import.meta.url` → 当前文件 → 同目录 `pricing.default.toml`
|
|
328
|
+
* (dev 走 src/, build 后走 dist/, 路径自动对齐;
|
|
329
|
+
* **不**走 cwd / XDG config dir / env var, 避免 dev-vs-prod 不一致)
|
|
330
|
+
*
|
|
331
|
+
* 测试环境 (vitest) 时 pricing.default.toml 跟 src/ 在一起, 也能加载。
|
|
332
|
+
*/
|
|
333
|
+
function loadDefaultPricing() {
|
|
334
|
+
try {
|
|
335
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
336
|
+
const defaultPath = resolve(here, 'pricing.default.toml');
|
|
337
|
+
const tomlText = readFileSync(defaultPath, 'utf-8');
|
|
338
|
+
return parsePricingConfig(tomlText);
|
|
339
|
+
}
|
|
340
|
+
catch {
|
|
341
|
+
// 任何错误 (file not found / parse error / 权限) → 不让 client 启动失败.
|
|
342
|
+
// 走 R7 中间路径: base 2 字段, cost 字段 absent.
|
|
343
|
+
return undefined;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
// ============================================================================
|
|
347
|
+
// 内部 helper:wire 消息转换 + 响应解析 + SSE 解析
|
|
348
|
+
// ============================================================================
|
|
349
|
+
/**
|
|
350
|
+
* 把 ChatMessage 转成 OAI wire 格式。
|
|
351
|
+
*
|
|
352
|
+
* 关键不变量(Prefix-cache 机制 2/3):
|
|
353
|
+
* - `content: ""` 永远序列化为 ""(不带 omitempty)
|
|
354
|
+
* - reasoning_content 字段不打 wire(即使 LLM 返回了也丢掉 — Sprint 1a 简化)
|
|
355
|
+
* - tool_calls 必带 type:'function' 包装
|
|
356
|
+
*/
|
|
357
|
+
function toWireMessage(m) {
|
|
358
|
+
if (m.role === 'tool') {
|
|
359
|
+
return {
|
|
360
|
+
role: 'tool',
|
|
361
|
+
content: m.content ?? '',
|
|
362
|
+
tool_call_id: m.tool_call_id ?? '',
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
if (m.role === 'assistant' && m.tool_calls && m.tool_calls.length > 0) {
|
|
366
|
+
return {
|
|
367
|
+
role: 'assistant',
|
|
368
|
+
content: m.content ?? '', // 机制 2:永远序列化
|
|
369
|
+
tool_calls: m.tool_calls.map((tc) => ({
|
|
370
|
+
id: tc.id,
|
|
371
|
+
type: 'function',
|
|
372
|
+
function: {
|
|
373
|
+
name: tc.name,
|
|
374
|
+
// OAI wire 上 args 是 string,我们这里已经是 object,JSON.stringify 转回去
|
|
375
|
+
arguments: JSON.stringify(tc.args),
|
|
376
|
+
},
|
|
377
|
+
})),
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
return { role: m.role, content: m.content ?? '' };
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* 从 OAI chat/completions 非流式响应里提取内容。
|
|
384
|
+
* 返回 null 表示结构异常。
|
|
385
|
+
*/
|
|
386
|
+
/**
|
|
387
|
+
* Step 2 起 parseOai* 3 个 module-level fn (parseOaiChatCompletion / parseSseEvent /
|
|
388
|
+
* parseSseUsageField) + isSseDoneSentinel 已抽到 parse.ts. 旧 import 位置保留
|
|
389
|
+
* 重导出给 1b 时代 test fixture 用 (deepseek-client.test.ts 没直接 import 它们,
|
|
390
|
+
* 是通过 client 调用间接, 但保险起见保留 export shell).
|
|
391
|
+
*
|
|
392
|
+
* Sprint 1b.5: 留空注释, 避免误删. 真实代码在 parse.ts.
|
|
393
|
+
*/
|
|
394
|
+
// (parseOaiChatCompletion / isSseDoneSentinel / parseSseEvent / parseSseUsageField) moved to ./parse.ts
|
|
395
|
+
//# sourceMappingURL=deepseek-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deepseek-client.js","sourceRoot":"","sources":["../src/deepseek-client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|