2025-08-25 01:46:31 +08:00
|
|
|
|
import { defineStore } from 'pinia';
|
2025-08-31 00:34:26 +08:00
|
|
|
|
import { agentApi, threadApi } from '@/apis/agent_api';
|
2025-08-25 13:57:47 +08:00
|
|
|
|
import { MessageProcessor } from '@/utils/messageProcessor';
|
|
|
|
|
|
import { handleChatError } from '@/utils/errorHandler';
|
2025-08-25 01:46:31 +08:00
|
|
|
|
|
|
|
|
|
|
export const useAgentStore = defineStore('agent', {
|
|
|
|
|
|
state: () => ({
|
|
|
|
|
|
// 智能体相关状态
|
|
|
|
|
|
agents: {}, // 以ID为键的智能体对象
|
|
|
|
|
|
selectedAgentId: null, // 当前选中的智能体ID
|
|
|
|
|
|
defaultAgentId: null, // 默认智能体ID
|
|
|
|
|
|
|
|
|
|
|
|
// 智能体配置相关状态
|
|
|
|
|
|
agentConfig: {}, // 当前智能体的配置
|
|
|
|
|
|
originalAgentConfig: {}, // 原始配置,用于重置
|
|
|
|
|
|
|
|
|
|
|
|
// 工具相关状态
|
|
|
|
|
|
availableTools: [], // 所有可用工具列表
|
|
|
|
|
|
|
|
|
|
|
|
// 线程相关状态
|
|
|
|
|
|
threads: {}, // 以智能体ID为键的线程列表
|
|
|
|
|
|
currentThreadId: null, // 当前选中的线程ID
|
|
|
|
|
|
threadMessages: {}, // 以线程ID为键的消息列表
|
|
|
|
|
|
|
2025-08-25 13:57:47 +08:00
|
|
|
|
// 对话状态
|
|
|
|
|
|
onGoingConv: { msgChunks: {} }, // 正在进行的对话(流式)
|
|
|
|
|
|
isStreaming: false, // 是否正在接收流式响应
|
2025-08-31 00:57:59 +08:00
|
|
|
|
streamAbortController: null, // 流式对话取消控制器
|
2025-08-25 13:57:47 +08:00
|
|
|
|
|
2025-08-25 01:46:31 +08:00
|
|
|
|
// 加载状态
|
|
|
|
|
|
isLoadingAgents: false,
|
|
|
|
|
|
isLoadingConfig: false,
|
|
|
|
|
|
isLoadingTools: false,
|
|
|
|
|
|
isLoadingThreads: false,
|
|
|
|
|
|
isLoadingMessages: false,
|
|
|
|
|
|
|
|
|
|
|
|
// 错误状态
|
|
|
|
|
|
error: null,
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化状态
|
|
|
|
|
|
isInitialized: false
|
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
|
|
|
|
getters: {
|
2025-08-25 13:57:47 +08:00
|
|
|
|
// --- 智能体相关 Getters ---
|
|
|
|
|
|
selectedAgent: (state) => state.selectedAgentId ? state.agents[state.selectedAgentId] : null,
|
|
|
|
|
|
defaultAgent: (state) => state.defaultAgentId ? state.agents[state.defaultAgentId] : state.agents[Object.keys(state.agents)[0]],
|
|
|
|
|
|
agentsList: (state) => Object.values(state.agents),
|
|
|
|
|
|
isDefaultAgent: (state) => state.selectedAgentId === state.defaultAgentId,
|
|
|
|
|
|
configurableItems: (state) => {
|
2025-08-31 00:34:26 +08:00
|
|
|
|
const agent = state.selectedAgentId ? state.agents[state.selectedAgentId] : null;
|
2025-08-31 00:57:59 +08:00
|
|
|
|
if (!agent || !agent.configurable_items) return {};
|
|
|
|
|
|
|
|
|
|
|
|
const agentConfigurableItems = agent.configurable_items;
|
2025-08-31 00:34:26 +08:00
|
|
|
|
const items = { ...agentConfigurableItems };
|
2025-08-25 01:46:31 +08:00
|
|
|
|
Object.keys(items).forEach(key => {
|
|
|
|
|
|
const item = items[key];
|
|
|
|
|
|
if (item && item.x_oap_ui_config) {
|
2025-08-25 13:57:47 +08:00
|
|
|
|
items[key] = { ...item, ...item.x_oap_ui_config };
|
2025-08-25 01:46:31 +08:00
|
|
|
|
delete items[key].x_oap_ui_config;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
return items;
|
|
|
|
|
|
},
|
2025-08-25 13:57:47 +08:00
|
|
|
|
hasConfigChanges: (state) => JSON.stringify(state.agentConfig) !== JSON.stringify(state.originalAgentConfig),
|
|
|
|
|
|
|
|
|
|
|
|
// --- 线程与消息相关 Getters ---
|
|
|
|
|
|
currentAgentThreads: (state) => state.selectedAgentId ? (state.threads[state.selectedAgentId] || []) : [],
|
|
|
|
|
|
currentThread: (state) => {
|
|
|
|
|
|
if (!state.currentThreadId || !state.selectedAgentId) return null;
|
|
|
|
|
|
const agentThreads = state.threads[state.selectedAgentId] || [];
|
|
|
|
|
|
return agentThreads.find(thread => thread.id === state.currentThreadId);
|
2025-08-25 01:46:31 +08:00
|
|
|
|
},
|
2025-08-25 13:57:47 +08:00
|
|
|
|
currentThreadMessages: (state) => state.currentThreadId ? (state.threadMessages[state.currentThreadId] || []) : [],
|
|
|
|
|
|
|
|
|
|
|
|
// --- 对话UI Getters ---
|
|
|
|
|
|
onGoingConvMessages: (state) => {
|
|
|
|
|
|
const msgs = Object.values(state.onGoingConv.msgChunks).map(MessageProcessor.mergeMessageChunk);
|
|
|
|
|
|
return msgs.length > 0
|
|
|
|
|
|
? MessageProcessor.convertToolResultToMessages(msgs).filter(msg => msg.type !== 'tool')
|
|
|
|
|
|
: [];
|
2025-08-25 01:46:31 +08:00
|
|
|
|
},
|
2025-08-25 13:57:47 +08:00
|
|
|
|
conversations: (state) => {
|
|
|
|
|
|
const historyConvs = MessageProcessor.convertServerHistoryToMessages(state.currentThreadMessages);
|
2025-08-31 00:57:59 +08:00
|
|
|
|
// Compute ongoing messages directly from state to avoid circular reference
|
|
|
|
|
|
const msgs = Object.values(state.onGoingConv.msgChunks).map(MessageProcessor.mergeMessageChunk);
|
|
|
|
|
|
const onGoingMessages = msgs.length > 0
|
|
|
|
|
|
? MessageProcessor.convertToolResultToMessages(msgs).filter(msg => msg.type !== 'tool')
|
|
|
|
|
|
: [];
|
2025-08-25 13:57:47 +08:00
|
|
|
|
|
|
|
|
|
|
if (onGoingMessages.length > 0) {
|
|
|
|
|
|
// Create a new conversation object for the ongoing messages
|
|
|
|
|
|
const onGoingConv = {
|
|
|
|
|
|
messages: onGoingMessages,
|
|
|
|
|
|
status: 'streaming'
|
|
|
|
|
|
};
|
|
|
|
|
|
return [...historyConvs, onGoingConv];
|
|
|
|
|
|
}
|
|
|
|
|
|
return historyConvs;
|
2025-08-25 01:46:31 +08:00
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
actions: {
|
|
|
|
|
|
// 初始化store
|
|
|
|
|
|
async initialize() {
|
|
|
|
|
|
if (this.isInitialized) return;
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
2025-08-31 00:57:59 +08:00
|
|
|
|
// 首先加载智能体列表
|
|
|
|
|
|
await this.fetchAgents();
|
|
|
|
|
|
|
|
|
|
|
|
// 然后设置默认智能体
|
|
|
|
|
|
await this.fetchDefaultAgent();
|
|
|
|
|
|
|
|
|
|
|
|
// 最后加载工具
|
|
|
|
|
|
await this.fetchTools();
|
|
|
|
|
|
|
2025-08-25 01:46:31 +08:00
|
|
|
|
this.isInitialized = true;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Failed to initialize agent store:', error);
|
2025-08-31 00:57:59 +08:00
|
|
|
|
handleChatError(error, 'initialize');
|
2025-08-25 01:46:31 +08:00
|
|
|
|
this.error = error.message;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 获取智能体列表
|
|
|
|
|
|
async fetchAgents() {
|
|
|
|
|
|
this.isLoadingAgents = true;
|
|
|
|
|
|
this.error = null;
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await agentApi.getAgents();
|
|
|
|
|
|
// 将数组转换为以ID为键的对象
|
|
|
|
|
|
this.agents = response.agents.reduce((acc, agent) => {
|
|
|
|
|
|
acc[agent.id] = agent;
|
|
|
|
|
|
return acc;
|
|
|
|
|
|
}, {});
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Failed to fetch agents:', error);
|
2025-08-31 00:57:59 +08:00
|
|
|
|
handleChatError(error, 'fetch');
|
2025-08-25 01:46:31 +08:00
|
|
|
|
this.error = error.message;
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
this.isLoadingAgents = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 获取默认智能体
|
|
|
|
|
|
async fetchDefaultAgent() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await agentApi.getDefaultAgent();
|
|
|
|
|
|
this.defaultAgentId = response.default_agent_id;
|
|
|
|
|
|
|
2025-08-31 00:57:59 +08:00
|
|
|
|
// 如果没有选中的智能体且默认智能体存在于智能体列表中,则选择默认智能体
|
|
|
|
|
|
if (!this.selectedAgentId && this.defaultAgentId && this.agents[this.defaultAgentId]) {
|
2025-08-25 01:46:31 +08:00
|
|
|
|
this.selectedAgentId = this.defaultAgentId;
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Failed to fetch default agent:', error);
|
2025-08-31 00:57:59 +08:00
|
|
|
|
handleChatError(error, 'fetch');
|
2025-08-25 01:46:31 +08:00
|
|
|
|
this.error = error.message;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 设置默认智能体
|
|
|
|
|
|
async setDefaultAgent(agentId) {
|
|
|
|
|
|
try {
|
2025-08-31 00:34:26 +08:00
|
|
|
|
await agentApi.setDefaultAgent(agentId);
|
2025-08-25 01:46:31 +08:00
|
|
|
|
this.defaultAgentId = agentId;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Failed to set default agent:', error);
|
2025-08-31 00:57:59 +08:00
|
|
|
|
handleChatError(error, 'save');
|
2025-08-25 01:46:31 +08:00
|
|
|
|
this.error = error.message;
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 选择智能体
|
|
|
|
|
|
selectAgent(agentId) {
|
|
|
|
|
|
if (this.agents[agentId]) {
|
|
|
|
|
|
this.selectedAgentId = agentId;
|
|
|
|
|
|
// 清空之前的配置
|
|
|
|
|
|
this.agentConfig = {};
|
|
|
|
|
|
this.originalAgentConfig = {};
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 加载智能体配置
|
|
|
|
|
|
async loadAgentConfig(agentId = null) {
|
|
|
|
|
|
const targetAgentId = agentId || this.selectedAgentId;
|
|
|
|
|
|
if (!targetAgentId) return;
|
|
|
|
|
|
|
|
|
|
|
|
this.isLoadingConfig = true;
|
|
|
|
|
|
this.error = null;
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await agentApi.getAgentConfig(targetAgentId);
|
|
|
|
|
|
this.agentConfig = { ...response.config };
|
|
|
|
|
|
this.originalAgentConfig = { ...response.config };
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Failed to load agent config:', error);
|
2025-08-31 00:57:59 +08:00
|
|
|
|
handleChatError(error, 'load');
|
2025-08-25 01:46:31 +08:00
|
|
|
|
this.error = error.message;
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
this.isLoadingConfig = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 保存智能体配置
|
|
|
|
|
|
async saveAgentConfig(agentId = null) {
|
|
|
|
|
|
const targetAgentId = agentId || this.selectedAgentId;
|
|
|
|
|
|
if (!targetAgentId) return;
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
await agentApi.saveAgentConfig(targetAgentId, this.agentConfig);
|
|
|
|
|
|
this.originalAgentConfig = { ...this.agentConfig };
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Failed to save agent config:', error);
|
2025-08-31 00:57:59 +08:00
|
|
|
|
handleChatError(error, 'save');
|
2025-08-25 01:46:31 +08:00
|
|
|
|
this.error = error.message;
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 重置智能体配置
|
|
|
|
|
|
resetAgentConfig() {
|
|
|
|
|
|
this.agentConfig = { ...this.originalAgentConfig };
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 更新配置项
|
|
|
|
|
|
updateConfigItem(key, value) {
|
|
|
|
|
|
this.agentConfig[key] = value;
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 更新智能体配置(支持批量更新)
|
|
|
|
|
|
updateAgentConfig(updates) {
|
|
|
|
|
|
Object.assign(this.agentConfig, updates);
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 获取工具列表
|
|
|
|
|
|
async fetchTools() {
|
|
|
|
|
|
this.isLoadingTools = true;
|
|
|
|
|
|
this.error = null;
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await agentApi.getTools();
|
|
|
|
|
|
this.availableTools = response.tools;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Failed to fetch tools:', error);
|
2025-08-31 00:57:59 +08:00
|
|
|
|
handleChatError(error, 'fetch');
|
2025-08-25 01:46:31 +08:00
|
|
|
|
this.error = error.message;
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
this.isLoadingTools = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 清除错误状态
|
|
|
|
|
|
clearError() {
|
|
|
|
|
|
this.error = null;
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== 线程管理方法 ====================
|
|
|
|
|
|
|
|
|
|
|
|
// 获取智能体的线程列表
|
|
|
|
|
|
async fetchThreads(agentId = null) {
|
|
|
|
|
|
const targetAgentId = agentId || this.selectedAgentId;
|
|
|
|
|
|
if (!targetAgentId) return;
|
|
|
|
|
|
|
|
|
|
|
|
this.isLoadingThreads = true;
|
|
|
|
|
|
this.error = null;
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const threads = await threadApi.getThreads(targetAgentId);
|
|
|
|
|
|
this.threads[targetAgentId] = threads || [];
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Failed to fetch threads:', error);
|
2025-08-31 00:57:59 +08:00
|
|
|
|
handleChatError(error, 'fetch');
|
2025-08-25 01:46:31 +08:00
|
|
|
|
this.error = error.message;
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
this.isLoadingThreads = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 创建新线程
|
|
|
|
|
|
async createThread(agentId, title = '新的对话') {
|
|
|
|
|
|
if (!agentId) return null;
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const thread = await threadApi.createThread(agentId, title);
|
|
|
|
|
|
if (thread) {
|
|
|
|
|
|
// 更新线程列表
|
|
|
|
|
|
if (!this.threads[agentId]) {
|
|
|
|
|
|
this.threads[agentId] = [];
|
|
|
|
|
|
}
|
|
|
|
|
|
this.threads[agentId].unshift(thread);
|
|
|
|
|
|
|
|
|
|
|
|
// 设置为当前线程
|
|
|
|
|
|
this.currentThreadId = thread.id;
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化消息列表
|
|
|
|
|
|
this.threadMessages[thread.id] = [];
|
|
|
|
|
|
}
|
|
|
|
|
|
return thread;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Failed to create thread:', error);
|
2025-08-31 00:57:59 +08:00
|
|
|
|
handleChatError(error, 'create');
|
2025-08-25 01:46:31 +08:00
|
|
|
|
this.error = error.message;
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 删除线程
|
|
|
|
|
|
async deleteThread(threadId) {
|
|
|
|
|
|
if (!threadId) return;
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
await threadApi.deleteThread(threadId);
|
|
|
|
|
|
|
|
|
|
|
|
// 从所有智能体的线程列表中移除
|
|
|
|
|
|
Object.keys(this.threads).forEach(agentId => {
|
|
|
|
|
|
this.threads[agentId] = this.threads[agentId].filter(thread => thread.id !== threadId);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 清理消息
|
|
|
|
|
|
delete this.threadMessages[threadId];
|
|
|
|
|
|
|
|
|
|
|
|
// 如果删除的是当前线程,重置当前线程ID
|
|
|
|
|
|
if (this.currentThreadId === threadId) {
|
|
|
|
|
|
this.currentThreadId = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Failed to delete thread:', error);
|
2025-08-31 00:57:59 +08:00
|
|
|
|
handleChatError(error, 'delete');
|
2025-08-25 01:46:31 +08:00
|
|
|
|
this.error = error.message;
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 更新线程标题
|
|
|
|
|
|
async updateThread(threadId, title) {
|
|
|
|
|
|
if (!threadId || !title) return;
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
await threadApi.updateThread(threadId, title);
|
|
|
|
|
|
|
|
|
|
|
|
// 更新本地线程列表中的标题
|
|
|
|
|
|
Object.keys(this.threads).forEach(agentId => {
|
|
|
|
|
|
const thread = this.threads[agentId].find(t => t.id === threadId);
|
|
|
|
|
|
if (thread) {
|
|
|
|
|
|
thread.title = title;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Failed to update thread:', error);
|
2025-08-31 00:57:59 +08:00
|
|
|
|
handleChatError(error, 'update');
|
2025-08-25 01:46:31 +08:00
|
|
|
|
this.error = error.message;
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 选择线程
|
|
|
|
|
|
selectThread(threadId) {
|
|
|
|
|
|
this.currentThreadId = threadId;
|
2025-08-25 13:57:47 +08:00
|
|
|
|
this.resetOnGoingConv();
|
2025-08-25 01:46:31 +08:00
|
|
|
|
|
|
|
|
|
|
// 如果没有该线程的消息,初始化空数组
|
|
|
|
|
|
if (threadId && !this.threadMessages[threadId]) {
|
|
|
|
|
|
this.threadMessages[threadId] = [];
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 获取线程消息
|
|
|
|
|
|
async fetchThreadMessages(threadId) {
|
|
|
|
|
|
if (!threadId) return;
|
|
|
|
|
|
|
|
|
|
|
|
this.isLoadingMessages = true;
|
|
|
|
|
|
this.error = null;
|
2025-08-25 13:57:47 +08:00
|
|
|
|
this.resetOnGoingConv();
|
2025-08-25 01:46:31 +08:00
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await agentApi.getAgentHistory(this.selectedAgentId, threadId);
|
|
|
|
|
|
this.threadMessages[threadId] = response.history || [];
|
|
|
|
|
|
} catch (error) {
|
2025-08-25 13:57:47 +08:00
|
|
|
|
handleChatError(error, 'load');
|
2025-08-25 01:46:31 +08:00
|
|
|
|
throw error;
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
this.isLoadingMessages = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2025-08-25 13:57:47 +08:00
|
|
|
|
// --- 流式对话 Actions ---
|
|
|
|
|
|
|
|
|
|
|
|
resetOnGoingConv() {
|
2025-08-31 00:57:59 +08:00
|
|
|
|
// 取消之前的请求(如果存在)
|
|
|
|
|
|
if (this.streamAbortController) {
|
|
|
|
|
|
this.streamAbortController.abort();
|
|
|
|
|
|
this.streamAbortController = null;
|
|
|
|
|
|
}
|
2025-08-25 13:57:47 +08:00
|
|
|
|
this.onGoingConv = { msgChunks: {} };
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
_processStreamChunk(chunk) {
|
|
|
|
|
|
const { status, msg, request_id, message } = chunk;
|
|
|
|
|
|
switch (status) {
|
|
|
|
|
|
case 'init':
|
|
|
|
|
|
this.onGoingConv.msgChunks[request_id] = [msg];
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'loading':
|
|
|
|
|
|
if (msg.id) {
|
|
|
|
|
|
if (!this.onGoingConv.msgChunks[msg.id]) {
|
|
|
|
|
|
this.onGoingConv.msgChunks[msg.id] = [];
|
|
|
|
|
|
}
|
|
|
|
|
|
this.onGoingConv.msgChunks[msg.id].push(msg);
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'error':
|
|
|
|
|
|
handleChatError({ message }, 'stream');
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'finished':
|
|
|
|
|
|
this.fetchThreadMessages(this.currentThreadId);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2025-08-31 00:57:59 +08:00
|
|
|
|
// 取消流式对话
|
|
|
|
|
|
cancelStreaming() {
|
|
|
|
|
|
if (this.streamAbortController && this.isStreaming) {
|
|
|
|
|
|
this.streamAbortController.abort();
|
|
|
|
|
|
this.streamAbortController = null;
|
|
|
|
|
|
this.isStreaming = false;
|
|
|
|
|
|
this.resetOnGoingConv();
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2025-08-25 13:57:47 +08:00
|
|
|
|
// 发送消息并处理流式响应
|
|
|
|
|
|
async sendMessage(text) {
|
|
|
|
|
|
if (!this.selectedAgentId || !this.currentThreadId || !text) {
|
|
|
|
|
|
handleChatError({ message: "Missing agent, thread, or message text" }, 'send');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.isStreaming = true;
|
|
|
|
|
|
this.resetOnGoingConv();
|
2025-08-31 00:57:59 +08:00
|
|
|
|
|
|
|
|
|
|
// 创建新的 AbortController
|
|
|
|
|
|
this.streamAbortController = new AbortController();
|
2025-08-25 13:57:47 +08:00
|
|
|
|
|
|
|
|
|
|
// 如果是新对话,用消息内容作为标题
|
|
|
|
|
|
if (this.currentThreadMessages.length === 0) {
|
|
|
|
|
|
this.updateThread(this.currentThreadId, text);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const requestData = {
|
|
|
|
|
|
query: text,
|
|
|
|
|
|
config: {
|
|
|
|
|
|
thread_id: this.currentThreadId,
|
|
|
|
|
|
},
|
|
|
|
|
|
};
|
2025-08-25 01:46:31 +08:00
|
|
|
|
|
|
|
|
|
|
try {
|
2025-08-25 13:57:47 +08:00
|
|
|
|
const response = await agentApi.sendAgentMessage(this.selectedAgentId, requestData);
|
2025-08-31 00:34:26 +08:00
|
|
|
|
|
2025-08-25 13:57:47 +08:00
|
|
|
|
const reader = response.body.getReader();
|
|
|
|
|
|
const decoder = new TextDecoder();
|
|
|
|
|
|
let buffer = '';
|
|
|
|
|
|
|
|
|
|
|
|
while (true) {
|
2025-08-31 00:57:59 +08:00
|
|
|
|
// 检查是否被取消
|
|
|
|
|
|
if (this.streamAbortController && this.streamAbortController.signal.aborted) {
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-25 13:57:47 +08:00
|
|
|
|
const { done, value } = await reader.read();
|
|
|
|
|
|
if (done) break;
|
|
|
|
|
|
|
|
|
|
|
|
buffer += decoder.decode(value, { stream: true });
|
|
|
|
|
|
const lines = buffer.split('\n');
|
|
|
|
|
|
buffer = lines.pop() || '';
|
|
|
|
|
|
|
|
|
|
|
|
for (const line of lines) {
|
2025-08-31 00:57:59 +08:00
|
|
|
|
if (line.trim() && (!this.streamAbortController || !this.streamAbortController.signal.aborted)) {
|
2025-08-25 13:57:47 +08:00
|
|
|
|
try {
|
|
|
|
|
|
const chunk = JSON.parse(line.trim());
|
|
|
|
|
|
this._processStreamChunk(chunk);
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.warn('Failed to parse stream chunk JSON:', e);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-31 00:57:59 +08:00
|
|
|
|
|
2025-08-25 13:57:47 +08:00
|
|
|
|
// Process any remaining data in the buffer
|
2025-08-31 00:57:59 +08:00
|
|
|
|
if (buffer.trim() && (!this.streamAbortController || !this.streamAbortController.signal.aborted)) {
|
2025-08-25 13:57:47 +08:00
|
|
|
|
try {
|
|
|
|
|
|
const chunk = JSON.parse(buffer.trim());
|
|
|
|
|
|
this._processStreamChunk(chunk);
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.warn('Failed to parse final stream chunk JSON:', e);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-25 01:46:31 +08:00
|
|
|
|
} catch (error) {
|
2025-08-31 00:57:59 +08:00
|
|
|
|
// 如果是取消错误,不显示错误信息
|
|
|
|
|
|
if (error.name === 'AbortError') {
|
|
|
|
|
|
console.log('Stream was cancelled');
|
|
|
|
|
|
} else {
|
|
|
|
|
|
handleChatError(error, 'send');
|
|
|
|
|
|
}
|
2025-08-25 13:57:47 +08:00
|
|
|
|
} finally {
|
|
|
|
|
|
this.isStreaming = false;
|
2025-08-31 00:57:59 +08:00
|
|
|
|
this.streamAbortController = null;
|
2025-08-25 01:46:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 添加消息到线程
|
|
|
|
|
|
addMessageToThread(threadId, message) {
|
|
|
|
|
|
if (!threadId || !message) return;
|
|
|
|
|
|
|
|
|
|
|
|
if (!this.threadMessages[threadId]) {
|
|
|
|
|
|
this.threadMessages[threadId] = [];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.threadMessages[threadId].push(message);
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 更新线程中的消息
|
|
|
|
|
|
updateMessageInThread(threadId, messageIndex, updatedMessage) {
|
|
|
|
|
|
if (!threadId || messageIndex < 0 || !this.threadMessages[threadId]) return;
|
|
|
|
|
|
|
|
|
|
|
|
if (messageIndex < this.threadMessages[threadId].length) {
|
|
|
|
|
|
this.threadMessages[threadId][messageIndex] = updatedMessage;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 重置store状态
|
|
|
|
|
|
reset() {
|
2025-08-31 00:57:59 +08:00
|
|
|
|
// 取消正在进行的流式对话
|
|
|
|
|
|
if (this.streamAbortController) {
|
|
|
|
|
|
this.streamAbortController.abort();
|
|
|
|
|
|
this.streamAbortController = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-25 01:46:31 +08:00
|
|
|
|
this.agents = {};
|
|
|
|
|
|
this.selectedAgentId = null;
|
|
|
|
|
|
this.defaultAgentId = null;
|
|
|
|
|
|
this.agentConfig = {};
|
|
|
|
|
|
this.originalAgentConfig = {};
|
|
|
|
|
|
this.availableTools = [];
|
|
|
|
|
|
this.threads = {};
|
|
|
|
|
|
this.currentThreadId = null;
|
|
|
|
|
|
this.threadMessages = {};
|
2025-08-31 00:57:59 +08:00
|
|
|
|
this.onGoingConv = { msgChunks: {} };
|
|
|
|
|
|
this.isStreaming = false;
|
2025-08-25 01:46:31 +08:00
|
|
|
|
this.isLoadingAgents = false;
|
|
|
|
|
|
this.isLoadingConfig = false;
|
|
|
|
|
|
this.isLoadingTools = false;
|
|
|
|
|
|
this.isLoadingThreads = false;
|
|
|
|
|
|
this.isLoadingMessages = false;
|
|
|
|
|
|
this.error = null;
|
|
|
|
|
|
this.isInitialized = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 持久化配置
|
|
|
|
|
|
persist: {
|
|
|
|
|
|
key: 'agent-store',
|
|
|
|
|
|
storage: localStorage,
|
|
|
|
|
|
paths: ['selectedAgentId', 'defaultAgentId'] // 只持久化关键状态
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|