my-ai-chat-framework 1.1.0 → 2.7.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/README.md +218 -406
- package/dist/my-ai-chat-framework.browser.es.js +1284 -2904
- package/dist/my-ai-chat-framework.browser.es.js.map +1 -1
- package/dist/my-ai-chat-framework.browser.umd.js +1310 -2917
- package/dist/my-ai-chat-framework.browser.umd.js.map +1 -1
- package/dist/my-ai-chat-framework.node.cjs.js +1296 -1654
- package/dist/my-ai-chat-framework.node.cjs.js.map +1 -1
- package/package.json +18 -35
- package/src/adapters/openai.js +280 -0
- package/src/core/ChatService.js +298 -0
- package/src/core/Errors.js +60 -0
- package/src/core/EventEmitter.js +51 -126
- package/src/core/MessageStore.js +112 -0
- package/src/core/SystemPromptStore.js +118 -0
- package/src/index.js +11 -540
- package/src/plugins/model-registry.js +187 -0
- package/src/plugins/tool-calling.js +213 -0
- package/src/utils/MessageFormatter.js +204 -0
- package/src/utils/typeCheck.js +12 -0
- package/src/utils/url.js +18 -0
- package/README_ZH.md +0 -545
- package/src/config/defaults.js +0 -25
- package/src/core/ApiClient.js +0 -316
- package/src/core/ConfigManager.js +0 -173
- package/src/core/MessageFormatter.js +0 -136
- package/src/core/Messages.js +0 -589
- package/src/core/RequestBuilder.js +0 -99
- package/src/core/ToolManager.js +0 -175
- package/src/utils/index.js +0 -21
package/src/core/EventEmitter.js
CHANGED
|
@@ -1,126 +1,51 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*/
|
|
5
|
-
class EventEmitter {
|
|
6
|
-
constructor() {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (!
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
// 如果某个回调出错,不影响其他回调
|
|
53
|
-
console.error(`事件 ${eventName} 的回调函数执行出错:`, error)
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* 取消监听事件
|
|
60
|
-
* @param {string} eventName --要取消的事件名称
|
|
61
|
-
* @param {Function} callback --要移除的回调函数
|
|
62
|
-
*/
|
|
63
|
-
off(eventName, callback) {
|
|
64
|
-
const callbacks = this.events[eventName]
|
|
65
|
-
|
|
66
|
-
//判断一下防止报错
|
|
67
|
-
if (!callbacks) return
|
|
68
|
-
|
|
69
|
-
//过滤掉要移除的回调函数
|
|
70
|
-
this.events[eventName] = callbacks.filter(item => item !== callback)
|
|
71
|
-
|
|
72
|
-
// 如果这个事件没有监听器了,删除这个事件
|
|
73
|
-
if (this.events[eventName].length === 0) {
|
|
74
|
-
delete this.events[eventName]
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* 只监听一次事件(触发后自动取消)
|
|
80
|
-
* @param {string} eventName - 事件名称
|
|
81
|
-
* @param {Function} callback - 回调函数
|
|
82
|
-
*/
|
|
83
|
-
once(evenName, callback) {
|
|
84
|
-
// 创建一个包装函数,执行完成后自动取消
|
|
85
|
-
const onceCallback = (data) => {
|
|
86
|
-
//先取消监听
|
|
87
|
-
this.off(evenName, onceCallback)
|
|
88
|
-
//在执行原回调
|
|
89
|
-
callback(data)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
//监听包装后的函数
|
|
93
|
-
this.on(evenName, onceCallback)
|
|
94
|
-
|
|
95
|
-
//返回取消函数
|
|
96
|
-
return () => this.off(evenName, onceCallback)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* 移除某个事件的所有监听器
|
|
101
|
-
* @param {string} eventName - 事件名称
|
|
102
|
-
*/
|
|
103
|
-
removeAllListeners(eventName) {
|
|
104
|
-
if (eventName) {
|
|
105
|
-
// 移除指定事件的所有监听器
|
|
106
|
-
delete this.events[eventName]
|
|
107
|
-
} else {
|
|
108
|
-
// 如果没有指定事件名,移除所有事件的监听器
|
|
109
|
-
this.events = {}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* 获取某个事件的所有监听器数量
|
|
115
|
-
* @param {string} eventName - 事件名称
|
|
116
|
-
* @returns {number} 监听器数量
|
|
117
|
-
*/
|
|
118
|
-
listenerCount(eventName) {
|
|
119
|
-
const callbacks = this.events[eventName]
|
|
120
|
-
return callbacks ? callbacks.length : 0
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
//导出EventEmiter类
|
|
126
|
-
export default EventEmitter
|
|
1
|
+
/**
|
|
2
|
+
* 极简事件发射器
|
|
3
|
+
* 支持同步/异步事件,handler 返回 Promise 时自动 await
|
|
4
|
+
*/
|
|
5
|
+
export class EventEmitter {
|
|
6
|
+
constructor() {
|
|
7
|
+
this._events = new Map();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
on(event, handler) {
|
|
11
|
+
if (!this._events.has(event)) this._events.set(event, []);
|
|
12
|
+
this._events.get(event).push(handler);
|
|
13
|
+
return () => this.off(event, handler);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
off(event, handler) {
|
|
17
|
+
if (!this._events.has(event)) return;
|
|
18
|
+
const handlers = this._events.get(event).filter(h => h !== handler);
|
|
19
|
+
if (handlers.length) this._events.set(event, handlers);
|
|
20
|
+
else this._events.delete(event);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 同步触发事件
|
|
25
|
+
*/
|
|
26
|
+
emit(event, data) {
|
|
27
|
+
if (!this._events.has(event)) return;
|
|
28
|
+
for (const handler of this._events.get(event)) {
|
|
29
|
+
try {
|
|
30
|
+
handler(data);
|
|
31
|
+
} catch (err) {
|
|
32
|
+
console.error(`事件 ${event} 处理出错:`, err);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 异步触发事件,逐个 await handler
|
|
39
|
+
* handler 返回 Promise 时自动等待
|
|
40
|
+
*/
|
|
41
|
+
async emitAsync(event, data) {
|
|
42
|
+
if (!this._events.has(event)) return;
|
|
43
|
+
for (const handler of this._events.get(event)) {
|
|
44
|
+
try {
|
|
45
|
+
await handler(data);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
console.error(`事件 ${event} 处理出错:`, err);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 消息存储 - 极简实现
|
|
3
|
+
* 每条消息格式:
|
|
4
|
+
* {
|
|
5
|
+
* id?: string,
|
|
6
|
+
* role: 'user'|'assistant'|'system'|'tool',
|
|
7
|
+
* content: string,
|
|
8
|
+
* images?: Array<string>, // 图片引用(URL / id),框架不存储图片数据
|
|
9
|
+
* _ephemeral?: boolean, // 临时消息:仅底部连续时参与请求,续写后转正
|
|
10
|
+
* _complete?: boolean, // 流式是否完成(false=未完成/中断)
|
|
11
|
+
* prefix?: boolean, // 前缀续写标记
|
|
12
|
+
* toolCalls?: Array,
|
|
13
|
+
* toolCallId?: string,
|
|
14
|
+
* timestamp?: number,
|
|
15
|
+
* metadata?: any
|
|
16
|
+
* }
|
|
17
|
+
*/
|
|
18
|
+
export class MessageStore {
|
|
19
|
+
constructor() {
|
|
20
|
+
this._messages = [];
|
|
21
|
+
this._idCounter = 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
add(message) {
|
|
25
|
+
const msg = {
|
|
26
|
+
id: message.id || `msg_${Date.now()}_${++this._idCounter}`,
|
|
27
|
+
timestamp: message.timestamp || Date.now(),
|
|
28
|
+
...message
|
|
29
|
+
};
|
|
30
|
+
this._messages.push(msg);
|
|
31
|
+
return msg;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
addUser(content, metadata = {}) {
|
|
35
|
+
return this.add({ role: 'user', content, metadata });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
addAssistant(content, metadata = {}) {
|
|
39
|
+
return this.add({ role: 'assistant', content, metadata });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
addSystem(content, metadata = {}) {
|
|
43
|
+
return this.add({ role: 'system', content, metadata });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
addTool(content, toolCallId, metadata = {}) {
|
|
47
|
+
return this.add({ role: 'tool', content, toolCallId, metadata });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 快速添加一次性的、带续写标记的 assistant 消息
|
|
52
|
+
* 适用于思维链引导、临时注入等场景
|
|
53
|
+
* @param {string} content — 引导内容
|
|
54
|
+
* @param {Object} [options] — { reasoningContent, prefix }
|
|
55
|
+
* @returns {Object} 添加的消息
|
|
56
|
+
*/
|
|
57
|
+
addOnceAssistant(content, options = {}) {
|
|
58
|
+
return this.add({
|
|
59
|
+
role: 'assistant',
|
|
60
|
+
content,
|
|
61
|
+
reasoningContent: options.reasoningContent,
|
|
62
|
+
prefix: options.prefix !== false, // 默认开启续写
|
|
63
|
+
_ephemeral: true
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
getLast() {
|
|
68
|
+
return this._messages[this._messages.length - 1] || null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
getAll() {
|
|
72
|
+
return [...this._messages];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
clear() {
|
|
76
|
+
this._messages = [];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// 撤回最后一条助手消息及其后的所有消息(用于工具调用循环)
|
|
80
|
+
undoToLastAssistant() {
|
|
81
|
+
let lastAssistantIndex = -1;
|
|
82
|
+
for (let i = this._messages.length - 1; i >= 0; i--) {
|
|
83
|
+
if (this._messages[i].role === 'assistant') {
|
|
84
|
+
lastAssistantIndex = i;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (lastAssistantIndex >= 0) {
|
|
89
|
+
const removed = this._messages.splice(lastAssistantIndex + 1);
|
|
90
|
+
return removed;
|
|
91
|
+
}
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* 更新消息:按 id 查找并合并 changes,不新增消息
|
|
97
|
+
* @param {string} id — 消息 id
|
|
98
|
+
* @param {Object} changes — 要合并的字段
|
|
99
|
+
* @returns {Object|null} 更新后的消息,未找到返回 null
|
|
100
|
+
*/
|
|
101
|
+
update(id, changes) {
|
|
102
|
+
for (const msg of this._messages) {
|
|
103
|
+
if (msg.id === id) {
|
|
104
|
+
Object.assign(msg, changes);
|
|
105
|
+
// 更新 timestamp 便于排查
|
|
106
|
+
msg.timestamp = changes.timestamp || Date.now();
|
|
107
|
+
return msg;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SystemPromptStore — 系统提示词存储
|
|
3
|
+
*
|
|
4
|
+
* 职责:管理多条 system prompt 的增删改查与开关
|
|
5
|
+
* 与 MessageStore 分离,因为 system prompt 是"AI 行为准则",不是对话事件
|
|
6
|
+
*
|
|
7
|
+
* 每条记录格式:
|
|
8
|
+
* {
|
|
9
|
+
* id: string,
|
|
10
|
+
* content: string,
|
|
11
|
+
* enabled: boolean,
|
|
12
|
+
* timestamp: number
|
|
13
|
+
* }
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export class SystemPromptStore {
|
|
17
|
+
constructor() {
|
|
18
|
+
this._prompts = [];
|
|
19
|
+
this._idCounter = 0;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 添加一条 system prompt
|
|
24
|
+
* @param {string} content — 提示词内容
|
|
25
|
+
* @param {boolean} [enabled=true] — 是否启用
|
|
26
|
+
* @returns {Object} 添加的记录
|
|
27
|
+
*/
|
|
28
|
+
add(content, enabled = true) {
|
|
29
|
+
if (!content || typeof content !== 'string' || !content.trim()) {
|
|
30
|
+
throw new Error('[SystemPromptStore] content 必须是非空字符串');
|
|
31
|
+
}
|
|
32
|
+
const record = {
|
|
33
|
+
id: `sys_${Date.now()}_${++this._idCounter}`,
|
|
34
|
+
content: content.trim(),
|
|
35
|
+
enabled: Boolean(enabled),
|
|
36
|
+
timestamp: Date.now()
|
|
37
|
+
};
|
|
38
|
+
this._prompts.push(record);
|
|
39
|
+
return record;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 删除指定索引的 system prompt
|
|
44
|
+
* @param {number} index
|
|
45
|
+
* @returns {Object} 被删除的记录
|
|
46
|
+
*/
|
|
47
|
+
remove(index) {
|
|
48
|
+
if (index < 0 || index >= this._prompts.length) {
|
|
49
|
+
throw new Error(`[SystemPromptStore] 索引越界: ${index}`);
|
|
50
|
+
}
|
|
51
|
+
return this._prompts.splice(index, 1)[0];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 切换指定索引的启用/禁用状态
|
|
56
|
+
* @param {number} index
|
|
57
|
+
* @returns {boolean} 切换后的状态
|
|
58
|
+
*/
|
|
59
|
+
toggle(index) {
|
|
60
|
+
if (index < 0 || index >= this._prompts.length) {
|
|
61
|
+
throw new Error(`[SystemPromptStore] 索引越界: ${index}`);
|
|
62
|
+
}
|
|
63
|
+
this._prompts[index].enabled = !this._prompts[index].enabled;
|
|
64
|
+
return this._prompts[index].enabled;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 更新指定索引的 content
|
|
69
|
+
* @param {number} index
|
|
70
|
+
* @param {string} content
|
|
71
|
+
*/
|
|
72
|
+
update(index, content) {
|
|
73
|
+
if (index < 0 || index >= this._prompts.length) {
|
|
74
|
+
throw new Error(`[SystemPromptStore] 索引越界: ${index}`);
|
|
75
|
+
}
|
|
76
|
+
if (!content || typeof content !== 'string' || !content.trim()) {
|
|
77
|
+
throw new Error('[SystemPromptStore] content 必须是非空字符串');
|
|
78
|
+
}
|
|
79
|
+
this._prompts[index].content = content.trim();
|
|
80
|
+
this._prompts[index].timestamp = Date.now();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 清空并用一条 content 替换(便捷方法,常用于 config.system = '...')
|
|
85
|
+
* @param {string} content
|
|
86
|
+
*/
|
|
87
|
+
set(content) {
|
|
88
|
+
this._prompts = [];
|
|
89
|
+
if (content && typeof content === 'string' && content.trim()) {
|
|
90
|
+
this.add(content, true);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 按 enabled 筛选后,转为适配器可用的格式
|
|
96
|
+
* @returns {Array<{role:'system', content:string}>}
|
|
97
|
+
*/
|
|
98
|
+
getEnabled() {
|
|
99
|
+
return this._prompts
|
|
100
|
+
.filter(p => p.enabled)
|
|
101
|
+
.map(p => ({ role: 'system', content: p.content }));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* 返回所有记录(含 enabled 状态,用于 UI 展示)
|
|
106
|
+
* @returns {Array}
|
|
107
|
+
*/
|
|
108
|
+
getAll() {
|
|
109
|
+
return [...this._prompts];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* 清空所有 system prompt
|
|
114
|
+
*/
|
|
115
|
+
clear() {
|
|
116
|
+
this._prompts = [];
|
|
117
|
+
}
|
|
118
|
+
}
|