@wwlocal/aibot-plugin-node 20260409.20.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.
Files changed (93) hide show
  1. package/README.md +489 -0
  2. package/config.example.json +169 -0
  3. package/dist/cjs/index.js +76 -0
  4. package/dist/cjs/src/adapters/anthropic-adapter.js +534 -0
  5. package/dist/cjs/src/adapters/base-adapter.js +176 -0
  6. package/dist/cjs/src/adapters/deepseek-adapter.js +328 -0
  7. package/dist/cjs/src/adapters/dify-adapter.js +636 -0
  8. package/dist/cjs/src/adapters/index.js +131 -0
  9. package/dist/cjs/src/adapters/openai-adapter.js +361 -0
  10. package/dist/cjs/src/adapters/webhook-adapter.js +260 -0
  11. package/dist/cjs/src/agent-forwarder.js +87 -0
  12. package/dist/cjs/src/ca-cert.js +162 -0
  13. package/dist/cjs/src/config.js +169 -0
  14. package/dist/cjs/src/const.js +124 -0
  15. package/dist/cjs/src/conversation-manager.js +147 -0
  16. package/dist/cjs/src/dm-policy.js +46 -0
  17. package/dist/cjs/src/group-policy.js +95 -0
  18. package/dist/cjs/src/media-handler.js +136 -0
  19. package/dist/cjs/src/media-loader.js +271 -0
  20. package/dist/cjs/src/media-storage.js +165 -0
  21. package/dist/cjs/src/media-uploader.js +203 -0
  22. package/dist/cjs/src/message-parser.js +133 -0
  23. package/dist/cjs/src/message-sender.js +87 -0
  24. package/dist/cjs/src/monitor.js +849 -0
  25. package/dist/cjs/src/reqid-store.js +87 -0
  26. package/dist/cjs/src/server.js +72 -0
  27. package/dist/cjs/src/service-manager.js +135 -0
  28. package/dist/cjs/src/state-manager.js +143 -0
  29. package/dist/cjs/src/template-card-parser.js +498 -0
  30. package/dist/cjs/src/timeout.js +41 -0
  31. package/dist/cjs/src/version.js +25 -0
  32. package/dist/esm/index.js +74 -0
  33. package/dist/esm/src/adapters/anthropic-adapter.js +512 -0
  34. package/dist/esm/src/adapters/base-adapter.js +174 -0
  35. package/dist/esm/src/adapters/deepseek-adapter.js +326 -0
  36. package/dist/esm/src/adapters/dify-adapter.js +634 -0
  37. package/dist/esm/src/adapters/index.js +123 -0
  38. package/dist/esm/src/adapters/openai-adapter.js +339 -0
  39. package/dist/esm/src/adapters/webhook-adapter.js +258 -0
  40. package/dist/esm/src/agent-forwarder.js +84 -0
  41. package/dist/esm/src/ca-cert.js +136 -0
  42. package/dist/esm/src/config.js +145 -0
  43. package/dist/esm/src/const.js +100 -0
  44. package/dist/esm/src/conversation-manager.js +144 -0
  45. package/dist/esm/src/dm-policy.js +44 -0
  46. package/dist/esm/src/group-policy.js +92 -0
  47. package/dist/esm/src/media-handler.js +133 -0
  48. package/dist/esm/src/media-loader.js +246 -0
  49. package/dist/esm/src/media-storage.js +143 -0
  50. package/dist/esm/src/media-uploader.js +198 -0
  51. package/dist/esm/src/message-parser.js +131 -0
  52. package/dist/esm/src/message-sender.js +83 -0
  53. package/dist/esm/src/monitor.js +841 -0
  54. package/dist/esm/src/reqid-store.js +85 -0
  55. package/dist/esm/src/server.js +69 -0
  56. package/dist/esm/src/service-manager.js +133 -0
  57. package/dist/esm/src/state-manager.js +134 -0
  58. package/dist/esm/src/template-card-parser.js +495 -0
  59. package/dist/esm/src/timeout.js +38 -0
  60. package/dist/esm/src/version.js +22 -0
  61. package/dist/esm/types/index.d.ts +14 -0
  62. package/dist/esm/types/src/adapters/anthropic-adapter.d.ts +93 -0
  63. package/dist/esm/types/src/adapters/base-adapter.d.ts +76 -0
  64. package/dist/esm/types/src/adapters/deepseek-adapter.d.ts +87 -0
  65. package/dist/esm/types/src/adapters/dify-adapter.d.ts +100 -0
  66. package/dist/esm/types/src/adapters/index.d.ts +60 -0
  67. package/dist/esm/types/src/adapters/openai-adapter.d.ts +82 -0
  68. package/dist/esm/types/src/adapters/types.d.ts +373 -0
  69. package/dist/esm/types/src/adapters/webhook-adapter.d.ts +54 -0
  70. package/dist/esm/types/src/agent-forwarder.d.ts +32 -0
  71. package/dist/esm/types/src/ca-cert.d.ts +53 -0
  72. package/dist/esm/types/src/config.d.ts +29 -0
  73. package/dist/esm/types/src/const.d.ts +74 -0
  74. package/dist/esm/types/src/conversation-manager.d.ts +81 -0
  75. package/dist/esm/types/src/dm-policy.d.ts +27 -0
  76. package/dist/esm/types/src/group-policy.d.ts +28 -0
  77. package/dist/esm/types/src/interface.d.ts +332 -0
  78. package/dist/esm/types/src/media-handler.d.ts +36 -0
  79. package/dist/esm/types/src/media-loader.d.ts +47 -0
  80. package/dist/esm/types/src/media-storage.d.ts +35 -0
  81. package/dist/esm/types/src/media-uploader.d.ts +65 -0
  82. package/dist/esm/types/src/message-parser.d.ts +89 -0
  83. package/dist/esm/types/src/message-sender.d.ts +34 -0
  84. package/dist/esm/types/src/monitor.d.ts +30 -0
  85. package/dist/esm/types/src/reqid-store.d.ts +23 -0
  86. package/dist/esm/types/src/server.d.ts +23 -0
  87. package/dist/esm/types/src/service-manager.d.ts +52 -0
  88. package/dist/esm/types/src/state-manager.d.ts +76 -0
  89. package/dist/esm/types/src/template-card-parser.d.ts +18 -0
  90. package/dist/esm/types/src/timeout.d.ts +20 -0
  91. package/dist/esm/types/src/version.d.ts +2 -0
  92. package/dist/index.d.ts +2 -0
  93. package/package.json +51 -0
@@ -0,0 +1,326 @@
1
+ import { OpenAIAdapter } from './openai-adapter.js';
2
+
3
+ /**
4
+ * DeepSeek 适配器
5
+ *
6
+ * 适用于:DeepSeek Chat (deepseek-chat) 和 DeepSeek Reasoner (deepseek-reasoner) 模型
7
+ *
8
+ * 与 OpenAI 适配器的差异:
9
+ * - 响应中额外包含 reasoning_content 字段(思维链)
10
+ * - 支持 reasoningDisplay 配置:show/hide/collapse(默认)
11
+ * - Reasoner 模型自动添加 thinking 参数
12
+ *
13
+ * 推理内容折叠展示格式(collapse 模式):
14
+ * <think>
15
+ * {reasoning_content}
16
+ * </think>
17
+ *
18
+ * {content}
19
+ */
20
+ /**
21
+ * DeepSeek 适配器
22
+ */
23
+ class DeepSeekAdapter extends OpenAIAdapter {
24
+ constructor() {
25
+ super(...arguments);
26
+ this.name = "deepseek";
27
+ this.displayName = "DeepSeek";
28
+ // 累积的推理内容(用于流式处理)
29
+ this.accumulatedReasoning = "";
30
+ }
31
+ /**
32
+ * 获取推理内容展示模式
33
+ */
34
+ getReasoningDisplayMode(endpoint) {
35
+ const options = endpoint.providerOptions;
36
+ return options?.reasoningDisplay || "collapse";
37
+ }
38
+ /**
39
+ * 是否为 Reasoner 模型
40
+ */
41
+ isReasonerModel(endpoint) {
42
+ return endpoint.model?.includes("reasoner") || false;
43
+ }
44
+ /**
45
+ * 构建请求体(覆写以添加 thinking 参数)
46
+ */
47
+ buildRequestBody(messages, endpoint) {
48
+ const requestBody = super.buildRequestBody(messages, endpoint);
49
+ // Reasoner 模型自动启用 thinking
50
+ if (this.isReasonerModel(endpoint)) {
51
+ requestBody.thinking = { type: "enabled" };
52
+ }
53
+ return requestBody;
54
+ }
55
+ /**
56
+ * 解析流式 delta 内容(覆写以处理 reasoning_content)
57
+ */
58
+ parseStreamDelta(parsed) {
59
+ const delta = parsed.choices?.[0]?.delta;
60
+ return {
61
+ content: delta?.content,
62
+ reasoningContent: delta?.reasoning_content,
63
+ };
64
+ }
65
+ /**
66
+ * 累积内容(覆写以处理推理内容)
67
+ *
68
+ * 注意:此方法需要知道 endpoint 配置,但父类签名不包含
69
+ * 因此使用实例变量 accumulatedReasoning 来跨调用保持状态
70
+ */
71
+ accumulateContent(accumulated, content, reasoningContent) {
72
+ // 累积推理内容
73
+ if (reasoningContent) {
74
+ this.accumulatedReasoning += reasoningContent;
75
+ }
76
+ // 累积主内容
77
+ if (content) {
78
+ // 注意:这里无法直接获取 displayMode
79
+ // 实际的格式化在 formatFinalOutput 中处理
80
+ return accumulated + content;
81
+ }
82
+ return accumulated;
83
+ }
84
+ /**
85
+ * 格式化最终输出(根据 displayMode 配置)
86
+ */
87
+ formatOutput(content, reasoningContent, displayMode) {
88
+ if (!reasoningContent) {
89
+ return content;
90
+ }
91
+ switch (displayMode) {
92
+ case "show":
93
+ // 直接展示推理内容和回答
94
+ return `${reasoningContent}\n\n${content}`;
95
+ case "hide":
96
+ // 仅展示回答,隐藏推理过程
97
+ return content;
98
+ case "collapse":
99
+ default:
100
+ // 折叠展示
101
+ return `<think>\n${reasoningContent}\n</think>\n\n${content}`;
102
+ }
103
+ }
104
+ /**
105
+ * 处理流式响应(覆写以支持推理内容格式化)
106
+ */
107
+ async handleStreamResponse(params) {
108
+ // 重置累积状态
109
+ this.accumulatedReasoning = "";
110
+ const result = await super.handleStreamResponse(params);
111
+ // 如果有推理内容,需要重新格式化输出
112
+ if (this.accumulatedReasoning && params.endpoint) {
113
+ const displayMode = this.getReasoningDisplayMode(params.endpoint);
114
+ return this.formatOutput(result, this.accumulatedReasoning, displayMode);
115
+ }
116
+ return result;
117
+ }
118
+ /**
119
+ * 解析非流式响应内容(覆写以处理 reasoning_content)
120
+ */
121
+ parseNonStreamResponse(data) {
122
+ const message = data.choices?.[0]?.message;
123
+ const content = message?.content || "";
124
+ const reasoningContent = message?.reasoning_content || "";
125
+ // 非流式时无法获取 endpoint,使用默认 collapse 模式
126
+ // 实际调用时应通过 forward 方法传递正确的 displayMode
127
+ if (reasoningContent) {
128
+ return this.formatOutput(content, reasoningContent, "collapse");
129
+ }
130
+ return content;
131
+ }
132
+ /**
133
+ * 转发请求(覆写以传递 endpoint 到流式处理)
134
+ */
135
+ async forward(request, endpoint, callbacks) {
136
+ const { text, mediaPaths, quoteContent, abortSignal, runtime, historyMessages } = request;
137
+ const { deliver, onReplyStart, onError } = callbacks;
138
+ this.log(runtime, `Forwarding to DeepSeek: url=${endpoint.url}, model=${endpoint.model}`);
139
+ // 重置状态
140
+ this.accumulatedReasoning = "";
141
+ // 构建消息
142
+ const messages = this.buildMessages({
143
+ text,
144
+ mediaPaths,
145
+ quoteContent,
146
+ systemPrompt: endpoint.systemPrompt,
147
+ historyMessages,
148
+ });
149
+ // 构建请求体
150
+ const requestBody = this.buildRequestBody(messages, endpoint);
151
+ // 构建请求头
152
+ const headers = this.buildHeaders(endpoint);
153
+ // 超时控制
154
+ const timeoutMs = endpoint.timeoutMs || 300000;
155
+ const { controller, timeoutId } = this.createTimeoutController(timeoutMs, abortSignal);
156
+ try {
157
+ const response = await this.fetchWithErrorHandling(endpoint.url, {
158
+ method: "POST",
159
+ headers,
160
+ body: JSON.stringify(requestBody),
161
+ signal: controller.signal,
162
+ }, runtime);
163
+ let finalText;
164
+ const displayMode = this.getReasoningDisplayMode(endpoint);
165
+ if (requestBody.stream) {
166
+ // 流式响应
167
+ finalText = await this.handleDeepSeekStreamResponse({
168
+ response,
169
+ deliver,
170
+ onReplyStart,
171
+ onError,
172
+ runtime,
173
+ displayMode,
174
+ });
175
+ }
176
+ else {
177
+ // 非流式响应
178
+ finalText = await this.handleDeepSeekNonStreamResponse({
179
+ response,
180
+ deliver,
181
+ onReplyStart,
182
+ onError,
183
+ runtime,
184
+ displayMode,
185
+ });
186
+ }
187
+ // 最终 deliver
188
+ if (finalText) {
189
+ try {
190
+ await deliver({ text: finalText }, { kind: "final" });
191
+ }
192
+ catch (e) {
193
+ onError?.(e instanceof Error ? e : new Error(String(e)), { kind: "final" });
194
+ }
195
+ }
196
+ this.log(runtime, `Response complete: textLength=${finalText.length}`);
197
+ return finalText;
198
+ }
199
+ catch (err) {
200
+ if (err?.name === "AbortError") {
201
+ const error = new Error(`Request timed out after ${timeoutMs}ms`);
202
+ this.logError(runtime, error.message);
203
+ onError?.(error, { kind: "timeout" });
204
+ throw error;
205
+ }
206
+ throw err;
207
+ }
208
+ finally {
209
+ clearTimeout(timeoutId);
210
+ }
211
+ }
212
+ /**
213
+ * 处理 DeepSeek 流式响应
214
+ *
215
+ * 注意:节流逻辑已移至 monitor 层统一控制,适配器层每个有效 delta 都直接 deliver
216
+ */
217
+ async handleDeepSeekStreamResponse(params) {
218
+ const { response, deliver, onReplyStart, onError, runtime, displayMode } = params;
219
+ const body = response.body;
220
+ if (!body) {
221
+ throw new Error("Response has no body for streaming");
222
+ }
223
+ const reader = body.getReader();
224
+ let accumulatedContent = "";
225
+ let accumulatedReasoning = "";
226
+ let replyStarted = false;
227
+ try {
228
+ for await (const { data } of this.readSSEStream(reader, runtime)) {
229
+ if (data === "[DONE]") {
230
+ this.log(runtime, "SSE stream [DONE]");
231
+ continue;
232
+ }
233
+ let parsed;
234
+ try {
235
+ parsed = JSON.parse(data);
236
+ }
237
+ catch {
238
+ this.log(runtime, `Failed to parse SSE JSON: ${data.slice(0, 200)}`);
239
+ continue;
240
+ }
241
+ const { content, reasoningContent } = this.parseStreamDelta(parsed);
242
+ // 累积推理内容
243
+ if (reasoningContent) {
244
+ accumulatedReasoning += reasoningContent;
245
+ }
246
+ // 累积主内容
247
+ if (content) {
248
+ accumulatedContent += content;
249
+ }
250
+ // 判断是否有新增内容
251
+ // - hide 模式下:仅当有主内容 (content) 时才考虑 deliver
252
+ // - show/collapse 模式下:有推理内容或主内容都考虑
253
+ const hasNewContent = displayMode === "hide" ? !!content : !!(content || reasoningContent);
254
+ if (hasNewContent) {
255
+ // 首次收到有效内容时触发 onReplyStart
256
+ if (!replyStarted) {
257
+ replyStarted = true;
258
+ try {
259
+ await onReplyStart?.();
260
+ }
261
+ catch (e) {
262
+ this.logError(runtime, `onReplyStart error: ${String(e)}`);
263
+ }
264
+ }
265
+ // 直接 deliver,节流由 monitor 层统一控制
266
+ const formattedText = this.formatOutput(accumulatedContent, accumulatedReasoning, displayMode);
267
+ try {
268
+ await deliver({ text: formattedText }, { kind: "block" });
269
+ }
270
+ catch (e) {
271
+ onError?.(e instanceof Error ? e : new Error(String(e)), { kind: "block" });
272
+ }
273
+ }
274
+ const finishReason = parsed.choices?.[0]?.finish_reason;
275
+ if (finishReason && finishReason !== "null") {
276
+ this.log(runtime, `SSE finish_reason: ${finishReason}`);
277
+ }
278
+ }
279
+ }
280
+ catch (err) {
281
+ this.logError(runtime, `SSE stream error: ${String(err)}`);
282
+ onError?.(err instanceof Error ? err : new Error(String(err)), { kind: "stream" });
283
+ }
284
+ return this.formatOutput(accumulatedContent, accumulatedReasoning, displayMode);
285
+ }
286
+ /**
287
+ * 处理 DeepSeek 非流式响应
288
+ */
289
+ async handleDeepSeekNonStreamResponse(params) {
290
+ const { response, deliver, onReplyStart, onError, runtime, displayMode } = params;
291
+ let data;
292
+ try {
293
+ data = (await response.json());
294
+ }
295
+ catch (err) {
296
+ const error = new Error(`Failed to parse response JSON: ${String(err)}`);
297
+ onError?.(error, { kind: "parse" });
298
+ throw error;
299
+ }
300
+ const message = data.choices?.[0]?.message;
301
+ const content = message?.content || "";
302
+ const reasoningContent = message?.reasoning_content || "";
303
+ const formattedText = this.formatOutput(content, reasoningContent, displayMode);
304
+ if (formattedText) {
305
+ try {
306
+ await onReplyStart?.();
307
+ }
308
+ catch (e) {
309
+ this.logError(runtime, `onReplyStart error: ${String(e)}`);
310
+ }
311
+ try {
312
+ await deliver({ text: formattedText }, { kind: "block" });
313
+ }
314
+ catch (e) {
315
+ onError?.(e instanceof Error ? e : new Error(String(e)), { kind: "block" });
316
+ }
317
+ }
318
+ // 记录 token 使用情况(含推理 token)
319
+ if (data.usage?.completion_tokens_details?.reasoning_tokens) {
320
+ this.log(runtime, `Token usage: reasoning_tokens=${data.usage.completion_tokens_details.reasoning_tokens}, total=${data.usage.total_tokens}`);
321
+ }
322
+ return formattedText;
323
+ }
324
+ }
325
+
326
+ export { DeepSeekAdapter };