my-ai-chat-framework 1.1.0 → 2.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/src/index.js CHANGED
@@ -1,540 +1,15 @@
1
- // 这是 AI 聊天框架的入口文件
2
- // 导出所有核心组件供外部使用
3
-
4
- import Messages from './core/Messages.js';
5
- import RequestBuilder from './core/RequestBuilder.js';
6
- import ApiClient from './core/ApiClient.js';
7
- import EventEmitter from './core/EventEmitter.js';
8
- import MessageFormatter from './core/MessageFormatter.js';
9
- import ToolManager from './core/ToolManager.js';
10
- import ConfigManager from './core/ConfigManager.js';
11
- import defaultConfig from './config/defaults.js'; // 默认配置
12
-
13
- // 导出所有核心类
14
- export { Messages, RequestBuilder, ApiClient, EventEmitter, MessageFormatter, ToolManager };
15
-
16
- // ChatService类,继承EventEmitter
17
- class ChatService extends EventEmitter {
18
- constructor(options = {}) {
19
- super();
20
-
21
- // 配置:是否自动添加消息(默认启用)
22
- this.autoAddMessages = options.autoAddMessages ?? true;
23
-
24
- // 创建配置管理器
25
- this.configManager = new ConfigManager({
26
- ...defaultConfig,
27
- ...options
28
- });
29
-
30
- // 初始化组件
31
- this.messages = new Messages();
32
- this.apiClient = new ApiClient(
33
- this.configManager.config.api.key,
34
- this.configManager.config.api.url
35
- );
36
- this.requestBuilder = new RequestBuilder(
37
- this.configManager.config.model.name,
38
- this.messages,
39
- {
40
- temperature: this.configManager.config.model.temperature,
41
- max_tokens: this.configManager.config.model.maxTokens,
42
- stream: this.configManager.config.model.stream,
43
- reasoning_effort: this.configManager.config.model.reasoningEffort
44
- }
45
- );
46
- this.toolManager = new ToolManager();
47
-
48
- // 设置配置变更处理器
49
- this._setupConfigHandlers();
50
-
51
- this._setupDefaultListeners();
52
- }
53
-
54
- /**
55
- * 设置配置变更处理器
56
- */
57
- _setupConfigHandlers() {
58
- // API Key 变更
59
- this.configManager.registerHandler('api.key', (newValue) => {
60
- this.apiClient.setApiKey(newValue);
61
- });
62
-
63
- // API URL 变更
64
- this.configManager.registerHandler('api.url', (newValue) => {
65
- this.apiClient.setApiUrl(newValue);
66
- });
67
-
68
- // 模型名称变更
69
- this.configManager.registerHandler('model.name', (newValue) => {
70
- this.requestBuilder.model = newValue;
71
- });
72
-
73
- // 温度变更
74
- this.configManager.registerHandler('model.temperature', (newValue) => {
75
- this.requestBuilder.config.temperature = newValue;
76
- });
77
-
78
- // 流式模式变更
79
- this.configManager.registerHandler('model.stream', (newValue) => {
80
- this.requestBuilder.config.stream = newValue;
81
- });
82
-
83
- // 调试模式变更
84
- this.configManager.registerHandler('debug', (newValue) => {
85
- if (newValue) {
86
- console.log('🔧 调试模式已启用');
87
- }
88
- });
89
-
90
- // 监听所有配置变更
91
- this.configManager.on('config-changed', (data) => {
92
- this.emit('service-config-changed', data);
93
- });
94
- }
95
-
96
- /**
97
- * 便捷方法:快速设置 API
98
- */
99
- setApi(key, url) {
100
- this.configManager.update({
101
- api: { key, url }
102
- });
103
- return this;
104
- }
105
-
106
- /**
107
- * 便捷方法:快速设置模型
108
- */
109
- setModel(name, temperature = 0.7) {
110
- this.configManager.update({
111
- model: { name, temperature }
112
- });
113
- return this;
114
- }
115
-
116
- _setupDefaultListeners() {
117
- // 监听API客户端的事件
118
- this.apiClient.on('request-start', (data) => {
119
- this.emit('request-start', data);
120
- });
121
-
122
- this.apiClient.on('request-success', (data) => {
123
- this.emit('request-success', data);
124
- });
125
-
126
- this.apiClient.on('request-error', (data) => {
127
- this.emit('request-error', data);
128
- });
129
-
130
- this.apiClient.on('stream-chunk', (data) => {
131
- this.emit('stream-chunk', data);
132
- });
133
-
134
- // 监听工具管理器的事件
135
- this.toolManager.on('tool-execute-start', (data) => {
136
- this.emit('tool-execute-start', data);
137
- });
138
-
139
- this.toolManager.on('tool-execute-success', (data) => {
140
- this.emit('tool-execute-success', data);
141
- });
142
-
143
- this.toolManager.on('tool-execute-error', (data) => {
144
- this.emit('tool-execute-error', data);
145
- });
146
- }
147
-
148
- /**
149
- * 注册工具
150
- */
151
- registerTool(name, definition, executor) {
152
- // 1. 在ToolManager中注册
153
- this.toolManager.registerTool(name, definition, executor);
154
-
155
- // 2. 在Messages中添加工具定义(用于发送给AI)
156
- this.messages.addTool(definition);
157
-
158
- return this; // 支持链式调用
159
- }
160
-
161
- /**
162
- * 发送消息(支持工具调用)
163
- */
164
- async send(userMessage, options = {}) {
165
- try {
166
- // 触发事件:开始发送(符合Messages格式)
167
- this.emit('sending', {
168
- role: 'user',
169
- content: userMessage,
170
- timestamp: new Date(),
171
- eventType: 'sending'
172
- });
173
-
174
- // 添加用户消息
175
- this.messages.addUserMessage(userMessage);
176
-
177
- // 构建请求体(会自动包含工具定义)
178
- // 注意:这里需要确保RequestBuilder的配置正确
179
- const requestBody = this.requestBuilder.toJSON(
180
- options.baseRounds,
181
- options.cycleRounds
182
- );
183
-
184
- // 发送请求
185
- const response = await this.apiClient.send(requestBody);
186
- const aiMessage = response.choices[0].message;
187
-
188
- // 检查是否有工具调用
189
- if (aiMessage.tool_calls && aiMessage.tool_calls.length > 0) {
190
- // 处理工具调用
191
- return await this._handleToolCalls(aiMessage);
192
- } else {
193
- // 普通回复
194
- const eventData = await this._handleNormalResponse(aiMessage);
195
- return eventData.content;
196
- }
197
- } catch (error) {
198
- this.emit('error', {
199
- error: error,
200
- message: userMessage,
201
- timestamp: new Date()
202
- });
203
- throw error;
204
- }
205
- }
206
-
207
- /**
208
- * 处理工具调用(支持循环调用)
209
- */
210
- async _handleToolCalls(aiMessage, maxIterations = 5) {
211
- let iteration = 0;
212
- let currentMessage = aiMessage;
213
-
214
- // 循环处理工具调用,直到没有工具调用或达到最大迭代次数
215
- while (iteration < maxIterations) {
216
- iteration++;
217
-
218
- // 1. 添加助手消息(包含工具调用和思考内容)
219
- this.messages.addAssistantMessage(currentMessage.content || '', {
220
- tool_calls: currentMessage.tool_calls,
221
- reasoning_content: currentMessage.reasoning_content || '' // ✅ 新增:保存思考内容
222
- });
223
-
224
- // 触发事件:AI请求工具调用
225
- this.emit('tool-call-requested', {
226
- tool_calls: currentMessage.tool_calls,
227
- reasoning_content: currentMessage.reasoning_content, // ✅ 新增:包含思考内容
228
- timestamp: new Date(),
229
- iteration: iteration
230
- });
231
-
232
- // 2. 执行工具
233
- const toolResults = await this.toolManager.executeToolCalls(currentMessage.tool_calls);
234
-
235
- // 3. 添加工具结果消息
236
- for (const result of toolResults) {
237
- this.messages.addToolMessage(
238
- result.result || result.error || '',
239
- result.tool_call_id
240
- );
241
- }
242
-
243
- // 4. 再次发送请求,获取AI对工具结果的响应
244
- const requestBody = this.requestBuilder.toJSON();
245
-
246
- const response = await this.apiClient.send(requestBody);
247
- currentMessage = response.choices[0].message;
248
-
249
- // 5. 检查AI是否还有新的工具调用请求
250
- if (!currentMessage.tool_calls || currentMessage.tool_calls.length === 0) {
251
- // 没有更多工具调用,结束循环
252
- break;
253
- }
254
- }
255
-
256
- // 6. 添加最终助手消息 - 使用统一方法处理
257
- const eventData = await this._handleAssistantResponse(currentMessage, {
258
- toolIterations: iteration
259
- });
260
-
261
- return eventData.content;
262
- }
263
-
264
- /**
265
- * 处理助手回复(统一方法,用于普通和流式请求)
266
- */
267
- async _handleAssistantResponse(aiMessage, options = {}) {
268
- const { isStreaming = false, toolIterations = 0, skipAddMessage = false } = options;
269
-
270
- // 根据配置决定是否添加消息
271
- const shouldAddMessage = this.autoAddMessages && !skipAddMessage;
272
-
273
- if (shouldAddMessage) {
274
- // 添加助手消息,处理空内容
275
- this.messages.addAssistantMessage(aiMessage.content || '', {
276
- reasoning_content: aiMessage.reasoning_content
277
- });
278
- }
279
-
280
- // 统一的事件数据结构(直接使用Messages格式)
281
- const eventData = {
282
- // 基础字段(Messages格式)
283
- role: 'assistant',
284
- content: aiMessage.content || '',
285
- timestamp: new Date(),
286
-
287
- // 扩展字段
288
- ...(aiMessage.reasoning_content && { reasoning_content: aiMessage.reasoning_content }),
289
- ...(aiMessage.tool_calls && { tool_calls: aiMessage.tool_calls }),
290
- ...(aiMessage.tool_call_id && { tool_call_id: aiMessage.tool_call_id }),
291
-
292
- // 框架元数据
293
- isStreaming,
294
- toolIterations,
295
- eventType: isStreaming ? 'stream-complete' : 'response-complete',
296
-
297
- // 保留原始响应(只有普通请求有)
298
- ...(isStreaming ? {} : {
299
- response: { choices: [{ message: aiMessage }] }
300
- })
301
- };
302
-
303
- // 统一使用 'message' 事件
304
- this.emit('message', eventData);
305
-
306
- return eventData;
307
- }
308
-
309
- /**
310
- * 处理普通回复
311
- */
312
- async _handleNormalResponse(aiMessage) {
313
- return await this._handleAssistantResponse(aiMessage);
314
- }
315
-
316
- /**
317
- * 流式发送消息(支持工具调用)
318
- */
319
- async stream(userMessage, onProgress, onDone, options = {}) {
320
- try {
321
- // 触发事件:开始发送(符合Messages格式)
322
- this.emit('sending', {
323
- role: 'user',
324
- content: userMessage,
325
- timestamp: new Date(),
326
- eventType: 'sending'
327
- });
328
-
329
- // 添加用户消息
330
- this.messages.addUserMessage(userMessage);
331
-
332
- // 构建请求体 - 关键:设置stream: true
333
- const requestBuilder = new RequestBuilder(this.requestBuilder.model, this.messages, {
334
- ...this.requestBuilder.config,
335
- stream: true // ✅ 必须设置为true!
336
- });
337
-
338
- const requestBody = requestBuilder.toJSON(
339
- options.baseRounds,
340
- options.cycleRounds
341
- );
342
-
343
- // 流式发送 - 使用可选的回调函数
344
- const response = await this.apiClient.strSend(
345
- requestBody,
346
- // 如果 onProgress 是函数就调用,否则用空函数
347
- typeof onProgress === 'function'
348
- ? (chunk) => {
349
- this.emit('stream-progress', chunk);
350
- onProgress(chunk);
351
- }
352
- : (chunk) => {
353
- this.emit('stream-progress', chunk);
354
- },
355
- typeof onDone === 'function'
356
- ? async (finalMessage) => {
357
- // 流式完成回调
358
- if (finalMessage.tool_calls && finalMessage.tool_calls.length > 0) {
359
- // 处理流式中的工具调用
360
- await this._handleStreamToolCalls(finalMessage, onProgress, onDone);
361
- } else {
362
- // 普通流式回复 - 使用统一方法处理(跳过添加消息,因为用户可能已经手动添加了)
363
- const eventData = await this._handleAssistantResponse(finalMessage, {
364
- isStreaming: true,
365
- skipAddMessage: true
366
- });
367
- onDone(eventData);
368
- }
369
- }
370
- : async (finalMessage) => {
371
- // 只触发事件,不调用回调
372
- if (finalMessage.tool_calls && finalMessage.tool_calls.length > 0) {
373
- await this._handleStreamToolCalls(finalMessage);
374
- } else {
375
- await this._handleAssistantResponse(finalMessage, {
376
- isStreaming: true,
377
- skipAddMessage: true
378
- });
379
- }
380
- }
381
- );
382
-
383
- return response;
384
- } catch (error) {
385
- this.emit('error', {
386
- error: error,
387
- message: userMessage,
388
- timestamp: new Date()
389
- });
390
- throw error;
391
- }
392
- }
393
-
394
- /**
395
- * 处理流式传输中的工具调用(支持循环调用)
396
- */
397
- async _handleStreamToolCalls(aiMessage, onProgress, onDone, maxIterations = 5) {
398
- let iteration = 0;
399
- let currentMessage = aiMessage;
400
-
401
- // 循环处理工具调用
402
- while (iteration < maxIterations) {
403
- iteration++;
404
-
405
- // 1. 添加助手消息(包含工具调用和思考内容)
406
- this.messages.addAssistantMessage(currentMessage.content || '', {
407
- tool_calls: currentMessage.tool_calls,
408
- reasoning_content: currentMessage.reasoning_content || '' // ✅ 新增:保存思考内容
409
- });
410
-
411
- // 触发事件:AI请求工具调用
412
- this.emit('tool-call-requested', {
413
- tool_calls: currentMessage.tool_calls,
414
- reasoning_content: currentMessage.reasoning_content, // ✅ 新增:包含思考内容
415
- timestamp: new Date(),
416
- iteration: iteration
417
- });
418
-
419
- // 2. 执行工具
420
- const toolResults = await this.toolManager.executeToolCalls(currentMessage.tool_calls);
421
-
422
- // 3. 添加工具结果消息
423
- for (const result of toolResults) {
424
- this.messages.addToolMessage(result.result || result.error, result.tool_call_id);
425
- }
426
-
427
- // 4. 再次发送流式请求(包含工具结果)
428
- const requestBody = this.requestBuilder.toJSON();
429
-
430
- // 使用Promise包装流式请求,等待完成
431
- await new Promise((resolve, reject) => {
432
- this.apiClient.strSend(
433
- requestBody,
434
- typeof onProgress === 'function'
435
- ? (chunk) => {
436
- this.emit('stream-progress', chunk);
437
- onProgress(chunk);
438
- }
439
- : (chunk) => {
440
- this.emit('stream-progress', chunk);
441
- },
442
- (finalMessage) => {
443
- currentMessage = finalMessage;
444
-
445
- // 检查是否还有工具调用
446
- if (!currentMessage.tool_calls || currentMessage.tool_calls.length === 0) {
447
- // 没有更多工具调用,结束循环
448
- resolve();
449
- } else {
450
- // 还有工具调用,继续循环
451
- resolve();
452
- }
453
- }
454
- ).catch(reject);
455
- });
456
-
457
- // 如果没有工具调用了,跳出循环
458
- if (!currentMessage.tool_calls || currentMessage.tool_calls.length === 0) {
459
- break;
460
- }
461
- }
462
-
463
- // 添加最终助手消息 - 使用统一方法处理(跳过添加消息)
464
- const eventData = await this._handleAssistantResponse(currentMessage, {
465
- isStreaming: true,
466
- toolIterations: iteration,
467
- skipAddMessage: true
468
- });
469
-
470
- // 如果有 onDone 回调就调用
471
- if (typeof onDone === 'function') {
472
- onDone(eventData);
473
- }
474
- }
475
-
476
- /**
477
- * 撤回消息
478
- */
479
- undo() {
480
- const removed = this.messages.undoToAssistant();
481
- if (removed.length > 0) {
482
- this.emit('undo', {
483
- removedMessages: removed,
484
- timestamp: new Date()
485
- });
486
- }
487
- return removed;
488
- }
489
-
490
- /**
491
- * 清空消息
492
- */
493
- clear() {
494
- const removed = this.messages.clearMessages();
495
- this.emit('clear', {
496
- removedMessages: removed,
497
- timestamp: new Date()
498
- });
499
- return removed;
500
- }
501
-
502
- /**
503
- * 导出对话数据
504
- */
505
- export() {
506
- const data = this.messages.export();
507
- this.emit('export', {
508
- data: data,
509
- timestamp: new Date()
510
- });
511
- return data;
512
- }
513
-
514
- /**
515
- * 导入对话数据
516
- */
517
- import(data) {
518
- this.messages.import(data);
519
- this.emit('import', {
520
- data: data,
521
- timestamp: new Date()
522
- });
523
- return this;
524
- }
525
- }
526
-
527
- // 导出 ChatService
528
- export { ChatService };
529
-
530
- // 导出默认对象
531
- export default {
532
- Messages,
533
- RequestBuilder,
534
- ApiClient,
535
- EventEmitter,
536
- MessageFormatter,
537
- ToolManager,
538
- ChatService,
539
- defaultConfig
1
+ import { ChatService } from './core/ChatService.js';
2
+ import { MessageStore } from './core/MessageStore.js';
3
+ import { EventEmitter } from './core/EventEmitter.js';
4
+ import { openaiAdapter } from './adapters/openai.js';
5
+ import { toolCallingPlugin } from './plugins/tool-calling.js';
6
+
7
+ export { ChatService, MessageStore, EventEmitter, openaiAdapter, toolCallingPlugin };
8
+
9
+ export default {
10
+ ChatService,
11
+ MessageStore,
12
+ EventEmitter,
13
+ openaiAdapter,
14
+ toolCallingPlugin
540
15
  };
@@ -0,0 +1,119 @@
1
+ /**
2
+ * 工具调用插件
3
+ * 拦截消息中的 tool_calls,执行注册的工具,并将结果插入对话,然后继续请求
4
+ */
5
+ export const toolCallingPlugin = {
6
+ name: 'tool-calling',
7
+ maxIterations: 5, // 防止无限循环
8
+
9
+ install(chatService) {
10
+ this.chatService = chatService;
11
+ this._tools = new Map(); // 工具名 -> { executor, definition }
12
+
13
+ // 覆盖 registerTool 方法
14
+ chatService.registerTool = (name, description, executor) => {
15
+ this._tools.set(name, { executor, description });
16
+ // 可选:同时生成工具定义,以便在请求中告知 AI
17
+ // 这里简化,依赖适配器在请求中自动包含工具定义(如果需要)
18
+ return chatService;
19
+ };
20
+
21
+ // 拦截发送流程:在发送前注入工具定义(如果需要)
22
+ // 在流式和非流式响应后检测 tool_calls
23
+ const originalSend = chatService.send;
24
+ chatService.send = async (input) => {
25
+ return this._handleWithTools(originalSend, input, false);
26
+ };
27
+
28
+ const originalStream = chatService.stream;
29
+ chatService.stream = async (input, onProgress, onDone) => {
30
+ return this._handleWithTools(originalStream, input, true, onProgress, onDone);
31
+ };
32
+ },
33
+
34
+ async _handleWithTools(originalMethod, input, isStream, onProgress, onDone) {
35
+ const self = this;
36
+ const chat = this.chatService;
37
+ let iteration = 0;
38
+
39
+ // 包装回调,用于递归处理
40
+ async function processResponse(initialResponse) {
41
+ let lastResponse = initialResponse;
42
+ while (iteration < self.maxIterations) {
43
+ // 检查是否有工具调用
44
+ const toolCalls = lastResponse?.toolCalls;
45
+ if (!toolCalls || toolCalls.length === 0) break;
46
+
47
+ iteration++;
48
+ // 执行所有工具
49
+ const toolResults = [];
50
+ for (const call of toolCalls) {
51
+ const toolName = call.function?.name;
52
+ const args = JSON.parse(call.function?.arguments || '{}');
53
+ const tool = self._tools.get(toolName);
54
+ if (!tool) {
55
+ console.warn(`未找到工具: ${toolName}`);
56
+ toolResults.push({ tool_call_id: call.id, error: `工具 ${toolName} 未注册`, success: false });
57
+ continue;
58
+ }
59
+ try {
60
+ const result = await tool.executor(args);
61
+ toolResults.push({
62
+ tool_call_id: call.id,
63
+ content: typeof result === 'string' ? result : JSON.stringify(result),
64
+ success: true
65
+ });
66
+ } catch (err) {
67
+ console.error(`工具 ${toolName} 执行失败:`, err);
68
+ toolResults.push({ tool_call_id: call.id, error: err.message, success: false });
69
+ }
70
+ }
71
+
72
+ // 添加工具结果消息
73
+ for (const tr of toolResults) {
74
+ chat.messages.addTool(tr.content || tr.error, tr.tool_call_id);
75
+ }
76
+
77
+ // 再次请求
78
+ if (isStream) {
79
+ // 流式重新请求:需要重新构建请求体
80
+ const userMsg = chat.messages.getLast(); // 实际上应该重新生成请求
81
+ // 这里简化:直接调用原 stream 方法,但需要避免循环
82
+ // 实际实现需更细致,我们这里仅示意
83
+ await new Promise((resolve) => {
84
+ originalStream.call(chat, null, (chunk) => {
85
+ if (onProgress) onProgress(chunk);
86
+ lastResponse = chunk;
87
+ }, (final) => {
88
+ lastResponse = final;
89
+ resolve();
90
+ });
91
+ });
92
+ } else {
93
+ const reqBody = chat._adapter.buildRequest(chat.messages.getAll(), chat.config);
94
+ const apiResp = await chat._adapter.send(reqBody, chat.config);
95
+ lastResponse = chat._adapter.parseResponse(apiResp);
96
+ }
97
+ }
98
+ return lastResponse;
99
+ }
100
+
101
+ if (isStream) {
102
+ // 流式模式
103
+ let finalMessage;
104
+ await originalMethod.call(chat, input,
105
+ (chunk) => {
106
+ if (onProgress) onProgress(chunk);
107
+ },
108
+ async (final) => {
109
+ finalMessage = await processResponse(final);
110
+ if (onDone) onDone(finalMessage);
111
+ }
112
+ );
113
+ } else {
114
+ const resp = await originalMethod.call(chat, input);
115
+ const final = await processResponse(resp);
116
+ return final;
117
+ }
118
+ }
119
+ };