my-ai-chat-framework 2.0.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 +242 -45
- package/dist/my-ai-chat-framework.browser.es.js +1048 -184
- package/dist/my-ai-chat-framework.browser.es.js.map +1 -1
- package/dist/my-ai-chat-framework.browser.umd.js +1056 -188
- package/dist/my-ai-chat-framework.browser.umd.js.map +1 -1
- package/dist/my-ai-chat-framework.node.cjs.js +1056 -188
- package/dist/my-ai-chat-framework.node.cjs.js.map +1 -1
- package/package.json +7 -7
- package/src/adapters/openai.js +214 -64
- package/src/core/ChatService.js +298 -110
- package/src/core/Errors.js +60 -0
- package/src/core/EventEmitter.js +19 -0
- package/src/core/MessageStore.js +39 -0
- package/src/core/SystemPromptStore.js +118 -0
- package/src/index.js +10 -14
- package/src/plugins/model-registry.js +187 -0
- package/src/plugins/tool-calling.js +172 -78
- package/src/utils/MessageFormatter.js +204 -0
- package/src/utils/typeCheck.js +12 -0
- package/src/utils/url.js +18 -0
- package/.env +0 -15
- package/test.js +0 -107
package/src/index.js
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
export { ChatService } from './core/ChatService.js';
|
|
2
|
+
export { MessageStore } from './core/MessageStore.js';
|
|
3
|
+
export { SystemPromptStore } from './core/SystemPromptStore.js';
|
|
4
|
+
export { EventEmitter } from './core/EventEmitter.js';
|
|
5
|
+
export { openaiAdapter } from './adapters/openai.js';
|
|
6
|
+
export { toolCallingPlugin } from './plugins/tool-calling.js';
|
|
7
|
+
export { modelRegistryPlugin } from './plugins/model-registry.js';
|
|
8
|
+
export { MessageFormatter } from './utils/MessageFormatter.js';
|
|
9
|
+
export { assembleMessages } from './utils/MessageFormatter.js';
|
|
6
10
|
|
|
7
|
-
export {
|
|
8
|
-
|
|
9
|
-
export default {
|
|
10
|
-
ChatService,
|
|
11
|
-
MessageStore,
|
|
12
|
-
EventEmitter,
|
|
13
|
-
openaiAdapter,
|
|
14
|
-
toolCallingPlugin
|
|
15
|
-
};
|
|
11
|
+
export { APIError, NetworkError, ConfigurationError, ParsingError } from './core/Errors.js';
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* model-registry 插件
|
|
3
|
+
*
|
|
4
|
+
* 职责:
|
|
5
|
+
* 1. 内置常用模型的能力标签表(model → capabilities 映射)
|
|
6
|
+
* 2. 安装时自动根据 config.model 查表,写入 config.capabilities
|
|
7
|
+
* 3. 监听 config-updated 事件,切换模型时自动同步能力
|
|
8
|
+
* 4. 提供 chat.registerModel(),允许用户追加/覆盖自定义模型
|
|
9
|
+
*
|
|
10
|
+
* 能力标签字段(全部可选,缺失视为 false / 未知):
|
|
11
|
+
* {
|
|
12
|
+
* reasoning: boolean — 是否返回 reasoning_content(思维链)
|
|
13
|
+
* vision: boolean — 是否支持图片输入
|
|
14
|
+
* toolCalling: boolean — 是否支持 function calling
|
|
15
|
+
* streaming: boolean — 是否支持流式传输
|
|
16
|
+
* maxInputTokens: number — 最大输入 token 数
|
|
17
|
+
* maxOutputTokens: number — 最大输出 token 数
|
|
18
|
+
* }
|
|
19
|
+
*
|
|
20
|
+
* 使用:
|
|
21
|
+
* chat.use(modelRegistryPlugin);
|
|
22
|
+
* // config.capabilities 现在已自动填充
|
|
23
|
+
* chat.registerModel('my-custom-model', { toolCalling: true });
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
// ====== 内置模型能力表 ======
|
|
27
|
+
// 数据来源:各厂商官方文档(2026-05 快照),用户可通过 registerModel() 覆盖
|
|
28
|
+
const BUILTIN_MODELS = {
|
|
29
|
+
// ---- DeepSeek ----
|
|
30
|
+
'deepseek-chat': {
|
|
31
|
+
reasoning: false,
|
|
32
|
+
vision: false,
|
|
33
|
+
toolCalling: true,
|
|
34
|
+
streaming: true,
|
|
35
|
+
maxInputTokens: 128000,
|
|
36
|
+
maxOutputTokens: 8192
|
|
37
|
+
},
|
|
38
|
+
'deepseek-reasoner': {
|
|
39
|
+
reasoning: true,
|
|
40
|
+
vision: false,
|
|
41
|
+
toolCalling: true,
|
|
42
|
+
streaming: true,
|
|
43
|
+
maxInputTokens: 128000,
|
|
44
|
+
maxOutputTokens: 8192
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
// ---- OpenAI ----
|
|
48
|
+
'gpt-4o': {
|
|
49
|
+
reasoning: false,
|
|
50
|
+
vision: true,
|
|
51
|
+
toolCalling: true,
|
|
52
|
+
streaming: true,
|
|
53
|
+
maxInputTokens: 128000,
|
|
54
|
+
maxOutputTokens: 16384
|
|
55
|
+
},
|
|
56
|
+
'gpt-4o-mini': {
|
|
57
|
+
reasoning: false,
|
|
58
|
+
vision: true,
|
|
59
|
+
toolCalling: true,
|
|
60
|
+
streaming: true,
|
|
61
|
+
maxInputTokens: 128000,
|
|
62
|
+
maxOutputTokens: 16384
|
|
63
|
+
},
|
|
64
|
+
'gpt-3.5-turbo': {
|
|
65
|
+
reasoning: false,
|
|
66
|
+
vision: false,
|
|
67
|
+
toolCalling: true,
|
|
68
|
+
streaming: true,
|
|
69
|
+
maxInputTokens: 16385,
|
|
70
|
+
maxOutputTokens: 4096
|
|
71
|
+
},
|
|
72
|
+
'o1': {
|
|
73
|
+
reasoning: true,
|
|
74
|
+
vision: false,
|
|
75
|
+
toolCalling: false,
|
|
76
|
+
streaming: false,
|
|
77
|
+
maxInputTokens: 200000,
|
|
78
|
+
maxOutputTokens: 100000
|
|
79
|
+
},
|
|
80
|
+
'o3-mini': {
|
|
81
|
+
reasoning: true,
|
|
82
|
+
vision: false,
|
|
83
|
+
toolCalling: true,
|
|
84
|
+
streaming: true,
|
|
85
|
+
maxInputTokens: 200000,
|
|
86
|
+
maxOutputTokens: 100000
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
// ---- Anthropic ----
|
|
90
|
+
'claude-3.5-sonnet': {
|
|
91
|
+
reasoning: false,
|
|
92
|
+
vision: true,
|
|
93
|
+
toolCalling: true,
|
|
94
|
+
streaming: true,
|
|
95
|
+
maxInputTokens: 200000,
|
|
96
|
+
maxOutputTokens: 8192
|
|
97
|
+
},
|
|
98
|
+
'claude-3.5-haiku': {
|
|
99
|
+
reasoning: false,
|
|
100
|
+
vision: true,
|
|
101
|
+
toolCalling: true,
|
|
102
|
+
streaming: true,
|
|
103
|
+
maxInputTokens: 200000,
|
|
104
|
+
maxOutputTokens: 8192
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const DEFAULT_CAPABILITIES = {
|
|
109
|
+
streaming: true,
|
|
110
|
+
toolCalling: false,
|
|
111
|
+
reasoning: false,
|
|
112
|
+
vision: false
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export const modelRegistryPlugin = {
|
|
116
|
+
name: 'model-registry',
|
|
117
|
+
|
|
118
|
+
install(chatService) {
|
|
119
|
+
this.chat = chatService;
|
|
120
|
+
// 合并内置表 + 用户通过 registerModel 追加的
|
|
121
|
+
this._registry = new Map(Object.entries(BUILTIN_MODELS));
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* 注册/覆盖一个模型的能力标签
|
|
125
|
+
* @param {string} name — 模型名称
|
|
126
|
+
* @param {Object} capabilities — 能力标签对象(部分字段即可,未提供的取默认值)
|
|
127
|
+
* @returns {ChatService}
|
|
128
|
+
*/
|
|
129
|
+
chatService.registerModel = (name, capabilities = {}) => {
|
|
130
|
+
if (!name || typeof name !== 'string' || !name.trim()) {
|
|
131
|
+
throw new Error('[model-registry] 模型名称必须是非空字符串');
|
|
132
|
+
}
|
|
133
|
+
const merged = { ...DEFAULT_CAPABILITIES, ...capabilities };
|
|
134
|
+
this._registry.set(name.trim(), merged);
|
|
135
|
+
// 如果注册的模型与当前使用的模型同名,立即同步
|
|
136
|
+
if (chatService.config.model === name.trim()) {
|
|
137
|
+
this._syncCapabilities();
|
|
138
|
+
}
|
|
139
|
+
return chatService;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* 列出所有已注册的模型名称
|
|
144
|
+
* @returns {Array<string>}
|
|
145
|
+
*/
|
|
146
|
+
chatService.listModels = () => {
|
|
147
|
+
return [...this._registry.keys()];
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// 安装时立即同步一次
|
|
151
|
+
this._syncCapabilities();
|
|
152
|
+
|
|
153
|
+
// 监听配置变更:model 变了就重新同步
|
|
154
|
+
chatService.on('config-updated', ({ changes }) => {
|
|
155
|
+
if ('model' in changes) {
|
|
156
|
+
this._syncCapabilities();
|
|
157
|
+
}
|
|
158
|
+
// 如果用户手动改了 capabilities,用 merge 保留未指定的字段
|
|
159
|
+
if ('capabilities' in changes && !('model' in changes)) {
|
|
160
|
+
const current = this._lookupCapabilities();
|
|
161
|
+
if (current) {
|
|
162
|
+
chatService.config.capabilities = { ...current, ...changes.capabilities };
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
// ---- 内部方法 ----
|
|
169
|
+
|
|
170
|
+
/** 查表获取模型能力,未注册返回 null */
|
|
171
|
+
_lookupCapabilities() {
|
|
172
|
+
const model = this.chat.config.model;
|
|
173
|
+
if (!model) return null;
|
|
174
|
+
return this._registry.get(model) || null;
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
/** 将查到的能力写入 config.capabilities */
|
|
178
|
+
_syncCapabilities() {
|
|
179
|
+
const caps = this._lookupCapabilities();
|
|
180
|
+
if (caps) {
|
|
181
|
+
// 若用户已手动设了 capabilities,用模型的合并(用户值优先?还是模型优先?)
|
|
182
|
+
// 策略:模型优先,因为换了模型能力应该重置
|
|
183
|
+
this.chat.config.capabilities = { ...caps };
|
|
184
|
+
}
|
|
185
|
+
// 未注册的模型:不清除已有 capabilities,保留用户手动设的值
|
|
186
|
+
}
|
|
187
|
+
};
|
|
@@ -1,119 +1,213 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 工具调用插件
|
|
3
|
-
*
|
|
3
|
+
* 功能:拦截助手消息中的 tool_calls,执行对应的工具,将结果作为 tool 消息加入对话,
|
|
4
|
+
* 然后自动继续对话(通过 sendExisting / sendExistingStream),直到没有新的工具调用。
|
|
5
|
+
*
|
|
6
|
+
* 设计要点:
|
|
7
|
+
* - 支持普通请求和流式请求(通过 isStream 标志区分)
|
|
8
|
+
* - 支持多次工具调用循环(maxIterations 防止无限循环)
|
|
9
|
+
* - 串行执行工具(可后续升级为并行)
|
|
10
|
+
* - 工具执行失败时,仍然返回错误信息给 AI,而不是中断整个流程
|
|
11
|
+
* - 触发 tool-error 事件,方便用户监听工具执行异常
|
|
4
12
|
*/
|
|
5
13
|
export const toolCallingPlugin = {
|
|
6
14
|
name: 'tool-calling',
|
|
7
|
-
maxIterations: 5,
|
|
15
|
+
maxIterations: 5, // 最大工具调用循环次数,防止无限循环(例如 AI 反复请求同一工具)
|
|
8
16
|
|
|
17
|
+
/**
|
|
18
|
+
* 安装插件,覆盖 ChatService 的 registerTool、send、stream 方法
|
|
19
|
+
* @param {ChatService} chatService - ChatService 实例
|
|
20
|
+
*/
|
|
9
21
|
install(chatService) {
|
|
10
22
|
this.chatService = chatService;
|
|
11
|
-
this._tools = new Map();
|
|
23
|
+
this._tools = new Map(); // 存储工具名 -> { executor, description }
|
|
12
24
|
|
|
13
|
-
//
|
|
14
|
-
chatService.
|
|
25
|
+
// 初始化 config.tools 数组(用于存放 OpenAI 格式的工具定义)
|
|
26
|
+
if (!chatService.config.tools) {
|
|
27
|
+
chatService.config.tools = [];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 注册工具
|
|
32
|
+
* @param {string} name - 工具名称(唯一标识)
|
|
33
|
+
* @param {string} description - 工具描述(告诉 AI 何时调用)
|
|
34
|
+
* @param {Function} executor - 异步执行函数,接收参数对象,返回结果(字符串或对象)
|
|
35
|
+
* @param {Object} parameters - JSON Schema 参数定义(可选,默认为空对象)
|
|
36
|
+
* @returns {ChatService} 返回 chatService 实例,支持链式调用
|
|
37
|
+
*/
|
|
38
|
+
chatService.registerTool = (name, description, executor, parameters = {}) => {
|
|
39
|
+
// 保存执行器
|
|
15
40
|
this._tools.set(name, { executor, description });
|
|
16
|
-
// 可选:同时生成工具定义,以便在请求中告知 AI
|
|
17
|
-
// 这里简化,依赖适配器在请求中自动包含工具定义(如果需要)
|
|
18
|
-
return chatService;
|
|
19
|
-
};
|
|
20
41
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
42
|
+
// 构建 OpenAI 兼容的工具定义
|
|
43
|
+
const toolDefinition = {
|
|
44
|
+
type: 'function',
|
|
45
|
+
function: {
|
|
46
|
+
name,
|
|
47
|
+
description,
|
|
48
|
+
parameters: {
|
|
49
|
+
type: 'object',
|
|
50
|
+
properties: parameters,
|
|
51
|
+
required: Object.keys(parameters).filter(key => parameters[key]?.required)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
// 避免重复添加相同工具
|
|
56
|
+
const existing = chatService.config.tools.find(t => t.function.name === name);
|
|
57
|
+
if (!existing) {
|
|
58
|
+
chatService.config.tools.push(toolDefinition);
|
|
59
|
+
}
|
|
60
|
+
return chatService;
|
|
26
61
|
};
|
|
27
62
|
|
|
28
|
-
|
|
29
|
-
chatService.
|
|
30
|
-
return this._handleWithTools(
|
|
63
|
+
// 注册响应后处理钩子,不再覆盖 send/stream
|
|
64
|
+
chatService._processResponse = async (response, { isStream, onProgress, onDone }) => {
|
|
65
|
+
return this._handleWithTools(response, isStream, onProgress, onDone);
|
|
31
66
|
};
|
|
32
67
|
},
|
|
33
68
|
|
|
34
|
-
|
|
69
|
+
/**
|
|
70
|
+
* 核心处理逻辑:检查响应 → 执行工具 → 继续请求 → 循环
|
|
71
|
+
* @param {Object} initialResponse - 初始 AI 响应
|
|
72
|
+
* @param {boolean} isStream - 是否流式
|
|
73
|
+
* @param {Function} onProgress - 流式进度回调
|
|
74
|
+
* @param {Function} onDone - 流式完成回调
|
|
75
|
+
* @returns {Promise<Object>} 最终 AI 响应
|
|
76
|
+
*/
|
|
77
|
+
async _handleWithTools(initialResponse, isStream, onProgress, onDone) {
|
|
35
78
|
const self = this;
|
|
36
79
|
const chat = this.chatService;
|
|
37
80
|
let iteration = 0;
|
|
38
81
|
|
|
39
|
-
|
|
82
|
+
/**
|
|
83
|
+
* 递归处理工具调用
|
|
84
|
+
* @param {Object} initialResponse - 初始 AI 响应(可能是第一次请求的响应)
|
|
85
|
+
* @returns {Promise<Object>} 最终 AI 响应(不含 tool_calls)
|
|
86
|
+
*/
|
|
40
87
|
async function processResponse(initialResponse) {
|
|
41
88
|
let lastResponse = initialResponse;
|
|
42
89
|
while (iteration < self.maxIterations) {
|
|
43
|
-
//
|
|
90
|
+
// 检查 AI 返回的消息中是否包含工具调用请求
|
|
44
91
|
const toolCalls = lastResponse?.toolCalls;
|
|
45
92
|
if (!toolCalls || toolCalls.length === 0) break;
|
|
46
93
|
|
|
47
94
|
iteration++;
|
|
48
|
-
//
|
|
49
|
-
const toolResults =
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
95
|
+
// 并行执行所有工具调用(彼此独立,不会因一个失败而阻塞其他)
|
|
96
|
+
const toolResults = await Promise.all(
|
|
97
|
+
toolCalls.map(async (call) => {
|
|
98
|
+
const toolName = call.function?.name;
|
|
99
|
+
|
|
100
|
+
// 解析参数(JSON.parse 可能失败,需单独 try/catch)
|
|
101
|
+
let args;
|
|
102
|
+
try {
|
|
103
|
+
args = JSON.parse(call.function?.arguments || '{}');
|
|
104
|
+
} catch (parseErr) {
|
|
105
|
+
chat.emit('tool-error', {
|
|
106
|
+
toolName: toolName || 'unknown',
|
|
107
|
+
error: parseErr,
|
|
108
|
+
toolCallId: call.id,
|
|
109
|
+
stage: 'parse',
|
|
110
|
+
timestamp: Date.now()
|
|
111
|
+
});
|
|
112
|
+
return {
|
|
113
|
+
tool_call_id: call.id,
|
|
114
|
+
error: `参数解析失败: ${parseErr.message}`,
|
|
115
|
+
success: false
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const tool = self._tools.get(toolName);
|
|
120
|
+
if (!tool) {
|
|
121
|
+
chat.emit('tool-error', {
|
|
122
|
+
toolName,
|
|
123
|
+
error: new Error(`Tool not registered: ${toolName}`),
|
|
124
|
+
toolCallId: call.id,
|
|
125
|
+
stage: 'lookup',
|
|
126
|
+
timestamp: Date.now()
|
|
127
|
+
});
|
|
128
|
+
return {
|
|
129
|
+
tool_call_id: call.id,
|
|
130
|
+
error: `工具 ${toolName} 未注册`,
|
|
131
|
+
success: false
|
|
132
|
+
};
|
|
133
|
+
}
|
|
71
134
|
|
|
72
|
-
|
|
135
|
+
try {
|
|
136
|
+
// 工具执行超时控制
|
|
137
|
+
const toolTimeout = chat.config.toolTimeout;
|
|
138
|
+
let execPromise = tool.executor(args);
|
|
139
|
+
if (toolTimeout && typeof toolTimeout === 'number' && toolTimeout > 0) {
|
|
140
|
+
const timeoutErr = new Error(`工具 ${toolName} 执行超时 (${toolTimeout}ms)`);
|
|
141
|
+
timeoutErr.name = 'ToolTimeoutError';
|
|
142
|
+
const timeoutPromise = new Promise((_, reject) =>
|
|
143
|
+
setTimeout(() => reject(timeoutErr), toolTimeout)
|
|
144
|
+
);
|
|
145
|
+
execPromise = Promise.race([execPromise, timeoutPromise]);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const result = await execPromise;
|
|
149
|
+
chat.emit('tool-success', {
|
|
150
|
+
toolName,
|
|
151
|
+
result,
|
|
152
|
+
toolCallId: call.id,
|
|
153
|
+
timestamp: Date.now()
|
|
154
|
+
});
|
|
155
|
+
return {
|
|
156
|
+
tool_call_id: call.id,
|
|
157
|
+
content: typeof result === 'string' ? result : JSON.stringify(result),
|
|
158
|
+
success: true
|
|
159
|
+
};
|
|
160
|
+
} catch (err) {
|
|
161
|
+
const stage = err.name === 'ToolTimeoutError' ? 'timeout' : 'execute';
|
|
162
|
+
chat.emit('tool-error', {
|
|
163
|
+
toolName,
|
|
164
|
+
error: err,
|
|
165
|
+
toolCallId: call.id,
|
|
166
|
+
stage,
|
|
167
|
+
timestamp: Date.now()
|
|
168
|
+
});
|
|
169
|
+
return {
|
|
170
|
+
tool_call_id: call.id,
|
|
171
|
+
error: err.message,
|
|
172
|
+
success: false
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
// 将工具执行结果作为 tool 消息添加到对话历史
|
|
73
179
|
for (const tr of toolResults) {
|
|
74
|
-
|
|
180
|
+
// 注意:addTool 的第二个参数是 tool_call_id
|
|
181
|
+
// 如果工具执行失败且没有内容,则传递错误信息作为 content
|
|
182
|
+
const content = tr.success ? tr.content : (tr.error || '工具执行失败');
|
|
183
|
+
chat.messages.addTool(content, tr.tool_call_id);
|
|
75
184
|
}
|
|
76
185
|
|
|
77
|
-
// 再次请求
|
|
186
|
+
// 再次请求 AI,获取基于工具结果的回复
|
|
78
187
|
if (isStream) {
|
|
79
|
-
//
|
|
80
|
-
const userMsg = chat.messages.getLast(); // 实际上应该重新生成请求
|
|
81
|
-
// 这里简化:直接调用原 stream 方法,但需要避免循环
|
|
82
|
-
// 实际实现需更细致,我们这里仅示意
|
|
188
|
+
// 流式继续:使用 sendExistingStream
|
|
83
189
|
await new Promise((resolve) => {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
190
|
+
chat.sendExistingStream(
|
|
191
|
+
(chunk) => {
|
|
192
|
+
if (onProgress) onProgress(chunk);
|
|
193
|
+
lastResponse = chunk;
|
|
194
|
+
},
|
|
195
|
+
(final) => {
|
|
196
|
+
lastResponse = final;
|
|
197
|
+
resolve();
|
|
198
|
+
}
|
|
199
|
+
);
|
|
91
200
|
});
|
|
92
201
|
} else {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
lastResponse = chat._adapter.parseResponse(apiResp);
|
|
202
|
+
// 非流式继续:使用 sendExisting
|
|
203
|
+
lastResponse = await chat.sendExisting();
|
|
96
204
|
}
|
|
97
205
|
}
|
|
98
206
|
return lastResponse;
|
|
99
207
|
}
|
|
100
208
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
}
|
|
209
|
+
// 初始响应已由 ChatService 发出,直接进入工具调用循环
|
|
210
|
+
const final = await processResponse(initialResponse);
|
|
211
|
+
return final;
|
|
118
212
|
}
|
|
119
|
-
};
|
|
213
|
+
};
|