@blueking/chat-helper 0.0.12-beta.10 → 0.0.12-beta.12

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.
@@ -80,9 +80,38 @@ function _object_spread(target) {
80
80
  return target;
81
81
  }
82
82
  import { ref } from 'vue';
83
- import { AGUIProtocol, ApprovalInterruptTicketStatus, ResumeStatus, RunFinishedOutcomeType } from '../event/index.ts.js';
83
+ import { AGUIProtocol, ApprovalInterruptTicketStatus, EventType, ResumeStatus, RunFinishedOutcomeType } from '../event/index.ts.js';
84
84
  import { MessageRole, MessageStatus, UserOperation } from '../message/index.ts.js';
85
85
  import { SessionStatus } from '../session/type.ts.js';
86
+ /** SSE 静默重连最大次数(退避总时长约 23s,落在后端 orphan grace ~30s 内) */ const MAX_RECONNECT_ATTEMPTS = 5;
87
+ const RECONNECT_BASE_DELAY_MS = 1000;
88
+ const RECONNECT_MAX_DELAY_MS = 8000;
89
+ const getReconnectDelayMs = (attempt)=>Math.min(RECONNECT_BASE_DELAY_MS * Math.pow(2, attempt - 1), RECONNECT_MAX_DELAY_MS);
90
+ /**
91
+ * 判断流式错误是否可静默重连。
92
+ * 不重试:Abort、401/403 等业务 4xx(408/429 除外);可重试:网络错误、5xx、无状态码的中断。
93
+ */ const isRecoverableStreamError = (error)=>{
94
+ var _requestError_response;
95
+ if (error.name === 'AbortError') {
96
+ return false;
97
+ }
98
+ const requestError = error;
99
+ const statusFromResponse = (_requestError_response = requestError.response) === null || _requestError_response === void 0 ? void 0 : _requestError_response.status;
100
+ const statusFromMessage = error.message.match(/status code (\d+)/i);
101
+ const status = statusFromResponse !== null && statusFromResponse !== void 0 ? statusFromResponse : statusFromMessage ? Number(statusFromMessage[1]) : undefined;
102
+ if (status !== undefined) {
103
+ if (status === 408 || status === 429) {
104
+ return true;
105
+ }
106
+ if (status >= 400 && status < 500) {
107
+ return false;
108
+ }
109
+ if (status >= 500) {
110
+ return true;
111
+ }
112
+ }
113
+ return true;
114
+ };
86
115
  /**
87
116
  * Agent 模块
88
117
  * @param options - 配置选项
@@ -91,10 +120,39 @@ import { SessionStatus } from '../session/type.ts.js';
91
120
  const info = ref(null);
92
121
  const isInfoLoading = ref(false);
93
122
  const isChatting = ref(false);
123
+ const models = ref([]);
124
+ const isModelsLoading = ref(false);
94
125
  let usedProtocol = protocol || new AGUIProtocol();
95
126
  let chatAbortController = null;
96
127
  let resumeAbortController = null;
97
128
  let longPollTimer = null;
129
+ /** 用户主动中止(abortChat),禁止自动重连 */ let manualAbort = false;
130
+ /** 当前轮次已静默重连次数 */ let reconnectAttempt = 0;
131
+ /** 本轮流是否已收到 RUN_FINISHED */ let runFinished = false;
132
+ let reconnectTimer = null;
133
+ let reconnectWaitResolve = null;
134
+ let activeStreamContext = null;
135
+ /**
136
+ * 获取可用模型列表,写入 models;失败时清空列表并抛出
137
+ */ const getLlms = (params, config)=>{
138
+ var _mediator_http;
139
+ isModelsLoading.value = true;
140
+ const request = (_mediator_http = mediator.http) === null || _mediator_http === void 0 ? void 0 : _mediator_http.agent.getLlms(params, config);
141
+ if (!request) {
142
+ isModelsLoading.value = false;
143
+ models.value = [];
144
+ return Promise.resolve([]);
145
+ }
146
+ return request.then((res)=>{
147
+ models.value = res;
148
+ return res;
149
+ })['catch']((error)=>{
150
+ models.value = [];
151
+ throw error;
152
+ })['finally'](()=>{
153
+ isModelsLoading.value = false;
154
+ });
155
+ };
98
156
  const getAgentInfo = ()=>{
99
157
  var _mediator_http;
100
158
  isInfoLoading.value = true;
@@ -123,23 +181,132 @@ import { SessionStatus } from '../session/type.ts.js';
123
181
  });
124
182
  }
125
183
  };
126
- const userOperationStreamRequest = (sessionCode, operation, payload, config)=>{
184
+ const userOperationStreamRequest = (sessionCode, operation, payload, config, model)=>{
127
185
  var _mediator_http;
128
186
  return (_mediator_http = mediator.http) === null || _mediator_http === void 0 ? void 0 : _mediator_http.message.userOperation(sessionCode, operation, payload, config).then(()=>{
129
187
  if (operation !== UserOperation.ApprovalCancel) {
130
188
  streamRequest({
131
189
  sessionCode,
132
- config
190
+ config,
191
+ model
133
192
  });
134
193
  } else {
135
194
  clearLongPollTimer();
136
- pollResumeSession(sessionCode);
195
+ pollResumeSession(sessionCode, model);
137
196
  }
138
197
  });
139
198
  };
199
+ const clearReconnectTimer = (continued = false)=>{
200
+ if (reconnectTimer) {
201
+ clearTimeout(reconnectTimer);
202
+ reconnectTimer = null;
203
+ }
204
+ if (reconnectWaitResolve) {
205
+ const resolve = reconnectWaitResolve;
206
+ reconnectWaitResolve = null;
207
+ resolve(continued);
208
+ }
209
+ };
210
+ const waitForReconnectDelay = (ms)=>new Promise((resolve)=>{
211
+ reconnectWaitResolve = resolve;
212
+ reconnectTimer = setTimeout(()=>{
213
+ reconnectTimer = null;
214
+ reconnectWaitResolve = null;
215
+ resolve(true);
216
+ }, ms);
217
+ });
218
+ const resetStreamReconnectState = ()=>{
219
+ manualAbort = false;
220
+ reconnectAttempt = 0;
221
+ runFinished = false;
222
+ clearReconnectTimer(false);
223
+ };
224
+ /** 裁掉尾部尚未完成的非用户消息,避免 DLQ 重放时重复气泡 */ const pruneTrailingIncompleteMessages = ()=>{
225
+ var _mediator_message;
226
+ const list = (_mediator_message = mediator.message) === null || _mediator_message === void 0 ? void 0 : _mediator_message.list.value;
227
+ if (!(list === null || list === void 0 ? void 0 : list.length)) {
228
+ return;
229
+ }
230
+ while(list.length > 0){
231
+ const last = list[list.length - 1];
232
+ if (last.role === MessageRole.User) {
233
+ break;
234
+ }
235
+ if (last.status === MessageStatus.Streaming || last.status === MessageStatus.Pending) {
236
+ list.pop();
237
+ continue;
238
+ }
239
+ break;
240
+ }
241
+ };
242
+ const isSameActiveSession = (sessionCode)=>{
243
+ var _mediator_session_current_value, _mediator_session_current, _mediator_session;
244
+ return sessionCode === ((_mediator_session = mediator.session) === null || _mediator_session === void 0 ? void 0 : (_mediator_session_current = _mediator_session.current) === null || _mediator_session_current === 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.sessionCode);
245
+ };
246
+ /**
247
+ * 尝试静默重连。
248
+ * @returns reconnected | finished(服务端已结束)| failed(需对外报错或中止)
249
+ */ const attemptSilentReconnect = function() {
250
+ var _ref = _async_to_generator(function*(sessionCode) {
251
+ var _mediator_session_current_value, _mediator_session, _mediator_session_current_value1, _mediator_session1, _mediator_message_list_value_at, _mediator_message;
252
+ if (manualAbort || !isSameActiveSession(sessionCode)) {
253
+ return 'failed';
254
+ }
255
+ try {
256
+ var _mediator_session2;
257
+ yield (_mediator_session2 = mediator.session) === null || _mediator_session2 === void 0 ? void 0 : _mediator_session2.getSession(sessionCode);
258
+ } catch (e) {
259
+ // 状态刷新失败时沿用本地 session.status
260
+ }
261
+ if (manualAbort || !isSameActiveSession(sessionCode)) {
262
+ return 'failed';
263
+ }
264
+ 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) {
265
+ return 'finished';
266
+ }
267
+ if (reconnectAttempt >= MAX_RECONNECT_ATTEMPTS) {
268
+ return 'failed';
269
+ }
270
+ reconnectAttempt += 1;
271
+ // 静默重连期间保持「生成中」态
272
+ isChatting.value = true;
273
+ const delayMs = getReconnectDelayMs(reconnectAttempt);
274
+ const waited = yield waitForReconnectDelay(delayMs);
275
+ if (!waited || manualAbort || !isSameActiveSession(sessionCode)) {
276
+ return 'failed';
277
+ }
278
+ 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
+ return 'finished';
280
+ }
281
+ pruneTrailingIncompleteMessages();
282
+ 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
+ const ctx = activeStreamContext;
284
+ streamRequest({
285
+ sessionCode,
286
+ model: ctx === null || ctx === void 0 ? void 0 : ctx.model,
287
+ url: ctx === null || ctx === void 0 ? void 0 : ctx.url,
288
+ config: ctx === null || ctx === void 0 ? void 0 : ctx.config,
289
+ lastMessageId: lastMessageId !== undefined ? String(lastMessageId) : undefined,
290
+ isReconnect: true
291
+ });
292
+ return 'reconnected';
293
+ });
294
+ return function attemptSilentReconnect(sessionCode) {
295
+ return _ref.apply(this, arguments);
296
+ };
297
+ }();
140
298
  const streamRequest = function() {
141
- var _ref = _async_to_generator(function*({ sessionCode, url, config, resume, input, lastMessageId }) {
299
+ var _ref = _async_to_generator(function*({ sessionCode, model, url, config, resume, input, lastMessageId, isReconnect = false }) {
142
300
  var _mediator_http;
301
+ if (!isReconnect) {
302
+ resetStreamReconnectState();
303
+ }
304
+ activeStreamContext = {
305
+ sessionCode,
306
+ model: model,
307
+ url,
308
+ config
309
+ };
143
310
  // ag-ui 协议需要注入消息模块
144
311
  if (usedProtocol instanceof AGUIProtocol) {
145
312
  usedProtocol.injectMessageModule(mediator.message);
@@ -154,8 +321,7 @@ import { SessionStatus } from '../session/type.ts.js';
154
321
  sessionCode
155
322
  });
156
323
  }
157
- // 事件代理
158
- const onDone = ()=>{
324
+ const finishSuccessfully = ()=>{
159
325
  var _usedProtocol_onDone;
160
326
  isChatting.value = false;
161
327
  (_usedProtocol_onDone = usedProtocol.onDone) === null || _usedProtocol_onDone === void 0 ? void 0 : _usedProtocol_onDone.call(usedProtocol);
@@ -166,19 +332,73 @@ import { SessionStatus } from '../session/type.ts.js';
166
332
  var _mediator_message;
167
333
  const lastUserMessage = (_mediator_message = mediator.message) === null || _mediator_message === void 0 ? void 0 : _mediator_message.list.value.findLast((item)=>item.role === MessageRole.User);
168
334
  const lastApiUserMessage = res.findLast((item)=>item.role === MessageRole.User);
169
- lastUserMessage.id = lastApiUserMessage.id;
335
+ if (lastUserMessage && lastApiUserMessage) {
336
+ lastUserMessage.id = lastApiUserMessage.id;
337
+ }
170
338
  });
171
339
  }
172
340
  // 轮询接口,判断是否可以继续聊天
173
341
  pollResumeSession(sessionCode);
174
342
  };
175
- const onError = (error)=>{
343
+ const failWithError = (error)=>{
176
344
  var _usedProtocol_onError;
177
345
  isChatting.value = false;
178
346
  (_usedProtocol_onError = usedProtocol.onError) === null || _usedProtocol_onError === void 0 ? void 0 : _usedProtocol_onError.call(usedProtocol, error);
179
347
  };
348
+ // 事件代理
349
+ const onDone = ()=>{
350
+ // 用户 abort:FetchClient 将 AbortError 转为 onDone,不重连
351
+ if (manualAbort || runFinished) {
352
+ finishSuccessfully();
353
+ return;
354
+ }
355
+ // 连接被干净掐断且未收到 RUN_FINISHED:尝试静默重连
356
+ void attemptSilentReconnect(sessionCode).then((result)=>{
357
+ if (result === 'reconnected') {
358
+ // 保持 isChatting=true,不展示错误
359
+ return;
360
+ }
361
+ if (result === 'finished') {
362
+ finishSuccessfully();
363
+ return;
364
+ }
365
+ // abort / 切会话导致的失败:静默结束,不展示错误气泡
366
+ if (manualAbort || !isSameActiveSession(sessionCode)) {
367
+ isChatting.value = false;
368
+ return;
369
+ }
370
+ failWithError(new Error('Connection lost, please try again'));
371
+ });
372
+ };
373
+ const onError = (error)=>{
374
+ if (manualAbort) {
375
+ return;
376
+ }
377
+ if (!isRecoverableStreamError(error)) {
378
+ failWithError(error);
379
+ return;
380
+ }
381
+ void attemptSilentReconnect(sessionCode).then((result)=>{
382
+ if (result === 'reconnected') {
383
+ return;
384
+ }
385
+ if (result === 'finished') {
386
+ finishSuccessfully();
387
+ return;
388
+ }
389
+ if (manualAbort || !isSameActiveSession(sessionCode)) {
390
+ isChatting.value = false;
391
+ return;
392
+ }
393
+ failWithError(error);
394
+ });
395
+ };
180
396
  const onMessage = (event)=>{
181
397
  var _usedProtocol_onMessage;
398
+ const typedEvent = event;
399
+ if ((typedEvent === null || typedEvent === void 0 ? void 0 : typedEvent.type) === EventType.RunFinished) {
400
+ runFinished = true;
401
+ }
182
402
  (_usedProtocol_onMessage = usedProtocol.onMessage) === null || _usedProtocol_onMessage === void 0 ? void 0 : _usedProtocol_onMessage.call(usedProtocol, event);
183
403
  };
184
404
  const onStart = ()=>{
@@ -194,6 +414,7 @@ import { SessionStatus } from '../session/type.ts.js';
194
414
  method: 'POST',
195
415
  data: {
196
416
  session_code: sessionCode,
417
+ model,
197
418
  input,
198
419
  execute_kwargs: {
199
420
  stream: true,
@@ -222,8 +443,9 @@ import { SessionStatus } from '../session/type.ts.js';
222
443
  * @param url - 请求 URL(可选)
223
444
  * @param config - 请求配置(可选)
224
445
  * @param property - 消息属性,用于传递引用内容或快捷键相关信息(可选)
446
+ * @param model - 模型标识(可选)
225
447
  */ const chat = function() {
226
- var _ref = _async_to_generator(function*(userInput, sessionCode, url, config, property) {
448
+ var _ref = _async_to_generator(function*(userInput, sessionCode, url, config, property, model) {
227
449
  var _mediator_message;
228
450
  // 先新增一个 message
229
451
  yield (_mediator_message = mediator.message) === null || _mediator_message === void 0 ? void 0 : _mediator_message.createAndPlusMessage(_object_spread({
@@ -237,11 +459,12 @@ import { SessionStatus } from '../session/type.ts.js';
237
459
  // 发起聊天
238
460
  streamRequest({
239
461
  sessionCode,
462
+ model,
240
463
  url,
241
464
  config
242
465
  });
243
466
  });
244
- return function chat(userInput, sessionCode, url, config, property) {
467
+ return function chat(userInput, sessionCode, url, config, property, model) {
245
468
  return _ref.apply(this, arguments);
246
469
  };
247
470
  }();
@@ -251,13 +474,15 @@ import { SessionStatus } from '../session/type.ts.js';
251
474
  * @param sessionCode - 会话代码
252
475
  * @param url - 请求 URL(可选)
253
476
  * @param config - 请求配置(可选)
254
- */ const resumeStreamingChat = (sessionCode, url, config)=>{
477
+ * @param model - 模型标识(可选)
478
+ */ const resumeStreamingChat = (sessionCode, url, config, model)=>{
255
479
  var _mediator_session_current_value, _mediator_session;
256
480
  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) {
257
481
  var _mediator_message_list_value_at, _mediator_message;
258
482
  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;
259
483
  streamRequest({
260
484
  sessionCode,
485
+ model,
261
486
  url,
262
487
  config,
263
488
  lastMessageId
@@ -268,7 +493,7 @@ import { SessionStatus } from '../session/type.ts.js';
268
493
  * 轮询接口,判断是否可以继续聊天
269
494
  * @param sessionCode - 会话编码
270
495
  * @returns 是否可以继续聊天
271
- */ const pollResumeSession = (sessionCode)=>{
496
+ */ const pollResumeSession = (sessionCode, model)=>{
272
497
  var _mediator_message, _lastMessage_content_outcome, _lastMessage_content;
273
498
  const lastMessage = (_mediator_message = mediator.message) === null || _mediator_message === void 0 ? void 0 : _mediator_message.list.value.at(-1);
274
499
  const pendingApprovalInterrupt = (lastMessage === null || lastMessage === void 0 ? void 0 : (_lastMessage_content = lastMessage.content) === null || _lastMessage_content === void 0 ? void 0 : (_lastMessage_content_outcome = _lastMessage_content.outcome) === null || _lastMessage_content_outcome === void 0 ? void 0 : _lastMessage_content_outcome.type) === RunFinishedOutcomeType.Interrupt ? lastMessage.content.outcome.interrupts.find((interrupt)=>{
@@ -295,6 +520,7 @@ import { SessionStatus } from '../session/type.ts.js';
295
520
  // 可以继续聊天,重新发起聊天(携带 execute_kwargs.resume 通知后端恢复中断)
296
521
  streamRequest({
297
522
  sessionCode,
523
+ model,
298
524
  resume: {
299
525
  interruptId: pendingApprovalInterrupt.id,
300
526
  status: ResumeStatus.Resolved
@@ -305,7 +531,7 @@ import { SessionStatus } from '../session/type.ts.js';
305
531
  var _mediator_session_current_value, _mediator_session_current, _mediator_session;
306
532
  // 如果会话不匹配,则不继续轮询
307
533
  if (sessionCode !== ((_mediator_session = mediator.session) === null || _mediator_session === void 0 ? void 0 : (_mediator_session_current = _mediator_session.current) === null || _mediator_session_current === 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.sessionCode)) return;
308
- pollResumeSession(sessionCode);
534
+ pollResumeSession(sessionCode, model);
309
535
  }, 10000);
310
536
  }
311
537
  });
@@ -322,6 +548,8 @@ import { SessionStatus } from '../session/type.ts.js';
322
548
  * 中止聊天(纯前端中止,后端继续处理)
323
549
  */ const abortChat = ()=>{
324
550
  var _chatAbortController_abort;
551
+ manualAbort = true;
552
+ clearReconnectTimer(false);
325
553
  chatAbortController === null || chatAbortController === void 0 ? void 0 : (_chatAbortController_abort = chatAbortController.abort) === null || _chatAbortController_abort === void 0 ? void 0 : _chatAbortController_abort.call(chatAbortController);
326
554
  chatAbortController = null;
327
555
  };
@@ -346,8 +574,9 @@ import { SessionStatus } from '../session/type.ts.js';
346
574
  * @param newContent - 新内容(可选,不传则使用原消息内容;支持多模态)
347
575
  * @param url - 请求 URL(可选)
348
576
  * @param config - 请求配置(可选)
577
+ * @param model - 模型标识(可选)
349
578
  */ const resendMessage = function() {
350
- var _ref = _async_to_generator(function*(messageId, sessionCode, newContent, url, config) {
579
+ var _ref = _async_to_generator(function*(messageId, sessionCode, newContent, url, config, model) {
351
580
  var _mediator_message, _mediator_message1, _mediator_message2;
352
581
  const messages = ((_mediator_message = mediator.message) === null || _mediator_message === void 0 ? void 0 : _mediator_message.list.value) || [];
353
582
  // 1. 找到目标用户消息
@@ -380,6 +609,7 @@ import { SessionStatus } from '../session/type.ts.js';
380
609
  // 6. 立即发起流式请求(不等待删除和创建的 API 完成)
381
610
  streamRequest({
382
611
  sessionCode,
612
+ model,
383
613
  url,
384
614
  config
385
615
  });
@@ -391,20 +621,29 @@ import { SessionStatus } from '../session/type.ts.js';
391
621
  console.error('[resendMessage] API error:', error);
392
622
  });
393
623
  });
394
- return function resendMessage(messageId, sessionCode, newContent, url, config) {
624
+ return function resendMessage(messageId, sessionCode, newContent, url, config, model) {
395
625
  return _ref.apply(this, arguments);
396
626
  };
397
627
  }();
398
628
  const reset = (protocol)=>{
399
- // 重置状态
629
+ abortChat();
630
+ // 重置状态(保留 manualAbort=true,避免 abort 异步 onDone 误触发重连;下次 streamRequest 会复位)
400
631
  usedProtocol = protocol || new AGUIProtocol();
401
632
  info.value = null;
402
633
  isInfoLoading.value = false;
634
+ isChatting.value = false;
635
+ models.value = [];
636
+ isModelsLoading.value = false;
637
+ activeStreamContext = null;
638
+ reconnectAttempt = 0;
639
+ runFinished = false;
403
640
  };
404
641
  return {
405
642
  info,
406
643
  isInfoLoading,
407
644
  isChatting,
645
+ models,
646
+ isModelsLoading,
408
647
  chat,
409
648
  handleRole,
410
649
  resendMessage,
@@ -412,6 +651,7 @@ import { SessionStatus } from '../session/type.ts.js';
412
651
  abortChat,
413
652
  stopChat,
414
653
  getAgentInfo,
654
+ getLlms,
415
655
  reset,
416
656
  pollResumeSession,
417
657
  clearLongPollTimer,
@@ -27,6 +27,7 @@ export declare class AGUIProtocol implements ISSEProtocol {
27
27
  * 记录活动的完整状态
28
28
  */
29
29
  handleActivitySnapshotEvent(event: IActivitySnapshotEvent): void;
30
+ handleArtifactsGeneratedCustomEvent(event: ICustomEvent): void;
30
31
  /**
31
32
  * 处理自定义事件
32
33
  */
@@ -51,6 +52,7 @@ export declare class AGUIProtocol implements ISSEProtocol {
51
52
  * 自定义事件 处理流程编排任务更新
52
53
  */
53
54
  handleFlowAgentUpdateCustomEvent(event: ICustomEvent): void;
55
+ handleInfoCustomEvent(event: ICustomEvent): void;
54
56
  /**
55
57
  * 自定义事件 处理意图识别结束
56
58
  */
@@ -131,6 +133,7 @@ export declare class AGUIProtocol implements ISSEProtocol {
131
133
  handleTextMessageEndEvent(event: ITextMessageEndEvent): void;
132
134
  /**
133
135
  * 处理文本消息开始事件
136
+ * 断线续传时 DLQ 会重放事件:同 messageId 已存在则重置内容,避免重复气泡/文本翻倍
134
137
  */
135
138
  handleTextMessageStartEvent(event: ITextMessageStartEvent): void;
136
139
  /**
@@ -35,7 +35,7 @@
35
35
  }
36
36
  return obj;
37
37
  }
38
- import { transferMessageApi2Message } from '../http/transform/index.ts.js';
38
+ import { mergeArtifactsActivityIntoMessages, transferMessageApi2Message } from '../http/transform/index.ts.js';
39
39
  import { ActivityType, MessageRole, MessageStatus, MessageType } from '../message/index.ts.js';
40
40
  import { CustomEventName, EventType, FlowTaskState, RunFinishedOutcomeType } from './type.ts.js';
41
41
  /**
@@ -50,18 +50,38 @@ import { CustomEventName, EventType, FlowTaskState, RunFinishedOutcomeType } fro
50
50
  * 处理活动快照事件
51
51
  * 记录活动的完整状态
52
52
  */ handleActivitySnapshotEvent(event) {
53
- const message = this.messageModule.getCurrentLoadingMessage();
53
+ if (event.activityType === ActivityType.Info) {
54
+ const message = this.messageModule.getMessageByMessageId(event.messageId);
55
+ if ((message === null || message === void 0 ? void 0 : message.role) === MessageRole.Info) {
56
+ message.content = event.content;
57
+ }
58
+ return;
59
+ }
54
60
  if (event.activityType === ActivityType.Interrupt) {
61
+ const message = this.messageModule.getCurrentLoadingMessage();
55
62
  if ((message === null || message === void 0 ? void 0 : message.role) === MessageRole.Interrupt) {
56
63
  message.content = event.content;
57
64
  message.status = MessageStatus.Complete;
58
65
  }
59
66
  }
60
67
  }
68
+ handleArtifactsGeneratedCustomEvent(event) {
69
+ const message = this.messageModule.list.value.findLast((item)=>item.role === MessageRole.Assistant);
70
+ if (message) {
71
+ var _message;
72
+ const value = event.value;
73
+ var _property;
74
+ (_property = (_message = message).property) !== null && _property !== void 0 ? _property : _message.property = {};
75
+ message.property.artifacts = value.artifacts;
76
+ }
77
+ }
61
78
  /**
62
79
  * 处理自定义事件
63
80
  */ handleCustomEvent(event) {
64
81
  switch(event.name){
82
+ case CustomEventName.ArtifactsGenerated:
83
+ this.handleArtifactsGeneratedCustomEvent(event);
84
+ break;
65
85
  case CustomEventName.FlowAgentStart:
66
86
  this.handleFlowAgentStartCustomEvent(event);
67
87
  break;
@@ -98,6 +118,9 @@ import { CustomEventName, EventType, FlowTaskState, RunFinishedOutcomeType } fro
98
118
  case CustomEventName.FlowAgentUpdate:
99
119
  this.handleFlowAgentUpdateCustomEvent(event);
100
120
  break;
121
+ case CustomEventName.Info:
122
+ this.handleInfoCustomEvent(event);
123
+ break;
101
124
  default:
102
125
  break;
103
126
  }
@@ -171,6 +194,15 @@ import { CustomEventName, EventType, FlowTaskState, RunFinishedOutcomeType } fro
171
194
  }
172
195
  });
173
196
  }
197
+ handleInfoCustomEvent(event) {
198
+ const value = event.value;
199
+ this.messageModule.plusMessage({
200
+ role: MessageRole.Info,
201
+ messageId: value.messageId,
202
+ content: value.content,
203
+ status: MessageStatus.Complete
204
+ });
205
+ }
174
206
  /**
175
207
  * 自定义事件 处理意图识别结束
176
208
  */ handleKnowledgeRagEndCustomEvent(_event) {
@@ -214,7 +246,7 @@ import { CustomEventName, EventType, FlowTaskState, RunFinishedOutcomeType } fro
214
246
  * 用于同步多端消息状态
215
247
  */ handleMessagesSnapshotEvent(event) {
216
248
  if (event.messages && event.messages.length > 0) {
217
- this.messageModule.list.value = event.messages.map(transferMessageApi2Message);
249
+ this.messageModule.list.value = mergeArtifactsActivityIntoMessages(event.messages.map(transferMessageApi2Message));
218
250
  }
219
251
  }
220
252
  /**
@@ -349,7 +381,14 @@ import { CustomEventName, EventType, FlowTaskState, RunFinishedOutcomeType } fro
349
381
  }
350
382
  /**
351
383
  * 处理文本消息开始事件
384
+ * 断线续传时 DLQ 会重放事件:同 messageId 已存在则重置内容,避免重复气泡/文本翻倍
352
385
  */ handleTextMessageStartEvent(event) {
386
+ const existing = this.messageModule.getMessageByMessageId(event.messageId);
387
+ if (existing) {
388
+ existing.content = '';
389
+ existing.status = MessageStatus.Streaming;
390
+ return;
391
+ }
353
392
  this.messageModule.plusMessage({
354
393
  role: event.role,
355
394
  content: '',
@@ -1,11 +1,13 @@
1
- import { type IMessageApi, type MessageRole, type MessageStatus } from '../message/type';
1
+ import { type IMessageApi, type IMessageArtifact, type MessageRole, type MessageStatus } from '../message/type';
2
2
  import type { JSONSchema4 } from 'json-schema';
3
3
  export declare enum CustomEventName {
4
+ ArtifactsGenerated = "artifacts_generated",
4
5
  FlowAgentEnd = "flow_agent_end",
5
6
  FlowAgentResult = "flow_agent_result",
6
7
  FlowAgentStart = "flow_agent_start",
7
8
  FlowAgentUpdate = "flow_agent_update",
8
9
  FlowAgentRestart = "flow_agent_restart",
10
+ Info = "info",
9
11
  KnowledgeRagEnd = "knowledge_rag_end",
10
12
  KnowledgeRagResult = "knowledge_rag_result",
11
13
  KnowledgeRagStart = "knowledge_rag_start",
@@ -114,6 +116,11 @@ export interface IActivitySnapshotEvent extends IBaseEvent {
114
116
  replace?: boolean;
115
117
  type: EventType.ActivitySnapshot;
116
118
  }
119
+ export interface IArtifactsGeneratedCustomValue {
120
+ artifacts: IMessageArtifact[];
121
+ runId: string;
122
+ status: 'complete' | 'empty';
123
+ }
117
124
  export interface IBaseEvent {
118
125
  timestamp?: number;
119
126
  type: EventType;
@@ -125,7 +132,7 @@ export interface IBaseEvent {
125
132
  export interface ICustomEvent extends IBaseEvent {
126
133
  name: CustomEventName;
127
134
  type: EventType.Custom;
128
- value: IFlowAgentEndCustomValue | IFlowAgentResultCustomValue | IFlowAgentStartCustomValue | IFlowAgentRestartCustomValue | IKnowledgeRagResultCustomValue | IKnowledgeRagTextContentCustomValue | IReferenceDocumentCustomValue | ITempMessageCustomValue | IApprovalResultCustomValue;
135
+ value: IArtifactsGeneratedCustomValue | IFlowAgentEndCustomValue | IFlowAgentResultCustomValue | IFlowAgentStartCustomValue | IFlowAgentRestartCustomValue | IInfoCustomValue | IKnowledgeRagResultCustomValue | IKnowledgeRagTextContentCustomValue | IReferenceDocumentCustomValue | ITempMessageCustomValue | IApprovalResultCustomValue;
129
136
  }
130
137
  export type IEvent = IActivityDeltaEvent | IActivitySnapshotEvent | ICustomEvent | IMessagesSnapshotEvent | IRawEvent | IRunErrorEvent | IRunFinishedEvent | IRunStartedEvent | IStateDeltaEvent | IStateSnapshotEvent | IStepFinishedEvent | IStepStartedEvent | ITextMessageChunkEvent | ITextMessageContentEvent | ITextMessageEndEvent | ITextMessageStartEvent | IThinkingEndEvent | IThinkingStartEvent | IThinkingTextMessageContentEvent | IThinkingTextMessageEndEvent | IThinkingTextMessageStartEvent | IToolCallArgsEvent | IToolCallChunkEvent | IToolCallEndEvent | IToolCallResultEvent | IToolCallStartEvent;
131
138
  export type IFlowAgentEndCustomValue = {
@@ -152,6 +159,10 @@ export type IFlowAgentStartCustomValue = {
152
159
  task_id: string;
153
160
  }[];
154
161
  export type IFlowAgentRestartCustomValue = IFlowAgentStartCustomValue;
162
+ export interface IInfoCustomValue {
163
+ content: string;
164
+ messageId: string;
165
+ }
155
166
  export type IKnowledgeRagResultCustomValue = IReferenceDocumentCustomValue;
156
167
  export interface IKnowledgeRagTextContentCustomValue {
157
168
  cover: boolean;
@@ -24,11 +24,13 @@
24
24
  * IN THE SOFTWARE.
25
25
  */ export var CustomEventName;
26
26
  (function(CustomEventName) {
27
+ CustomEventName["ArtifactsGenerated"] = "artifacts_generated";
27
28
  CustomEventName["FlowAgentEnd"] = "flow_agent_end";
28
29
  CustomEventName["FlowAgentResult"] = "flow_agent_result";
29
30
  CustomEventName["FlowAgentStart"] = "flow_agent_start";
30
31
  CustomEventName["FlowAgentUpdate"] = "flow_agent_update";
31
32
  CustomEventName["FlowAgentRestart"] = "flow_agent_restart";
33
+ CustomEventName["Info"] = "info";
32
34
  CustomEventName["KnowledgeRagEnd"] = "knowledge_rag_end";
33
35
  CustomEventName["KnowledgeRagResult"] = "knowledge_rag_result";
34
36
  CustomEventName["KnowledgeRagStart"] = "knowledge_rag_start";
@@ -12,6 +12,7 @@ export declare const useHttp: (options: IUseChatHelperOptions) => {
12
12
  onError: (handler: import("./fetch").GlobalErrorHandler, options?: import("./fetch").ErrorHandlerOptions) => void;
13
13
  agent: {
14
14
  getAgentInfo: (config?: import("./fetch").IRequestConfig) => Promise<import("..").IAgentInfo>;
15
+ getLlms: (params?: import("..").ILlmListQuery, config?: import("./fetch").IRequestConfig) => Promise<import("..").ILlmItem[]>;
15
16
  };
16
17
  session: {
17
18
  clearSession: (sessionCode: string, config?: import("./fetch").IRequestConfig) => Promise<unknown>;
@@ -38,6 +39,7 @@ export declare const useHttp: (options: IUseChatHelperOptions) => {
38
39
  uploadFile: (sessionCode: string, file: File, config?: import("./fetch").IRequestConfig) => Promise<{
39
40
  download_url?: string;
40
41
  }>;
42
+ getPvFileDownloadUrl: (sessionCode: string, path: string, options?: import("..").GetPvFileDownloadUrlOptions, config?: import("./fetch").IRequestConfig) => Promise<import("..").IPvFileDownloadUrlResult>;
41
43
  isResumeSession: (sessionCode: string, config?: import("./fetch").IRequestConfig) => Promise<boolean>;
42
44
  };
43
45
  message: {
@@ -1,3 +1,4 @@
1
+ import type { ILlmItem, ILlmListQuery } from '../../agent/type';
1
2
  import type { FetchClient, IRequestConfig } from '../fetch';
2
3
  /**
3
4
  * agent 相关 http 接口
@@ -6,4 +7,5 @@ import type { FetchClient, IRequestConfig } from '../fetch';
6
7
  */
7
8
  export declare const useAgent: (fetchClient: FetchClient) => {
8
9
  getAgentInfo: (config?: IRequestConfig) => Promise<import("../../agent/type").IAgentInfo>;
10
+ getLlms: (params?: ILlmListQuery, config?: IRequestConfig) => Promise<ILlmItem[]>;
9
11
  };