feat(agent): 添加流式对话取消功能并优化错误处理
添加 streamAbortController 以支持取消正在进行的流式对话 优化初始化流程的顺序和错误处理逻辑 修复 configurableItems 计算中的潜在空指针问题
This commit is contained in:
parent
b524ca687b
commit
13b0c7b4c8
@ -25,6 +25,7 @@ export const useAgentStore = defineStore('agent', {
|
|||||||
// 对话状态
|
// 对话状态
|
||||||
onGoingConv: { msgChunks: {} }, // 正在进行的对话(流式)
|
onGoingConv: { msgChunks: {} }, // 正在进行的对话(流式)
|
||||||
isStreaming: false, // 是否正在接收流式响应
|
isStreaming: false, // 是否正在接收流式响应
|
||||||
|
streamAbortController: null, // 流式对话取消控制器
|
||||||
|
|
||||||
// 加载状态
|
// 加载状态
|
||||||
isLoadingAgents: false,
|
isLoadingAgents: false,
|
||||||
@ -48,8 +49,9 @@ export const useAgentStore = defineStore('agent', {
|
|||||||
isDefaultAgent: (state) => state.selectedAgentId === state.defaultAgentId,
|
isDefaultAgent: (state) => state.selectedAgentId === state.defaultAgentId,
|
||||||
configurableItems: (state) => {
|
configurableItems: (state) => {
|
||||||
const agent = state.selectedAgentId ? state.agents[state.selectedAgentId] : null;
|
const agent = state.selectedAgentId ? state.agents[state.selectedAgentId] : null;
|
||||||
const agentConfigurableItems = agent.configurable_items || {};
|
if (!agent || !agent.configurable_items) return {};
|
||||||
if (!agentConfigurableItems) return {};
|
|
||||||
|
const agentConfigurableItems = agent.configurable_items;
|
||||||
const items = { ...agentConfigurableItems };
|
const items = { ...agentConfigurableItems };
|
||||||
Object.keys(items).forEach(key => {
|
Object.keys(items).forEach(key => {
|
||||||
const item = items[key];
|
const item = items[key];
|
||||||
@ -80,8 +82,11 @@ export const useAgentStore = defineStore('agent', {
|
|||||||
},
|
},
|
||||||
conversations: (state) => {
|
conversations: (state) => {
|
||||||
const historyConvs = MessageProcessor.convertServerHistoryToMessages(state.currentThreadMessages);
|
const historyConvs = MessageProcessor.convertServerHistoryToMessages(state.currentThreadMessages);
|
||||||
// Access onGoingConvMessages getter through state
|
// Compute ongoing messages directly from state to avoid circular reference
|
||||||
const onGoingMessages = state.onGoingConvMessages;
|
const msgs = Object.values(state.onGoingConv.msgChunks).map(MessageProcessor.mergeMessageChunk);
|
||||||
|
const onGoingMessages = msgs.length > 0
|
||||||
|
? MessageProcessor.convertToolResultToMessages(msgs).filter(msg => msg.type !== 'tool')
|
||||||
|
: [];
|
||||||
|
|
||||||
if (onGoingMessages.length > 0) {
|
if (onGoingMessages.length > 0) {
|
||||||
// Create a new conversation object for the ongoing messages
|
// Create a new conversation object for the ongoing messages
|
||||||
@ -101,14 +106,19 @@ export const useAgentStore = defineStore('agent', {
|
|||||||
if (this.isInitialized) return;
|
if (this.isInitialized) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await Promise.all([
|
// 首先加载智能体列表
|
||||||
this.fetchAgents(),
|
await this.fetchAgents();
|
||||||
this.fetchDefaultAgent(),
|
|
||||||
this.fetchTools()
|
// 然后设置默认智能体
|
||||||
]);
|
await this.fetchDefaultAgent();
|
||||||
|
|
||||||
|
// 最后加载工具
|
||||||
|
await this.fetchTools();
|
||||||
|
|
||||||
this.isInitialized = true;
|
this.isInitialized = true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to initialize agent store:', error);
|
console.error('Failed to initialize agent store:', error);
|
||||||
|
handleChatError(error, 'initialize');
|
||||||
this.error = error.message;
|
this.error = error.message;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -127,6 +137,7 @@ export const useAgentStore = defineStore('agent', {
|
|||||||
}, {});
|
}, {});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch agents:', error);
|
console.error('Failed to fetch agents:', error);
|
||||||
|
handleChatError(error, 'fetch');
|
||||||
this.error = error.message;
|
this.error = error.message;
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
@ -140,12 +151,13 @@ export const useAgentStore = defineStore('agent', {
|
|||||||
const response = await agentApi.getDefaultAgent();
|
const response = await agentApi.getDefaultAgent();
|
||||||
this.defaultAgentId = response.default_agent_id;
|
this.defaultAgentId = response.default_agent_id;
|
||||||
|
|
||||||
// 如果没有选中的智能体,则选择默认智能体
|
// 如果没有选中的智能体且默认智能体存在于智能体列表中,则选择默认智能体
|
||||||
if (!this.selectedAgentId && this.defaultAgentId) {
|
if (!this.selectedAgentId && this.defaultAgentId && this.agents[this.defaultAgentId]) {
|
||||||
this.selectedAgentId = this.defaultAgentId;
|
this.selectedAgentId = this.defaultAgentId;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch default agent:', error);
|
console.error('Failed to fetch default agent:', error);
|
||||||
|
handleChatError(error, 'fetch');
|
||||||
this.error = error.message;
|
this.error = error.message;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -157,6 +169,7 @@ export const useAgentStore = defineStore('agent', {
|
|||||||
this.defaultAgentId = agentId;
|
this.defaultAgentId = agentId;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to set default agent:', error);
|
console.error('Failed to set default agent:', error);
|
||||||
|
handleChatError(error, 'save');
|
||||||
this.error = error.message;
|
this.error = error.message;
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
@ -186,6 +199,7 @@ export const useAgentStore = defineStore('agent', {
|
|||||||
this.originalAgentConfig = { ...response.config };
|
this.originalAgentConfig = { ...response.config };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load agent config:', error);
|
console.error('Failed to load agent config:', error);
|
||||||
|
handleChatError(error, 'load');
|
||||||
this.error = error.message;
|
this.error = error.message;
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
@ -203,6 +217,7 @@ export const useAgentStore = defineStore('agent', {
|
|||||||
this.originalAgentConfig = { ...this.agentConfig };
|
this.originalAgentConfig = { ...this.agentConfig };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to save agent config:', error);
|
console.error('Failed to save agent config:', error);
|
||||||
|
handleChatError(error, 'save');
|
||||||
this.error = error.message;
|
this.error = error.message;
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
@ -233,6 +248,7 @@ export const useAgentStore = defineStore('agent', {
|
|||||||
this.availableTools = response.tools;
|
this.availableTools = response.tools;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch tools:', error);
|
console.error('Failed to fetch tools:', error);
|
||||||
|
handleChatError(error, 'fetch');
|
||||||
this.error = error.message;
|
this.error = error.message;
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
@ -260,6 +276,7 @@ export const useAgentStore = defineStore('agent', {
|
|||||||
this.threads[targetAgentId] = threads || [];
|
this.threads[targetAgentId] = threads || [];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch threads:', error);
|
console.error('Failed to fetch threads:', error);
|
||||||
|
handleChatError(error, 'fetch');
|
||||||
this.error = error.message;
|
this.error = error.message;
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
@ -289,6 +306,7 @@ export const useAgentStore = defineStore('agent', {
|
|||||||
return thread;
|
return thread;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to create thread:', error);
|
console.error('Failed to create thread:', error);
|
||||||
|
handleChatError(error, 'create');
|
||||||
this.error = error.message;
|
this.error = error.message;
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
@ -315,6 +333,7 @@ export const useAgentStore = defineStore('agent', {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to delete thread:', error);
|
console.error('Failed to delete thread:', error);
|
||||||
|
handleChatError(error, 'delete');
|
||||||
this.error = error.message;
|
this.error = error.message;
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
@ -336,6 +355,7 @@ export const useAgentStore = defineStore('agent', {
|
|||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to update thread:', error);
|
console.error('Failed to update thread:', error);
|
||||||
|
handleChatError(error, 'update');
|
||||||
this.error = error.message;
|
this.error = error.message;
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
@ -374,6 +394,11 @@ export const useAgentStore = defineStore('agent', {
|
|||||||
// --- 流式对话 Actions ---
|
// --- 流式对话 Actions ---
|
||||||
|
|
||||||
resetOnGoingConv() {
|
resetOnGoingConv() {
|
||||||
|
// 取消之前的请求(如果存在)
|
||||||
|
if (this.streamAbortController) {
|
||||||
|
this.streamAbortController.abort();
|
||||||
|
this.streamAbortController = null;
|
||||||
|
}
|
||||||
this.onGoingConv = { msgChunks: {} };
|
this.onGoingConv = { msgChunks: {} };
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -400,6 +425,16 @@ export const useAgentStore = defineStore('agent', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 取消流式对话
|
||||||
|
cancelStreaming() {
|
||||||
|
if (this.streamAbortController && this.isStreaming) {
|
||||||
|
this.streamAbortController.abort();
|
||||||
|
this.streamAbortController = null;
|
||||||
|
this.isStreaming = false;
|
||||||
|
this.resetOnGoingConv();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// 发送消息并处理流式响应
|
// 发送消息并处理流式响应
|
||||||
async sendMessage(text) {
|
async sendMessage(text) {
|
||||||
if (!this.selectedAgentId || !this.currentThreadId || !text) {
|
if (!this.selectedAgentId || !this.currentThreadId || !text) {
|
||||||
@ -410,6 +445,9 @@ export const useAgentStore = defineStore('agent', {
|
|||||||
this.isStreaming = true;
|
this.isStreaming = true;
|
||||||
this.resetOnGoingConv();
|
this.resetOnGoingConv();
|
||||||
|
|
||||||
|
// 创建新的 AbortController
|
||||||
|
this.streamAbortController = new AbortController();
|
||||||
|
|
||||||
// 如果是新对话,用消息内容作为标题
|
// 如果是新对话,用消息内容作为标题
|
||||||
if (this.currentThreadMessages.length === 0) {
|
if (this.currentThreadMessages.length === 0) {
|
||||||
this.updateThread(this.currentThreadId, text);
|
this.updateThread(this.currentThreadId, text);
|
||||||
@ -430,6 +468,11 @@ export const useAgentStore = defineStore('agent', {
|
|||||||
let buffer = '';
|
let buffer = '';
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
// 检查是否被取消
|
||||||
|
if (this.streamAbortController && this.streamAbortController.signal.aborted) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
const { done, value } = await reader.read();
|
const { done, value } = await reader.read();
|
||||||
if (done) break;
|
if (done) break;
|
||||||
|
|
||||||
@ -438,7 +481,7 @@ export const useAgentStore = defineStore('agent', {
|
|||||||
buffer = lines.pop() || '';
|
buffer = lines.pop() || '';
|
||||||
|
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
if (line.trim()) {
|
if (line.trim() && (!this.streamAbortController || !this.streamAbortController.signal.aborted)) {
|
||||||
try {
|
try {
|
||||||
const chunk = JSON.parse(line.trim());
|
const chunk = JSON.parse(line.trim());
|
||||||
this._processStreamChunk(chunk);
|
this._processStreamChunk(chunk);
|
||||||
@ -448,8 +491,9 @@ export const useAgentStore = defineStore('agent', {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process any remaining data in the buffer
|
// Process any remaining data in the buffer
|
||||||
if (buffer.trim()) {
|
if (buffer.trim() && (!this.streamAbortController || !this.streamAbortController.signal.aborted)) {
|
||||||
try {
|
try {
|
||||||
const chunk = JSON.parse(buffer.trim());
|
const chunk = JSON.parse(buffer.trim());
|
||||||
this._processStreamChunk(chunk);
|
this._processStreamChunk(chunk);
|
||||||
@ -459,9 +503,15 @@ export const useAgentStore = defineStore('agent', {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
// 如果是取消错误,不显示错误信息
|
||||||
|
if (error.name === 'AbortError') {
|
||||||
|
console.log('Stream was cancelled');
|
||||||
|
} else {
|
||||||
handleChatError(error, 'send');
|
handleChatError(error, 'send');
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
this.isStreaming = false;
|
this.isStreaming = false;
|
||||||
|
this.streamAbortController = null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -487,6 +537,12 @@ export const useAgentStore = defineStore('agent', {
|
|||||||
|
|
||||||
// 重置store状态
|
// 重置store状态
|
||||||
reset() {
|
reset() {
|
||||||
|
// 取消正在进行的流式对话
|
||||||
|
if (this.streamAbortController) {
|
||||||
|
this.streamAbortController.abort();
|
||||||
|
this.streamAbortController = null;
|
||||||
|
}
|
||||||
|
|
||||||
this.agents = {};
|
this.agents = {};
|
||||||
this.selectedAgentId = null;
|
this.selectedAgentId = null;
|
||||||
this.defaultAgentId = null;
|
this.defaultAgentId = null;
|
||||||
@ -496,6 +552,8 @@ export const useAgentStore = defineStore('agent', {
|
|||||||
this.threads = {};
|
this.threads = {};
|
||||||
this.currentThreadId = null;
|
this.currentThreadId = null;
|
||||||
this.threadMessages = {};
|
this.threadMessages = {};
|
||||||
|
this.onGoingConv = { msgChunks: {} };
|
||||||
|
this.isStreaming = false;
|
||||||
this.isLoadingAgents = false;
|
this.isLoadingAgents = false;
|
||||||
this.isLoadingConfig = false;
|
this.isLoadingConfig = false;
|
||||||
this.isLoadingTools = false;
|
this.isLoadingTools = false;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user