@blueking/chat-helper 0.0.12-beta.17 → 0.0.12-beta.18
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/dist/agent/use-agent.ts.js +82 -17
- package/dist/http/fetch/fetch.ts.js +22 -6
- package/package.json +1 -1
|
@@ -79,6 +79,30 @@ function _object_spread(target) {
|
|
|
79
79
|
}
|
|
80
80
|
return target;
|
|
81
81
|
}
|
|
82
|
+
function ownKeys(object, enumerableOnly) {
|
|
83
|
+
var keys = Object.keys(object);
|
|
84
|
+
if (Object.getOwnPropertySymbols) {
|
|
85
|
+
var symbols = Object.getOwnPropertySymbols(object);
|
|
86
|
+
if (enumerableOnly) {
|
|
87
|
+
symbols = symbols.filter(function(sym) {
|
|
88
|
+
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
keys.push.apply(keys, symbols);
|
|
92
|
+
}
|
|
93
|
+
return keys;
|
|
94
|
+
}
|
|
95
|
+
function _object_spread_props(target, source) {
|
|
96
|
+
source = source != null ? source : {};
|
|
97
|
+
if (Object.getOwnPropertyDescriptors) {
|
|
98
|
+
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
|
|
99
|
+
} else {
|
|
100
|
+
ownKeys(Object(source)).forEach(function(key) {
|
|
101
|
+
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
return target;
|
|
105
|
+
}
|
|
82
106
|
import { ref } from 'vue';
|
|
83
107
|
import { AGUIProtocol, ApprovalInterruptTicketStatus, EventType, ResumeStatus, RunFinishedOutcomeType } from '../event/index.ts.js';
|
|
84
108
|
import { MessageRole, MessageStatus, UserOperation } from '../message/index.ts.js';
|
|
@@ -129,6 +153,7 @@ const getReconnectDelayMs = (attempt)=>Math.min(RECONNECT_BASE_DELAY_MS * Math.p
|
|
|
129
153
|
/** 用户主动中止(abortChat),禁止自动重连 */ let manualAbort = false;
|
|
130
154
|
/** 当前轮次已静默重连次数 */ let reconnectAttempt = 0;
|
|
131
155
|
/** 本轮流是否已收到 RUN_FINISHED */ let runFinished = false;
|
|
156
|
+
/** 流世代号:新开流时递增,用于忽略已被替换的旧 SSE 回调 */ let activeStreamId = 0;
|
|
132
157
|
let reconnectTimer = null;
|
|
133
158
|
let reconnectWaitResolve = null;
|
|
134
159
|
let activeStreamContext = null;
|
|
@@ -245,11 +270,13 @@ const getReconnectDelayMs = (attempt)=>Math.min(RECONNECT_BASE_DELAY_MS * Math.p
|
|
|
245
270
|
};
|
|
246
271
|
/**
|
|
247
272
|
* 尝试静默重连。
|
|
273
|
+
* @param streamId - 发起重连的流世代;任一 await 后若已被新流/abort 取代则立即退出,避免 abort 掉更新流
|
|
248
274
|
* @returns reconnected | finished(服务端已结束)| failed(需对外报错或中止)
|
|
249
275
|
*/ const attemptSilentReconnect = function() {
|
|
250
|
-
var _ref = _async_to_generator(function*(sessionCode) {
|
|
276
|
+
var _ref = _async_to_generator(function*(sessionCode, streamId) {
|
|
251
277
|
var _mediator_session_current_value, _mediator_session, _mediator_session_current_value1, _mediator_session1, _mediator_message_list_value_at, _mediator_message;
|
|
252
|
-
|
|
278
|
+
const isOwnerStream = ()=>streamId === activeStreamId && !manualAbort;
|
|
279
|
+
if (!isOwnerStream() || !isSameActiveSession(sessionCode)) {
|
|
253
280
|
return 'failed';
|
|
254
281
|
}
|
|
255
282
|
try {
|
|
@@ -258,7 +285,7 @@ const getReconnectDelayMs = (attempt)=>Math.min(RECONNECT_BASE_DELAY_MS * Math.p
|
|
|
258
285
|
} catch (e) {
|
|
259
286
|
// 状态刷新失败时沿用本地 session.status
|
|
260
287
|
}
|
|
261
|
-
if (
|
|
288
|
+
if (!isOwnerStream() || !isSameActiveSession(sessionCode)) {
|
|
262
289
|
return 'failed';
|
|
263
290
|
}
|
|
264
291
|
if (((_mediator_session = mediator.session) === null || _mediator_session === void 0 ? void 0 : (_mediator_session_current_value = _mediator_session.current.value) === null || _mediator_session_current_value === void 0 ? void 0 : _mediator_session_current_value.status) !== SessionStatus.Running) {
|
|
@@ -272,12 +299,16 @@ const getReconnectDelayMs = (attempt)=>Math.min(RECONNECT_BASE_DELAY_MS * Math.p
|
|
|
272
299
|
isChatting.value = true;
|
|
273
300
|
const delayMs = getReconnectDelayMs(reconnectAttempt);
|
|
274
301
|
const waited = yield waitForReconnectDelay(delayMs);
|
|
275
|
-
if (!waited ||
|
|
302
|
+
if (!waited || !isOwnerStream() || !isSameActiveSession(sessionCode)) {
|
|
276
303
|
return 'failed';
|
|
277
304
|
}
|
|
278
305
|
if (((_mediator_session1 = mediator.session) === null || _mediator_session1 === void 0 ? void 0 : (_mediator_session_current_value1 = _mediator_session1.current.value) === null || _mediator_session_current_value1 === void 0 ? void 0 : _mediator_session_current_value1.status) !== SessionStatus.Running) {
|
|
279
306
|
return 'finished';
|
|
280
307
|
}
|
|
308
|
+
// 发起新流前再校验一次:防止 await 后窗口期被新用户请求抢走 activeStreamId
|
|
309
|
+
if (!isOwnerStream()) {
|
|
310
|
+
return 'failed';
|
|
311
|
+
}
|
|
281
312
|
pruneTrailingIncompleteMessages();
|
|
282
313
|
const lastMessageId = (_mediator_message = mediator.message) === null || _mediator_message === void 0 ? void 0 : (_mediator_message_list_value_at = _mediator_message.list.value.at(-1)) === null || _mediator_message_list_value_at === void 0 ? void 0 : _mediator_message_list_value_at.id;
|
|
283
314
|
const ctx = activeStreamContext;
|
|
@@ -291,13 +322,13 @@ const getReconnectDelayMs = (attempt)=>Math.min(RECONNECT_BASE_DELAY_MS * Math.p
|
|
|
291
322
|
});
|
|
292
323
|
return 'reconnected';
|
|
293
324
|
});
|
|
294
|
-
return function attemptSilentReconnect(sessionCode) {
|
|
325
|
+
return function attemptSilentReconnect(sessionCode, streamId) {
|
|
295
326
|
return _ref.apply(this, arguments);
|
|
296
327
|
};
|
|
297
328
|
}();
|
|
298
329
|
const streamRequest = function() {
|
|
299
330
|
var _ref = _async_to_generator(function*({ sessionCode, model, url, config, resume, input, lastMessageId, isReconnect = false }) {
|
|
300
|
-
var _mediator_http;
|
|
331
|
+
var _previousController_abort, _mediator_http;
|
|
301
332
|
if (!isReconnect) {
|
|
302
333
|
resetStreamReconnectState();
|
|
303
334
|
}
|
|
@@ -307,6 +338,12 @@ const getReconnectDelayMs = (attempt)=>Math.min(RECONNECT_BASE_DELAY_MS * Math.p
|
|
|
307
338
|
url,
|
|
308
339
|
config
|
|
309
340
|
};
|
|
341
|
+
// 先递增世代再 abort 旧连接,避免旧流 onDone 误触发二次重连
|
|
342
|
+
const streamId = ++activeStreamId;
|
|
343
|
+
const previousController = chatAbortController;
|
|
344
|
+
chatAbortController = new AbortController();
|
|
345
|
+
previousController === null || previousController === void 0 ? void 0 : (_previousController_abort = previousController.abort) === null || _previousController_abort === void 0 ? void 0 : _previousController_abort.call(previousController);
|
|
346
|
+
const isActiveStream = ()=>streamId === activeStreamId;
|
|
310
347
|
// ag-ui 协议需要注入消息模块
|
|
311
348
|
if (usedProtocol instanceof AGUIProtocol) {
|
|
312
349
|
usedProtocol.injectMessageModule(mediator.message);
|
|
@@ -323,6 +360,9 @@ const getReconnectDelayMs = (attempt)=>Math.min(RECONNECT_BASE_DELAY_MS * Math.p
|
|
|
323
360
|
}
|
|
324
361
|
const finishSuccessfully = ()=>{
|
|
325
362
|
var _usedProtocol_onDone;
|
|
363
|
+
if (!isActiveStream()) {
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
326
366
|
isChatting.value = false;
|
|
327
367
|
(_usedProtocol_onDone = usedProtocol.onDone) === null || _usedProtocol_onDone === void 0 ? void 0 : _usedProtocol_onDone.call(usedProtocol);
|
|
328
368
|
if (input) {
|
|
@@ -342,18 +382,30 @@ const getReconnectDelayMs = (attempt)=>Math.min(RECONNECT_BASE_DELAY_MS * Math.p
|
|
|
342
382
|
};
|
|
343
383
|
const failWithError = (error)=>{
|
|
344
384
|
var _usedProtocol_onError;
|
|
385
|
+
if (!isActiveStream()) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
345
388
|
isChatting.value = false;
|
|
346
389
|
(_usedProtocol_onError = usedProtocol.onError) === null || _usedProtocol_onError === void 0 ? void 0 : _usedProtocol_onError.call(usedProtocol, error);
|
|
347
390
|
};
|
|
348
391
|
// 事件代理
|
|
349
392
|
const onDone = ()=>{
|
|
350
|
-
|
|
351
|
-
|
|
393
|
+
if (!isActiveStream()) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
// 用户 abort:本地状态已在 abortChat 结算,勿走 completion/poll
|
|
397
|
+
if (manualAbort) {
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
if (runFinished) {
|
|
352
401
|
finishSuccessfully();
|
|
353
402
|
return;
|
|
354
403
|
}
|
|
355
404
|
// 连接被干净掐断且未收到 RUN_FINISHED:尝试静默重连
|
|
356
|
-
void attemptSilentReconnect(sessionCode).then((result)=>{
|
|
405
|
+
void attemptSilentReconnect(sessionCode, streamId).then((result)=>{
|
|
406
|
+
if (!isActiveStream()) {
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
357
409
|
if (result === 'reconnected') {
|
|
358
410
|
// 保持 isChatting=true,不展示错误
|
|
359
411
|
return;
|
|
@@ -371,14 +423,17 @@ const getReconnectDelayMs = (attempt)=>Math.min(RECONNECT_BASE_DELAY_MS * Math.p
|
|
|
371
423
|
});
|
|
372
424
|
};
|
|
373
425
|
const onError = (error)=>{
|
|
374
|
-
if (manualAbort) {
|
|
426
|
+
if (!isActiveStream() || manualAbort) {
|
|
375
427
|
return;
|
|
376
428
|
}
|
|
377
429
|
if (!isRecoverableStreamError(error)) {
|
|
378
430
|
failWithError(error);
|
|
379
431
|
return;
|
|
380
432
|
}
|
|
381
|
-
void attemptSilentReconnect(sessionCode).then((result)=>{
|
|
433
|
+
void attemptSilentReconnect(sessionCode, streamId).then((result)=>{
|
|
434
|
+
if (!isActiveStream()) {
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
382
437
|
if (result === 'reconnected') {
|
|
383
438
|
return;
|
|
384
439
|
}
|
|
@@ -395,6 +450,9 @@ const getReconnectDelayMs = (attempt)=>Math.min(RECONNECT_BASE_DELAY_MS * Math.p
|
|
|
395
450
|
};
|
|
396
451
|
const onMessage = (event)=>{
|
|
397
452
|
var _usedProtocol_onMessage;
|
|
453
|
+
if (!isActiveStream()) {
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
398
456
|
const typedEvent = event;
|
|
399
457
|
if ((typedEvent === null || typedEvent === void 0 ? void 0 : typedEvent.type) === EventType.RunFinished) {
|
|
400
458
|
runFinished = true;
|
|
@@ -403,13 +461,14 @@ const getReconnectDelayMs = (attempt)=>Math.min(RECONNECT_BASE_DELAY_MS * Math.p
|
|
|
403
461
|
};
|
|
404
462
|
const onStart = ()=>{
|
|
405
463
|
var _usedProtocol_onStart;
|
|
464
|
+
if (!isActiveStream()) {
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
406
467
|
isChatting.value = true;
|
|
407
468
|
(_usedProtocol_onStart = usedProtocol.onStart) === null || _usedProtocol_onStart === void 0 ? void 0 : _usedProtocol_onStart.call(usedProtocol);
|
|
408
469
|
};
|
|
409
|
-
//
|
|
410
|
-
|
|
411
|
-
// 发起聊天
|
|
412
|
-
void ((_mediator_http = mediator.http) === null || _mediator_http === void 0 ? void 0 : _mediator_http.fetchClient.streamRequest(_object_spread({
|
|
470
|
+
// 发起聊天(controller / 回调必须在 ...config 之后,防止被覆盖)
|
|
471
|
+
void ((_mediator_http = mediator.http) === null || _mediator_http === void 0 ? void 0 : _mediator_http.fetchClient.streamRequest(_object_spread_props(_object_spread({
|
|
413
472
|
url: url || 'chat_completion/',
|
|
414
473
|
method: 'POST',
|
|
415
474
|
data: {
|
|
@@ -422,13 +481,14 @@ const getReconnectDelayMs = (attempt)=>Math.min(RECONNECT_BASE_DELAY_MS * Math.p
|
|
|
422
481
|
last_message_id: lastMessageId,
|
|
423
482
|
resume
|
|
424
483
|
}
|
|
425
|
-
}
|
|
484
|
+
}
|
|
485
|
+
}, config), {
|
|
426
486
|
controller: chatAbortController,
|
|
427
487
|
onDone,
|
|
428
488
|
onError,
|
|
429
489
|
onMessage,
|
|
430
490
|
onStart
|
|
431
|
-
}
|
|
491
|
+
})).catch(()=>{
|
|
432
492
|
// 非 abort 错误已通过 onError 回调处理;abort 为正常结束
|
|
433
493
|
}));
|
|
434
494
|
});
|
|
@@ -546,10 +606,15 @@ const getReconnectDelayMs = (attempt)=>Math.min(RECONNECT_BASE_DELAY_MS * Math.p
|
|
|
546
606
|
};
|
|
547
607
|
/**
|
|
548
608
|
* 中止聊天(纯前端中止,后端继续处理)
|
|
609
|
+
* 会使当前流世代失效,避免 abort 后的 onDone 误触发 finishSuccessfully / pollResumeSession
|
|
549
610
|
*/ const abortChat = ()=>{
|
|
550
611
|
var _chatAbortController_abort;
|
|
551
612
|
manualAbort = true;
|
|
613
|
+
// 先失效世代,再 abort:FetchClient 同步触发的 onDone 会因 isActiveStream=false 直接返回
|
|
614
|
+
activeStreamId += 1;
|
|
552
615
|
clearReconnectTimer(false);
|
|
616
|
+
clearLongPollTimer();
|
|
617
|
+
isChatting.value = false;
|
|
553
618
|
chatAbortController === null || chatAbortController === void 0 ? void 0 : (_chatAbortController_abort = chatAbortController.abort) === null || _chatAbortController_abort === void 0 ? void 0 : _chatAbortController_abort.call(chatAbortController);
|
|
554
619
|
chatAbortController = null;
|
|
555
620
|
};
|
|
@@ -399,7 +399,12 @@ export class FetchClient {
|
|
|
399
399
|
const wrappedConfig = _object_spread_props(_object_spread({}, config), {
|
|
400
400
|
onError: wrappedOnError
|
|
401
401
|
});
|
|
402
|
-
const { url, fetchConfig, requestConfig } = _this.prepareRequest(wrappedConfig, true);
|
|
402
|
+
const { url, fetchConfig, requestConfig, controller } = _this.prepareRequest(wrappedConfig, true);
|
|
403
|
+
let reader;
|
|
404
|
+
const cancelReaderOnAbort = ()=>{
|
|
405
|
+
// pipeThrough(TextDecoderStream) 后,仅 abort fetch signal 在部分环境下不会立刻打断 reader.read()
|
|
406
|
+
void (reader === null || reader === void 0 ? void 0 : reader.cancel().catch(()=>{}));
|
|
407
|
+
};
|
|
403
408
|
try {
|
|
404
409
|
var _wrappedConfig_onStart, _fetchResponse_body;
|
|
405
410
|
const fetchResponse = yield fetch(url, fetchConfig);
|
|
@@ -417,12 +422,21 @@ export class FetchClient {
|
|
|
417
422
|
return;
|
|
418
423
|
}
|
|
419
424
|
(_wrappedConfig_onStart = wrappedConfig.onStart) === null || _wrappedConfig_onStart === void 0 ? void 0 : _wrappedConfig_onStart.call(wrappedConfig);
|
|
420
|
-
|
|
425
|
+
reader = (_fetchResponse_body = fetchResponse.body) === null || _fetchResponse_body === void 0 ? void 0 : _fetchResponse_body.pipeThrough(new window.TextDecoderStream()).getReader();
|
|
421
426
|
if (!reader) {
|
|
422
427
|
const error = new Error('IResponse body is not readable');
|
|
423
428
|
wrappedOnError(error);
|
|
424
429
|
return;
|
|
425
430
|
}
|
|
431
|
+
controller.signal.addEventListener('abort', cancelReaderOnAbort, {
|
|
432
|
+
once: true
|
|
433
|
+
});
|
|
434
|
+
if (controller.signal.aborted) {
|
|
435
|
+
var _wrappedConfig_onDone;
|
|
436
|
+
cancelReaderOnAbort();
|
|
437
|
+
(_wrappedConfig_onDone = wrappedConfig.onDone) === null || _wrappedConfig_onDone === void 0 ? void 0 : _wrappedConfig_onDone.call(wrappedConfig);
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
426
440
|
// 缓存跨行分片数据
|
|
427
441
|
let temp = '';
|
|
428
442
|
const isJson = (str)=>{
|
|
@@ -437,8 +451,8 @@ export class FetchClient {
|
|
|
437
451
|
while(true){
|
|
438
452
|
const { value, done } = yield reader.read();
|
|
439
453
|
if (done) {
|
|
440
|
-
var
|
|
441
|
-
(
|
|
454
|
+
var _wrappedConfig_onDone1;
|
|
455
|
+
(_wrappedConfig_onDone1 = wrappedConfig.onDone) === null || _wrappedConfig_onDone1 === void 0 ? void 0 : _wrappedConfig_onDone1.call(wrappedConfig);
|
|
442
456
|
break;
|
|
443
457
|
}
|
|
444
458
|
const values = (temp + value.toString()).split('\n');
|
|
@@ -457,13 +471,15 @@ export class FetchClient {
|
|
|
457
471
|
} catch (error) {
|
|
458
472
|
if (error instanceof Error) {
|
|
459
473
|
if (error.name === 'AbortError') {
|
|
460
|
-
var
|
|
461
|
-
(
|
|
474
|
+
var _wrappedConfig_onDone2;
|
|
475
|
+
(_wrappedConfig_onDone2 = wrappedConfig.onDone) === null || _wrappedConfig_onDone2 === void 0 ? void 0 : _wrappedConfig_onDone2.call(wrappedConfig);
|
|
462
476
|
return;
|
|
463
477
|
}
|
|
464
478
|
wrappedOnError(error);
|
|
465
479
|
}
|
|
466
480
|
throw error;
|
|
481
|
+
} finally{
|
|
482
|
+
controller.signal.removeEventListener('abort', cancelReaderOnAbort);
|
|
467
483
|
}
|
|
468
484
|
})();
|
|
469
485
|
}
|