my-ai-chat-framework 1.0.2 → 1.1.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 +189 -2
- package/README_ZH.md +145 -1
- package/dist/my-ai-chat-framework.browser.es.js +367 -83
- package/dist/my-ai-chat-framework.browser.es.js.map +1 -1
- package/dist/my-ai-chat-framework.browser.umd.js +367 -83
- package/dist/my-ai-chat-framework.browser.umd.js.map +1 -1
- package/dist/my-ai-chat-framework.node.cjs.js +365 -81
- package/dist/my-ai-chat-framework.node.cjs.js.map +1 -1
- package/package.json +2 -2
- package/src/config/defaults.js +25 -0
- package/src/core/ApiClient.js +36 -8
- package/src/core/ConfigManager.js +173 -0
- package/src/core/MessageFormatter.js +20 -10
- package/src/core/Messages.js +37 -10
- package/src/core/RequestBuilder.js +4 -1
- package/src/index.js +539 -418
- package/dist/my-ai-chat-framework.cjs.js +0 -2630
- package/dist/my-ai-chat-framework.cjs.js.map +0 -1
- package/dist/my-ai-chat-framework.es.js +0 -2630
- package/dist/my-ai-chat-framework.es.js.map +0 -1
- package/dist/my-ai-chat-framework.umd.js +0 -2634
- package/dist/my-ai-chat-framework.umd.js.map +0 -1
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
class MessageFormatter {
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* 将消息转换为标准格式(不再过滤自定义元数据,只确保必需字段)
|
|
4
4
|
*/
|
|
5
5
|
static toStandardFormat(message) {
|
|
6
6
|
const { role, content } = message;
|
|
7
7
|
const standardMessage = { role, content };
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
standardMessage.tool_calls = message.tool_calls;
|
|
13
|
-
}
|
|
14
|
-
if (message.tool_call_id) {
|
|
15
|
-
standardMessage.tool_call_id = message.tool_call_id;
|
|
8
|
+
for (const [key, value] of Object.entries(message)) {
|
|
9
|
+
if (key !== "role" && key !== "content" && value !== void 0) {
|
|
10
|
+
standardMessage[key] = value;
|
|
11
|
+
}
|
|
16
12
|
}
|
|
17
13
|
return standardMessage;
|
|
18
14
|
}
|
|
@@ -95,6 +91,17 @@ class MessageFormatter {
|
|
|
95
91
|
}
|
|
96
92
|
};
|
|
97
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* 过滤消息以供API使用(只移除空content的消息,保留所有字段)
|
|
96
|
+
*/
|
|
97
|
+
static filterForAPI(messages) {
|
|
98
|
+
return messages.filter((msg) => {
|
|
99
|
+
if (msg.role === "tool") {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
return msg.content !== "" && msg.content !== void 0 && msg.content !== null;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
98
105
|
}
|
|
99
106
|
class Messages {
|
|
100
107
|
constructor() {
|
|
@@ -111,7 +118,7 @@ class Messages {
|
|
|
111
118
|
/**
|
|
112
119
|
* 添加消息(通用方法)
|
|
113
120
|
* @param {string} role - 角色:user/assistant/system/tool
|
|
114
|
-
* @param {string} content -
|
|
121
|
+
* @param {string} content - 消息内容(允许空字符串)
|
|
115
122
|
* @param {object} metadata - 额外元数据
|
|
116
123
|
* @returns {number} 新消息的索引
|
|
117
124
|
*/
|
|
@@ -125,12 +132,12 @@ class Messages {
|
|
|
125
132
|
throw new Error("tool消息必须包含tool_call_id");
|
|
126
133
|
}
|
|
127
134
|
} else {
|
|
128
|
-
if (typeof content !== "string") {
|
|
129
|
-
throw new Error("消息内容必须是字符串");
|
|
130
|
-
}
|
|
131
135
|
if (content === void 0 || content === null) {
|
|
132
136
|
throw new Error("消息内容不能为undefined或null");
|
|
133
137
|
}
|
|
138
|
+
if (typeof content !== "string") {
|
|
139
|
+
throw new Error("消息内容必须是字符串");
|
|
140
|
+
}
|
|
134
141
|
}
|
|
135
142
|
const message = {
|
|
136
143
|
role,
|
|
@@ -248,16 +255,16 @@ class Messages {
|
|
|
248
255
|
}
|
|
249
256
|
// ========== 消息修改 ==========
|
|
250
257
|
/**
|
|
251
|
-
*
|
|
258
|
+
* 更新消息内容(允许空字符串)
|
|
252
259
|
*/
|
|
253
260
|
updateMessage(index2, content) {
|
|
254
261
|
if (!Number.isInteger(index2) || index2 < 0 || index2 >= this.messages.length) {
|
|
255
262
|
throw new Error(`无效的消息索引: ${index2}`);
|
|
256
263
|
}
|
|
257
|
-
if (typeof content !== "string"
|
|
258
|
-
throw new Error("
|
|
264
|
+
if (typeof content !== "string") {
|
|
265
|
+
throw new Error("消息内容必须是字符串");
|
|
259
266
|
}
|
|
260
|
-
this.messages[index2].content = content
|
|
267
|
+
this.messages[index2].content = content;
|
|
261
268
|
this.messages[index2].lastModified = /* @__PURE__ */ new Date();
|
|
262
269
|
return this;
|
|
263
270
|
}
|
|
@@ -281,6 +288,30 @@ class Messages {
|
|
|
281
288
|
this.messages[index2].lastModified = /* @__PURE__ */ new Date();
|
|
282
289
|
return this;
|
|
283
290
|
}
|
|
291
|
+
/**
|
|
292
|
+
* 批量设置消息字段
|
|
293
|
+
*/
|
|
294
|
+
setMessageFields(index2, fields) {
|
|
295
|
+
if (!Number.isInteger(index2) || index2 < 0 || index2 >= this.messages.length) {
|
|
296
|
+
throw new Error(`无效的消息索引: ${index2}`);
|
|
297
|
+
}
|
|
298
|
+
for (const [field, value] of Object.entries(fields)) {
|
|
299
|
+
if (value !== void 0 && value !== null) {
|
|
300
|
+
this.messages[index2][field] = value;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
this.messages[index2].lastModified = /* @__PURE__ */ new Date();
|
|
304
|
+
return this;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* 批量更新最后一条消息的字段
|
|
308
|
+
*/
|
|
309
|
+
updateLastMessageFields(fields) {
|
|
310
|
+
if (this.messages.length === 0) {
|
|
311
|
+
throw new Error("没有消息可以修改");
|
|
312
|
+
}
|
|
313
|
+
return this.setMessageFields(this.messages.length - 1, fields);
|
|
314
|
+
}
|
|
284
315
|
// ========== 消息删除 ==========
|
|
285
316
|
/**
|
|
286
317
|
* 删除消息
|
|
@@ -791,9 +822,10 @@ class RequestBuilder {
|
|
|
791
822
|
baseRounds,
|
|
792
823
|
cycleRounds
|
|
793
824
|
);
|
|
825
|
+
const filteredMessages = MessageFormatter.filterForAPI(formattedMessages);
|
|
794
826
|
const request = {
|
|
795
827
|
model: this.model,
|
|
796
|
-
messages:
|
|
828
|
+
messages: filteredMessages,
|
|
797
829
|
...this.config
|
|
798
830
|
};
|
|
799
831
|
let tools = [];
|
|
@@ -2052,8 +2084,8 @@ function requireAxios$1() {
|
|
|
2052
2084
|
var Axios = requireAxios$2();
|
|
2053
2085
|
var mergeConfig2 = requireMergeConfig();
|
|
2054
2086
|
var defaults = requireDefaults();
|
|
2055
|
-
function createInstance(
|
|
2056
|
-
var context = new Axios(
|
|
2087
|
+
function createInstance(defaultConfig2) {
|
|
2088
|
+
var context = new Axios(defaultConfig2);
|
|
2057
2089
|
var instance = bind2(Axios.prototype.request, context);
|
|
2058
2090
|
utils2.extend(instance, Axios.prototype, context);
|
|
2059
2091
|
utils2.extend(instance, context);
|
|
@@ -2142,12 +2174,14 @@ class ApiClient extends EventEmitter {
|
|
|
2142
2174
|
this.cancelTokenSource = null;
|
|
2143
2175
|
}
|
|
2144
2176
|
}
|
|
2145
|
-
async strSend(body, onProgress
|
|
2146
|
-
}, onDone = () => {
|
|
2147
|
-
}) {
|
|
2177
|
+
async strSend(body, onProgress, onDone) {
|
|
2148
2178
|
this.isGenerating = true;
|
|
2149
2179
|
this.cancelTokenSource = axios.CancelToken.source();
|
|
2150
2180
|
try {
|
|
2181
|
+
const safeOnProgress = typeof onProgress === "function" ? onProgress : () => {
|
|
2182
|
+
};
|
|
2183
|
+
const safeOnDone = typeof onDone === "function" ? onDone : () => {
|
|
2184
|
+
};
|
|
2151
2185
|
this.emit("stream-start", { body });
|
|
2152
2186
|
const requestBody = this._buildRequestBody(body);
|
|
2153
2187
|
const response = await fetch(this.url, {
|
|
@@ -2167,10 +2201,20 @@ class ApiClient extends EventEmitter {
|
|
|
2167
2201
|
const decoder = new TextDecoder();
|
|
2168
2202
|
let buffer = "";
|
|
2169
2203
|
const message = {
|
|
2204
|
+
// 基础信息(符合Messages格式)
|
|
2205
|
+
role: "assistant",
|
|
2170
2206
|
content: "",
|
|
2171
2207
|
reasoning_content: "",
|
|
2172
|
-
|
|
2173
|
-
//
|
|
2208
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
2209
|
+
// 工具调用
|
|
2210
|
+
tool_calls: [],
|
|
2211
|
+
// 流式元数据
|
|
2212
|
+
streamMetadata: {
|
|
2213
|
+
chunkCount: 0,
|
|
2214
|
+
firstChunkTime: Date.now(),
|
|
2215
|
+
lastChunkTime: null,
|
|
2216
|
+
isStreaming: true
|
|
2217
|
+
}
|
|
2174
2218
|
};
|
|
2175
2219
|
while (true) {
|
|
2176
2220
|
const { done, value } = await reader.read();
|
|
@@ -2183,12 +2227,14 @@ class ApiClient extends EventEmitter {
|
|
|
2183
2227
|
if (!cleanChunk) continue;
|
|
2184
2228
|
if (cleanChunk === "[DONE]") {
|
|
2185
2229
|
this.emit("stream-done", { message });
|
|
2186
|
-
|
|
2230
|
+
safeOnDone({ ...message });
|
|
2187
2231
|
return;
|
|
2188
2232
|
}
|
|
2189
2233
|
try {
|
|
2190
2234
|
const obj = JSON.parse(cleanChunk);
|
|
2191
2235
|
console.log("🔍 收到流式chunk:", obj);
|
|
2236
|
+
message.streamMetadata.chunkCount++;
|
|
2237
|
+
message.streamMetadata.lastChunkTime = Date.now();
|
|
2192
2238
|
if (obj.choices?.[0]?.delta?.content) {
|
|
2193
2239
|
message.content += obj.choices[0].delta.content;
|
|
2194
2240
|
}
|
|
@@ -2217,8 +2263,12 @@ class ApiClient extends EventEmitter {
|
|
|
2217
2263
|
}
|
|
2218
2264
|
});
|
|
2219
2265
|
}
|
|
2220
|
-
this.emit("stream-chunk", {
|
|
2221
|
-
|
|
2266
|
+
this.emit("stream-chunk", {
|
|
2267
|
+
chunk: obj,
|
|
2268
|
+
message: { ...message }
|
|
2269
|
+
// 发送副本
|
|
2270
|
+
});
|
|
2271
|
+
safeOnProgress({ ...message });
|
|
2222
2272
|
} catch (e) {
|
|
2223
2273
|
console.error("🔍 解析chunk错误:", e, "原始数据:", cleanChunk);
|
|
2224
2274
|
}
|
|
@@ -2227,8 +2277,10 @@ class ApiClient extends EventEmitter {
|
|
|
2227
2277
|
break;
|
|
2228
2278
|
}
|
|
2229
2279
|
}
|
|
2230
|
-
|
|
2231
|
-
|
|
2280
|
+
message.streamMetadata.isStreaming = false;
|
|
2281
|
+
message.streamMetadata.completedAt = /* @__PURE__ */ new Date();
|
|
2282
|
+
this.emit("stream-done", { message: { ...message } });
|
|
2283
|
+
safeOnDone({ ...message });
|
|
2232
2284
|
} catch (error) {
|
|
2233
2285
|
if (error.name === "AbortError" || axios.isCancel(error)) {
|
|
2234
2286
|
const cancelError = new Error("请求被中断");
|
|
@@ -2312,15 +2364,227 @@ class ApiClient extends EventEmitter {
|
|
|
2312
2364
|
throw new Error("无法识别的聊天数据格式");
|
|
2313
2365
|
}
|
|
2314
2366
|
}
|
|
2367
|
+
class ConfigManager extends EventEmitter {
|
|
2368
|
+
constructor(defaultConfig2 = {}) {
|
|
2369
|
+
super();
|
|
2370
|
+
this._config = JSON.parse(JSON.stringify(defaultConfig2));
|
|
2371
|
+
this._handlers = /* @__PURE__ */ new Map();
|
|
2372
|
+
this.config = this._createProxy(this._config);
|
|
2373
|
+
}
|
|
2374
|
+
/**
|
|
2375
|
+
* 创建配置代理
|
|
2376
|
+
*/
|
|
2377
|
+
_createProxy(target, path = "") {
|
|
2378
|
+
return new Proxy(target, {
|
|
2379
|
+
set: (obj, prop, value) => {
|
|
2380
|
+
const oldValue = obj[prop];
|
|
2381
|
+
const fullPath = path ? `${path}.${prop}` : prop;
|
|
2382
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
2383
|
+
value = this._createProxy(value, fullPath);
|
|
2384
|
+
}
|
|
2385
|
+
obj[prop] = value;
|
|
2386
|
+
this._triggerChange(fullPath, oldValue, value);
|
|
2387
|
+
return true;
|
|
2388
|
+
},
|
|
2389
|
+
deleteProperty: (obj, prop) => {
|
|
2390
|
+
const oldValue = obj[prop];
|
|
2391
|
+
const fullPath = path ? `${path}.${prop}` : prop;
|
|
2392
|
+
delete obj[prop];
|
|
2393
|
+
this.emit("config-deleted", {
|
|
2394
|
+
path: fullPath,
|
|
2395
|
+
oldValue,
|
|
2396
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
2397
|
+
});
|
|
2398
|
+
return true;
|
|
2399
|
+
}
|
|
2400
|
+
});
|
|
2401
|
+
}
|
|
2402
|
+
/**
|
|
2403
|
+
* 注册配置处理器
|
|
2404
|
+
*/
|
|
2405
|
+
registerHandler(path, handler) {
|
|
2406
|
+
if (!this._handlers.has(path)) {
|
|
2407
|
+
this._handlers.set(path, []);
|
|
2408
|
+
}
|
|
2409
|
+
this._handlers.get(path).push(handler);
|
|
2410
|
+
return this;
|
|
2411
|
+
}
|
|
2412
|
+
/**
|
|
2413
|
+
* 移除配置处理器
|
|
2414
|
+
*/
|
|
2415
|
+
removeHandler(path, handler) {
|
|
2416
|
+
const handlers = this._handlers.get(path);
|
|
2417
|
+
if (handlers) {
|
|
2418
|
+
const index2 = handlers.indexOf(handler);
|
|
2419
|
+
if (index2 > -1) {
|
|
2420
|
+
handlers.splice(index2, 1);
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2423
|
+
return this;
|
|
2424
|
+
}
|
|
2425
|
+
/**
|
|
2426
|
+
* 触发配置变更
|
|
2427
|
+
*/
|
|
2428
|
+
_triggerChange(path, oldValue, newValue) {
|
|
2429
|
+
this.emit("config-changed", {
|
|
2430
|
+
path,
|
|
2431
|
+
oldValue,
|
|
2432
|
+
newValue,
|
|
2433
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
2434
|
+
});
|
|
2435
|
+
this.emit(`config-changed:${path}`, {
|
|
2436
|
+
oldValue,
|
|
2437
|
+
newValue,
|
|
2438
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
2439
|
+
});
|
|
2440
|
+
const handlers = this._handlers.get(path);
|
|
2441
|
+
if (handlers) {
|
|
2442
|
+
handlers.forEach((handler) => {
|
|
2443
|
+
try {
|
|
2444
|
+
handler(newValue, oldValue, path);
|
|
2445
|
+
} catch (error) {
|
|
2446
|
+
console.error(`配置处理器执行失败 (${path}):`, error);
|
|
2447
|
+
}
|
|
2448
|
+
});
|
|
2449
|
+
}
|
|
2450
|
+
const pathParts = path.split(".");
|
|
2451
|
+
for (let i = pathParts.length - 1; i >= 0; i--) {
|
|
2452
|
+
const parentPath = pathParts.slice(0, i).join(".");
|
|
2453
|
+
const wildcardPath = parentPath ? `${parentPath}.*` : "*";
|
|
2454
|
+
const parentHandlers = this._handlers.get(wildcardPath);
|
|
2455
|
+
if (parentHandlers) {
|
|
2456
|
+
parentHandlers.forEach((handler) => {
|
|
2457
|
+
try {
|
|
2458
|
+
handler(newValue, oldValue, path);
|
|
2459
|
+
} catch (error) {
|
|
2460
|
+
console.error(`通配符处理器执行失败 (${wildcardPath}):`, error);
|
|
2461
|
+
}
|
|
2462
|
+
});
|
|
2463
|
+
}
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
/**
|
|
2467
|
+
* 获取配置快照(非代理版本)
|
|
2468
|
+
*/
|
|
2469
|
+
getSnapshot() {
|
|
2470
|
+
return JSON.parse(JSON.stringify(this._config));
|
|
2471
|
+
}
|
|
2472
|
+
/**
|
|
2473
|
+
* 批量更新配置
|
|
2474
|
+
*/
|
|
2475
|
+
update(newConfig) {
|
|
2476
|
+
Object.keys(newConfig).forEach((key) => {
|
|
2477
|
+
this.config[key] = newConfig[key];
|
|
2478
|
+
});
|
|
2479
|
+
return this;
|
|
2480
|
+
}
|
|
2481
|
+
/**
|
|
2482
|
+
* 重置为默认值
|
|
2483
|
+
*/
|
|
2484
|
+
reset() {
|
|
2485
|
+
const oldConfig = this.getSnapshot();
|
|
2486
|
+
this._config = JSON.parse(JSON.stringify(this._defaultConfig));
|
|
2487
|
+
this.config = this._createProxy(this._config);
|
|
2488
|
+
this.emit("config-reset", {
|
|
2489
|
+
oldConfig,
|
|
2490
|
+
newConfig: this.getSnapshot(),
|
|
2491
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
2492
|
+
});
|
|
2493
|
+
return this;
|
|
2494
|
+
}
|
|
2495
|
+
}
|
|
2496
|
+
const defaultConfig = {
|
|
2497
|
+
api: {
|
|
2498
|
+
key: "",
|
|
2499
|
+
url: "https://api.deepseek.com/v1/chat/completions",
|
|
2500
|
+
timeout: 3e4
|
|
2501
|
+
},
|
|
2502
|
+
model: {
|
|
2503
|
+
name: "deepseek-chat",
|
|
2504
|
+
temperature: 0.7,
|
|
2505
|
+
// maxTokens: 2000,
|
|
2506
|
+
stream: false,
|
|
2507
|
+
reasoningEffort: "medium"
|
|
2508
|
+
},
|
|
2509
|
+
conversation: {
|
|
2510
|
+
baseRounds: 10,
|
|
2511
|
+
cycleRounds: 5
|
|
2512
|
+
},
|
|
2513
|
+
debug: false
|
|
2514
|
+
};
|
|
2315
2515
|
class ChatService extends EventEmitter {
|
|
2316
|
-
constructor(
|
|
2516
|
+
constructor(options = {}) {
|
|
2317
2517
|
super();
|
|
2518
|
+
this.autoAddMessages = options.autoAddMessages ?? true;
|
|
2519
|
+
this.configManager = new ConfigManager({
|
|
2520
|
+
...defaultConfig,
|
|
2521
|
+
...options
|
|
2522
|
+
});
|
|
2318
2523
|
this.messages = new Messages();
|
|
2319
|
-
this.apiClient = new ApiClient(
|
|
2320
|
-
|
|
2524
|
+
this.apiClient = new ApiClient(
|
|
2525
|
+
this.configManager.config.api.key,
|
|
2526
|
+
this.configManager.config.api.url
|
|
2527
|
+
);
|
|
2528
|
+
this.requestBuilder = new RequestBuilder(
|
|
2529
|
+
this.configManager.config.model.name,
|
|
2530
|
+
this.messages,
|
|
2531
|
+
{
|
|
2532
|
+
temperature: this.configManager.config.model.temperature,
|
|
2533
|
+
max_tokens: this.configManager.config.model.maxTokens,
|
|
2534
|
+
stream: this.configManager.config.model.stream,
|
|
2535
|
+
reasoning_effort: this.configManager.config.model.reasoningEffort
|
|
2536
|
+
}
|
|
2537
|
+
);
|
|
2321
2538
|
this.toolManager = new ToolManager();
|
|
2539
|
+
this._setupConfigHandlers();
|
|
2322
2540
|
this._setupDefaultListeners();
|
|
2323
2541
|
}
|
|
2542
|
+
/**
|
|
2543
|
+
* 设置配置变更处理器
|
|
2544
|
+
*/
|
|
2545
|
+
_setupConfigHandlers() {
|
|
2546
|
+
this.configManager.registerHandler("api.key", (newValue) => {
|
|
2547
|
+
this.apiClient.setApiKey(newValue);
|
|
2548
|
+
});
|
|
2549
|
+
this.configManager.registerHandler("api.url", (newValue) => {
|
|
2550
|
+
this.apiClient.setApiUrl(newValue);
|
|
2551
|
+
});
|
|
2552
|
+
this.configManager.registerHandler("model.name", (newValue) => {
|
|
2553
|
+
this.requestBuilder.model = newValue;
|
|
2554
|
+
});
|
|
2555
|
+
this.configManager.registerHandler("model.temperature", (newValue) => {
|
|
2556
|
+
this.requestBuilder.config.temperature = newValue;
|
|
2557
|
+
});
|
|
2558
|
+
this.configManager.registerHandler("model.stream", (newValue) => {
|
|
2559
|
+
this.requestBuilder.config.stream = newValue;
|
|
2560
|
+
});
|
|
2561
|
+
this.configManager.registerHandler("debug", (newValue) => {
|
|
2562
|
+
if (newValue) {
|
|
2563
|
+
console.log("🔧 调试模式已启用");
|
|
2564
|
+
}
|
|
2565
|
+
});
|
|
2566
|
+
this.configManager.on("config-changed", (data) => {
|
|
2567
|
+
this.emit("service-config-changed", data);
|
|
2568
|
+
});
|
|
2569
|
+
}
|
|
2570
|
+
/**
|
|
2571
|
+
* 便捷方法:快速设置 API
|
|
2572
|
+
*/
|
|
2573
|
+
setApi(key, url) {
|
|
2574
|
+
this.configManager.update({
|
|
2575
|
+
api: { key, url }
|
|
2576
|
+
});
|
|
2577
|
+
return this;
|
|
2578
|
+
}
|
|
2579
|
+
/**
|
|
2580
|
+
* 便捷方法:快速设置模型
|
|
2581
|
+
*/
|
|
2582
|
+
setModel(name, temperature = 0.7) {
|
|
2583
|
+
this.configManager.update({
|
|
2584
|
+
model: { name, temperature }
|
|
2585
|
+
});
|
|
2586
|
+
return this;
|
|
2587
|
+
}
|
|
2324
2588
|
_setupDefaultListeners() {
|
|
2325
2589
|
this.apiClient.on("request-start", (data) => {
|
|
2326
2590
|
this.emit("request-start", data);
|
|
@@ -2360,7 +2624,8 @@ class ChatService extends EventEmitter {
|
|
|
2360
2624
|
this.emit("sending", {
|
|
2361
2625
|
role: "user",
|
|
2362
2626
|
content: userMessage,
|
|
2363
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
2627
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
2628
|
+
eventType: "sending"
|
|
2364
2629
|
});
|
|
2365
2630
|
this.messages.addUserMessage(userMessage);
|
|
2366
2631
|
const requestBody = this.requestBuilder.toJSON(
|
|
@@ -2372,7 +2637,8 @@ class ChatService extends EventEmitter {
|
|
|
2372
2637
|
if (aiMessage.tool_calls && aiMessage.tool_calls.length > 0) {
|
|
2373
2638
|
return await this._handleToolCalls(aiMessage);
|
|
2374
2639
|
} else {
|
|
2375
|
-
|
|
2640
|
+
const eventData = await this._handleNormalResponse(aiMessage);
|
|
2641
|
+
return eventData.content;
|
|
2376
2642
|
}
|
|
2377
2643
|
} catch (error) {
|
|
2378
2644
|
this.emit("error", {
|
|
@@ -2387,12 +2653,10 @@ class ChatService extends EventEmitter {
|
|
|
2387
2653
|
* 处理工具调用(支持循环调用)
|
|
2388
2654
|
*/
|
|
2389
2655
|
async _handleToolCalls(aiMessage, maxIterations = 5) {
|
|
2390
|
-
console.log("🔍 处理工具调用,aiMessage:", aiMessage);
|
|
2391
2656
|
let iteration = 0;
|
|
2392
2657
|
let currentMessage = aiMessage;
|
|
2393
2658
|
while (iteration < maxIterations) {
|
|
2394
2659
|
iteration++;
|
|
2395
|
-
console.log(`🔄 工具调用迭代 ${iteration}/${maxIterations}`);
|
|
2396
2660
|
this.messages.addAssistantMessage(currentMessage.content || "", {
|
|
2397
2661
|
tool_calls: currentMessage.tool_calls,
|
|
2398
2662
|
reasoning_content: currentMessage.reasoning_content || ""
|
|
@@ -2406,54 +2670,61 @@ class ChatService extends EventEmitter {
|
|
|
2406
2670
|
iteration
|
|
2407
2671
|
});
|
|
2408
2672
|
const toolResults = await this.toolManager.executeToolCalls(currentMessage.tool_calls);
|
|
2409
|
-
console.log("🔍 工具执行结果:", toolResults);
|
|
2410
2673
|
for (const result of toolResults) {
|
|
2411
|
-
console.log("🔍 添加工具消息,result:", result);
|
|
2412
2674
|
this.messages.addToolMessage(
|
|
2413
2675
|
result.result || result.error || "",
|
|
2414
2676
|
result.tool_call_id
|
|
2415
2677
|
);
|
|
2416
2678
|
}
|
|
2417
|
-
console.log("🔍 当前所有消息:", this.messages.getMessages());
|
|
2418
2679
|
const requestBody = this.requestBuilder.toJSON();
|
|
2419
|
-
console.log("🔍 第", iteration, "次请求体:", requestBody);
|
|
2420
2680
|
const response = await this.apiClient.send(requestBody);
|
|
2421
2681
|
currentMessage = response.choices[0].message;
|
|
2422
2682
|
if (!currentMessage.tool_calls || currentMessage.tool_calls.length === 0) {
|
|
2423
|
-
console.log("✅ 工具调用完成,AI给出最终回复");
|
|
2424
2683
|
break;
|
|
2425
2684
|
}
|
|
2426
|
-
console.log("🔄 AI请求了新的工具调用,继续处理...");
|
|
2427
2685
|
}
|
|
2428
|
-
this.
|
|
2429
|
-
|
|
2430
|
-
// ✅ 新增:保存最终思考内容
|
|
2686
|
+
const eventData = await this._handleAssistantResponse(currentMessage, {
|
|
2687
|
+
toolIterations: iteration
|
|
2431
2688
|
});
|
|
2432
|
-
|
|
2689
|
+
return eventData.content;
|
|
2690
|
+
}
|
|
2691
|
+
/**
|
|
2692
|
+
* 处理助手回复(统一方法,用于普通和流式请求)
|
|
2693
|
+
*/
|
|
2694
|
+
async _handleAssistantResponse(aiMessage, options = {}) {
|
|
2695
|
+
const { isStreaming = false, toolIterations = 0, skipAddMessage = false } = options;
|
|
2696
|
+
const shouldAddMessage = this.autoAddMessages && !skipAddMessage;
|
|
2697
|
+
if (shouldAddMessage) {
|
|
2698
|
+
this.messages.addAssistantMessage(aiMessage.content || "", {
|
|
2699
|
+
reasoning_content: aiMessage.reasoning_content
|
|
2700
|
+
});
|
|
2701
|
+
}
|
|
2702
|
+
const eventData = {
|
|
2703
|
+
// 基础字段(Messages格式)
|
|
2433
2704
|
role: "assistant",
|
|
2434
|
-
content:
|
|
2435
|
-
reasoning_content: currentMessage.reasoning_content,
|
|
2436
|
-
// ✅ 新增:包含思考内容
|
|
2705
|
+
content: aiMessage.content || "",
|
|
2437
2706
|
timestamp: /* @__PURE__ */ new Date(),
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2707
|
+
// 扩展字段
|
|
2708
|
+
...aiMessage.reasoning_content && { reasoning_content: aiMessage.reasoning_content },
|
|
2709
|
+
...aiMessage.tool_calls && { tool_calls: aiMessage.tool_calls },
|
|
2710
|
+
...aiMessage.tool_call_id && { tool_call_id: aiMessage.tool_call_id },
|
|
2711
|
+
// 框架元数据
|
|
2712
|
+
isStreaming,
|
|
2713
|
+
toolIterations,
|
|
2714
|
+
eventType: isStreaming ? "stream-complete" : "response-complete",
|
|
2715
|
+
// 保留原始响应(只有普通请求有)
|
|
2716
|
+
...isStreaming ? {} : {
|
|
2717
|
+
response: { choices: [{ message: aiMessage }] }
|
|
2718
|
+
}
|
|
2719
|
+
};
|
|
2720
|
+
this.emit("message", eventData);
|
|
2721
|
+
return eventData;
|
|
2442
2722
|
}
|
|
2443
2723
|
/**
|
|
2444
2724
|
* 处理普通回复
|
|
2445
2725
|
*/
|
|
2446
2726
|
async _handleNormalResponse(aiMessage) {
|
|
2447
|
-
this.
|
|
2448
|
-
reasoning_content: aiMessage.reasoning_content
|
|
2449
|
-
});
|
|
2450
|
-
this.emit("message", {
|
|
2451
|
-
role: "assistant",
|
|
2452
|
-
content: aiMessage.content,
|
|
2453
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
2454
|
-
response: { choices: [{ message: aiMessage }] }
|
|
2455
|
-
});
|
|
2456
|
-
return aiMessage.content;
|
|
2727
|
+
return await this._handleAssistantResponse(aiMessage);
|
|
2457
2728
|
}
|
|
2458
2729
|
/**
|
|
2459
2730
|
* 流式发送消息(支持工具调用)
|
|
@@ -2463,7 +2734,8 @@ class ChatService extends EventEmitter {
|
|
|
2463
2734
|
this.emit("sending", {
|
|
2464
2735
|
role: "user",
|
|
2465
2736
|
content: userMessage,
|
|
2466
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
2737
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
2738
|
+
eventType: "sending"
|
|
2467
2739
|
});
|
|
2468
2740
|
this.messages.addUserMessage(userMessage);
|
|
2469
2741
|
const requestBuilder = new RequestBuilder(this.requestBuilder.model, this.messages, {
|
|
@@ -2477,19 +2749,31 @@ class ChatService extends EventEmitter {
|
|
|
2477
2749
|
);
|
|
2478
2750
|
const response = await this.apiClient.strSend(
|
|
2479
2751
|
requestBody,
|
|
2480
|
-
|
|
2752
|
+
// 如果 onProgress 是函数就调用,否则用空函数
|
|
2753
|
+
typeof onProgress === "function" ? (chunk) => {
|
|
2481
2754
|
this.emit("stream-progress", chunk);
|
|
2482
2755
|
onProgress(chunk);
|
|
2756
|
+
} : (chunk) => {
|
|
2757
|
+
this.emit("stream-progress", chunk);
|
|
2483
2758
|
},
|
|
2484
|
-
async (finalMessage) => {
|
|
2759
|
+
typeof onDone === "function" ? async (finalMessage) => {
|
|
2485
2760
|
if (finalMessage.tool_calls && finalMessage.tool_calls.length > 0) {
|
|
2486
2761
|
await this._handleStreamToolCalls(finalMessage, onProgress, onDone);
|
|
2487
2762
|
} else {
|
|
2488
|
-
this.
|
|
2489
|
-
|
|
2763
|
+
const eventData = await this._handleAssistantResponse(finalMessage, {
|
|
2764
|
+
isStreaming: true,
|
|
2765
|
+
skipAddMessage: true
|
|
2766
|
+
});
|
|
2767
|
+
onDone(eventData);
|
|
2768
|
+
}
|
|
2769
|
+
} : async (finalMessage) => {
|
|
2770
|
+
if (finalMessage.tool_calls && finalMessage.tool_calls.length > 0) {
|
|
2771
|
+
await this._handleStreamToolCalls(finalMessage);
|
|
2772
|
+
} else {
|
|
2773
|
+
await this._handleAssistantResponse(finalMessage, {
|
|
2774
|
+
isStreaming: true,
|
|
2775
|
+
skipAddMessage: true
|
|
2490
2776
|
});
|
|
2491
|
-
this.emit("stream-done", { message: finalMessage });
|
|
2492
|
-
onDone(finalMessage);
|
|
2493
2777
|
}
|
|
2494
2778
|
}
|
|
2495
2779
|
);
|
|
@@ -2531,9 +2815,11 @@ class ChatService extends EventEmitter {
|
|
|
2531
2815
|
await new Promise((resolve, reject) => {
|
|
2532
2816
|
this.apiClient.strSend(
|
|
2533
2817
|
requestBody,
|
|
2534
|
-
(chunk) => {
|
|
2818
|
+
typeof onProgress === "function" ? (chunk) => {
|
|
2535
2819
|
this.emit("stream-progress", chunk);
|
|
2536
2820
|
onProgress(chunk);
|
|
2821
|
+
} : (chunk) => {
|
|
2822
|
+
this.emit("stream-progress", chunk);
|
|
2537
2823
|
},
|
|
2538
2824
|
(finalMessage) => {
|
|
2539
2825
|
currentMessage = finalMessage;
|
|
@@ -2549,17 +2835,14 @@ class ChatService extends EventEmitter {
|
|
|
2549
2835
|
break;
|
|
2550
2836
|
}
|
|
2551
2837
|
}
|
|
2552
|
-
this.
|
|
2553
|
-
|
|
2554
|
-
|
|
2838
|
+
const eventData = await this._handleAssistantResponse(currentMessage, {
|
|
2839
|
+
isStreaming: true,
|
|
2840
|
+
toolIterations: iteration,
|
|
2841
|
+
skipAddMessage: true
|
|
2555
2842
|
});
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
// ✅ 新增:包含思考内容
|
|
2560
|
-
toolIterations: iteration
|
|
2561
|
-
});
|
|
2562
|
-
onDone(currentMessage);
|
|
2843
|
+
if (typeof onDone === "function") {
|
|
2844
|
+
onDone(eventData);
|
|
2845
|
+
}
|
|
2563
2846
|
}
|
|
2564
2847
|
/**
|
|
2565
2848
|
* 撤回消息
|
|
@@ -2615,7 +2898,8 @@ const index = {
|
|
|
2615
2898
|
EventEmitter,
|
|
2616
2899
|
MessageFormatter,
|
|
2617
2900
|
ToolManager,
|
|
2618
|
-
ChatService
|
|
2901
|
+
ChatService,
|
|
2902
|
+
defaultConfig
|
|
2619
2903
|
};
|
|
2620
2904
|
export {
|
|
2621
2905
|
ApiClient,
|