|
@@ -1,4 +1,4 @@
|
|
|
-import { computed, nextTick, provide, ref, watch } from "vue";
|
|
|
|
|
|
|
+import { computed, nextTick, provide, ref, shallowRef, watch } from "vue";
|
|
|
import cardAPI from '@/network/api/card/index';
|
|
import cardAPI from '@/network/api/card/index';
|
|
|
import { isZhaoshangLLM } from '@/utils';
|
|
import { isZhaoshangLLM } from '@/utils';
|
|
|
import { getChatApiBaseUrl } from '@/utils/runtime-config';
|
|
import { getChatApiBaseUrl } from '@/utils/runtime-config';
|
|
@@ -8,6 +8,8 @@ import { ApiChatCoordinator, toChatQuestionAnswer, type ChatInterruptPayload } f
|
|
|
import {
|
|
import {
|
|
|
ChatGenerationTask,
|
|
ChatGenerationTask,
|
|
|
isSessionGenerating,
|
|
isSessionGenerating,
|
|
|
|
|
+ withTaskAdded,
|
|
|
|
|
+ withTaskRemoved,
|
|
|
resolveSendAction,
|
|
resolveSendAction,
|
|
|
resolveStopTargetTaskId,
|
|
resolveStopTargetTaskId,
|
|
|
shouldNormalizeSessionHistory,
|
|
shouldNormalizeSessionHistory,
|
|
@@ -54,10 +56,15 @@ export function useBusinessAssistantChat(options: UseBusinessAssistantChatOption
|
|
|
/**
|
|
/**
|
|
|
* 按会话并行的生成任务表(key = sessionId)。
|
|
* 按会话并行的生成任务表(key = sessionId)。
|
|
|
*
|
|
*
|
|
|
- * 用 `ref(new Map())` 而不是 `reactive(new Map())`:只需要 `.size` 这一个响应式依赖,
|
|
|
|
|
- * reactive 会把 task(含协调器实例)整体 proxy 化,既无意义又可能出怪问题。
|
|
|
|
|
|
|
+ * ⚠️ 必须是 **`shallowRef` + 整体替换**(用 withTaskAdded/withTaskRemoved),
|
|
|
|
|
+ * 不能用 `ref(new Map())` 也不能原地 `.set()/.delete()`:
|
|
|
|
|
+ * · `ref(new Map())` 取出来的值是**响应式代理**,`tasks.value.get(id) !== task`
|
|
|
|
|
+ * 这种同一性判断会永远成立 → 监听器全部提前返回(曾因此让 mock 流跑完不清理、
|
|
|
|
|
+ * 真实对话内容一帧不追加)
|
|
|
|
|
+ * · shallowRef 下原地 mutate 不触发响应式 → 所以每次换一个新 Map
|
|
|
|
|
+ * 详见 chat-generation-task.ts 末尾的说明。
|
|
|
*/
|
|
*/
|
|
|
- const tasks = ref(new Map<string, ChatGenerationTask>());
|
|
|
|
|
|
|
+ const tasks = shallowRef(new Map<string, ChatGenerationTask>());
|
|
|
/** 最近一次补问数据,按会话存(补问提交发生在上一轮结束之后,协调器实例已销毁) */
|
|
/** 最近一次补问数据,按会话存(补问提交发生在上一轮结束之后,协调器实例已销毁) */
|
|
|
const sessionInterruptPayloads = new Map<string, ChatInterruptPayload>();
|
|
const sessionInterruptPayloads = new Map<string, ChatInterruptPayload>();
|
|
|
/** 是否**有任何**会话在生成(mock 测试按钮等全局场景用) */
|
|
/** 是否**有任何**会话在生成(mock 测试按钮等全局场景用) */
|
|
@@ -251,7 +258,7 @@ export function useBusinessAssistantChat(options: UseBusinessAssistantChatOption
|
|
|
|
|
|
|
|
task.chunkBuffer.flush();
|
|
task.chunkBuffer.flush();
|
|
|
task.chunkBuffer.cancel();
|
|
task.chunkBuffer.cancel();
|
|
|
- tasks.value.delete(task.sessionId);
|
|
|
|
|
|
|
+ tasks.value = withTaskRemoved(tasks.value, task.sessionId);
|
|
|
|
|
|
|
|
// 补问提交发生在**下一轮**(协调器实例已销毁),所以把最近一次补问数据留在会话上
|
|
// 补问提交发生在**下一轮**(协调器实例已销毁),所以把最近一次补问数据留在会话上
|
|
|
if (task.coordinator?.lastInterruptPayload) {
|
|
if (task.coordinator?.lastInterruptPayload) {
|
|
@@ -449,7 +456,7 @@ export function useBusinessAssistantChat(options: UseBusinessAssistantChatOption
|
|
|
baseUrl: getChatApiBaseUrl(),
|
|
baseUrl: getChatApiBaseUrl(),
|
|
|
threadId: session.id,
|
|
threadId: session.id,
|
|
|
});
|
|
});
|
|
|
- tasks.value.set(session.id, createGenerationTask(session.id, pendingAiMessage.id, coordinator));
|
|
|
|
|
|
|
+ tasks.value = withTaskAdded(tasks.value, session.id, createGenerationTask(session.id, pendingAiMessage.id, coordinator));
|
|
|
|
|
|
|
|
saveHistory();
|
|
saveHistory();
|
|
|
await options.afterSend?.();
|
|
await options.afterSend?.();
|
|
@@ -487,7 +494,7 @@ export function useBusinessAssistantChat(options: UseBusinessAssistantChatOption
|
|
|
const pendingAiMessage = ensurePendingAiMessage(session.messages);
|
|
const pendingAiMessage = ensurePendingAiMessage(session.messages);
|
|
|
inputText.value = '';
|
|
inputText.value = '';
|
|
|
const mockTask = createMockTask(session.id, pendingAiMessage.id);
|
|
const mockTask = createMockTask(session.id, pendingAiMessage.id);
|
|
|
- tasks.value.set(session.id, mockTask);
|
|
|
|
|
|
|
+ tasks.value = withTaskAdded(tasks.value, session.id, mockTask);
|
|
|
saveHistory();
|
|
saveHistory();
|
|
|
await options.afterSend?.();
|
|
await options.afterSend?.();
|
|
|
|
|
|
|
@@ -549,7 +556,7 @@ export function useBusinessAssistantChat(options: UseBusinessAssistantChatOption
|
|
|
const pendingAiMessage = ensurePendingAiMessage(session.messages);
|
|
const pendingAiMessage = ensurePendingAiMessage(session.messages);
|
|
|
inputText.value = '';
|
|
inputText.value = '';
|
|
|
const mockTask = createMockTask(session.id, pendingAiMessage.id);
|
|
const mockTask = createMockTask(session.id, pendingAiMessage.id);
|
|
|
- tasks.value.set(session.id, mockTask);
|
|
|
|
|
|
|
+ tasks.value = withTaskAdded(tasks.value, session.id, mockTask);
|
|
|
saveHistory();
|
|
saveHistory();
|
|
|
await options.afterSend?.();
|
|
await options.afterSend?.();
|
|
|
|
|
|
|
@@ -1650,7 +1657,7 @@ export function useBusinessAssistantChat(options: UseBusinessAssistantChatOption
|
|
|
baseUrl: getChatApiBaseUrl(),
|
|
baseUrl: getChatApiBaseUrl(),
|
|
|
threadId: session.id,
|
|
threadId: session.id,
|
|
|
});
|
|
});
|
|
|
- tasks.value.set(session.id, createGenerationTask(session.id, pendingAiMessage.id, coordinator));
|
|
|
|
|
|
|
+ tasks.value = withTaskAdded(tasks.value, session.id, createGenerationTask(session.id, pendingAiMessage.id, coordinator));
|
|
|
|
|
|
|
|
saveHistory();
|
|
saveHistory();
|
|
|
await options.afterSend?.();
|
|
await options.afterSend?.();
|