feat: 支持删除指定会话以及部分会话管理优化 (#2895)

* feat: add toast notification system with snackbar component

* feat: add session deletion functionality

* feat: support batch operations for updating session persona, provider, LLM, and TTS statuses

fix: #2263

* feat: 修复对话状态关闭,删除对话管理库会导致对话无法恢复

fixes: #2309
This commit is contained in:
Soulter
2025-09-27 20:36:30 +08:00
committed by GitHub
parent a69195a02b
commit c2a34475f1
10 changed files with 563 additions and 227 deletions
+12 -4
View File
@@ -87,17 +87,25 @@ class ConversationManager:
unified_msg_origin (str): 统一的消息来源字符串。格式为 platform_name:message_type:session_id
conversation_id (str): 对话 ID, 是 uuid 格式的字符串
"""
f = False
if not conversation_id:
conversation_id = self.session_conversations.get(unified_msg_origin)
if conversation_id:
f = True
if conversation_id:
await self.db.delete_conversation(cid=conversation_id)
if f:
curr_cid = await self.get_curr_conversation_id(unified_msg_origin)
if curr_cid == conversation_id:
self.session_conversations.pop(unified_msg_origin, None)
await sp.session_remove(unified_msg_origin, "sel_conv_id")
async def delete_conversations_by_user_id(self, unified_msg_origin: str):
"""删除会话的所有对话
Args:
unified_msg_origin (str): 统一的消息来源字符串。格式为 platform_name:message_type:session_id
"""
await self.db.delete_conversations_by_user_id(user_id=unified_msg_origin)
self.session_conversations.pop(unified_msg_origin, None)
await sp.session_remove(unified_msg_origin, "sel_conv_id")
async def get_curr_conversation_id(self, unified_msg_origin: str) -> str | None:
"""获取会话当前的对话 ID
+5
View File
@@ -154,6 +154,11 @@ class BaseDatabase(abc.ABC):
"""Delete a conversation by its ID."""
...
@abc.abstractmethod
async def delete_conversations_by_user_id(self, user_id: str) -> None:
"""Delete all conversations for a specific user."""
...
@abc.abstractmethod
async def insert_platform_message_history(
self,
+8
View File
@@ -249,6 +249,14 @@ class SQLiteDatabase(BaseDatabase):
delete(ConversationV2).where(ConversationV2.conversation_id == cid)
)
async def delete_conversations_by_user_id(self, user_id: str) -> None:
async with self.get_db() as session:
session: AsyncSession
async with session.begin():
await session.execute(
delete(ConversationV2).where(ConversationV2.user_id == user_id)
)
async def insert_platform_message_history(
self,
platform_id,
@@ -11,7 +11,8 @@ class SessionStatusCheckStage(Stage):
"""检查会话是否整体启用"""
async def initialize(self, ctx: PipelineContext) -> None:
pass
self.ctx = ctx
self.conv_mgr = ctx.plugin_manager.context.conversation_manager
async def process(
self, event: AstrMessageEvent
@@ -19,4 +20,14 @@ class SessionStatusCheckStage(Stage):
# 检查会话是否整体启用
if not SessionServiceManager.is_session_enabled(event.unified_msg_origin):
logger.debug(f"会话 {event.unified_msg_origin} 已被关闭,已终止事件传播。")
# workaround for #2309
conv_id = await self.conv_mgr.get_curr_conversation_id(
event.unified_msg_origin
)
if not conv_id:
await self.conv_mgr.new_conversation(
event.unified_msg_origin, platform_id=event.get_platform_id()
)
event.stop_event()
-4
View File
@@ -52,10 +52,6 @@ class SessionServiceManager:
"session_service_config", session_config, scope="umo", scope_id=session_id
)
logger.info(
f"会话 {session_id} 的LLM状态已更新为: {'启用' if enabled else '禁用'}"
)
@staticmethod
def should_process_llm_request(event: AstrMessageEvent) -> bool:
"""检查是否应该处理LLM请求
+235 -78
View File
@@ -30,6 +30,7 @@ class SessionManagementRoute(Route):
"/session/update_tts": ("POST", self.update_session_tts),
"/session/update_name": ("POST", self.update_session_name),
"/session/update_status": ("POST", self.update_session_status),
"/session/delete": ("POST", self.delete_session),
}
self.conv_mgr = core_lifecycle.conversation_manager
self.core_lifecycle = core_lifecycle
@@ -180,60 +181,132 @@ class SessionManagementRoute(Route):
logger.error(error_msg)
return Response().error(f"获取会话列表失败: {str(e)}").__dict__
async def _update_single_session_persona(self, session_id: str, persona_name: str):
"""更新单个会话的 persona 的内部方法"""
conversation_manager = self.core_lifecycle.star_context.conversation_manager
conversation_id = await conversation_manager.get_curr_conversation_id(
session_id
)
conv = None
if conversation_id:
conv = await conversation_manager.get_conversation(
unified_msg_origin=session_id,
conversation_id=conversation_id,
)
if not conv or not conversation_id:
conversation_id = await conversation_manager.new_conversation(session_id)
# 更新 persona
await conversation_manager.update_conversation_persona_id(
session_id, persona_name
)
async def _handle_batch_operation(
self, session_ids: list, operation_func, operation_name: str, **kwargs
):
"""通用的批量操作处理方法"""
success_count = 0
error_sessions = []
for session_id in session_ids:
try:
await operation_func(session_id, **kwargs)
success_count += 1
except Exception as e:
logger.error(f"批量{operation_name} 会话 {session_id} 失败: {str(e)}")
error_sessions.append(session_id)
if error_sessions:
return (
Response()
.ok(
{
"message": f"批量更新完成,成功: {success_count},失败: {len(error_sessions)}",
"success_count": success_count,
"error_count": len(error_sessions),
"error_sessions": error_sessions,
}
)
.__dict__
)
else:
return (
Response()
.ok(
{
"message": f"成功批量{operation_name} {success_count} 个会话",
"success_count": success_count,
}
)
.__dict__
)
async def update_session_persona(self):
"""更新指定会话的 persona"""
"""更新指定会话的 persona,支持批量操作"""
try:
data = await request.get_json()
session_id = data.get("session_id")
is_batch = data.get("is_batch", False)
persona_name = data.get("persona_name")
if not session_id:
return Response().error("缺少必要参数: session_id").__dict__
if persona_name is None:
return Response().error("缺少必要参数: persona_name").__dict__
# 获取会话当前的对话 ID
conversation_manager = self.core_lifecycle.star_context.conversation_manager
conversation_id = await conversation_manager.get_curr_conversation_id(
session_id
)
if is_batch:
session_ids = data.get("session_ids", [])
if not session_ids:
return Response().error("缺少必要参数: session_ids").__dict__
if not conversation_id:
# 如果没有对话,创建一个新的对话
conversation_id = await conversation_manager.new_conversation(
session_id
return await self._handle_batch_operation(
session_ids,
self._update_single_session_persona,
"更新人格",
persona_name=persona_name,
)
else:
session_id = data.get("session_id")
if not session_id:
return Response().error("缺少必要参数: session_id").__dict__
# 更新 persona
await conversation_manager.update_conversation_persona_id(
session_id, persona_name
)
return (
Response()
.ok({"message": f"成功更新会话 {session_id} 的人格为 {persona_name}"})
.__dict__
)
await self._update_single_session_persona(session_id, persona_name)
return (
Response()
.ok(
{
"message": f"成功更新会话 {session_id} 的人格为 {persona_name}"
}
)
.__dict__
)
except Exception as e:
error_msg = f"更新会话人格失败: {str(e)}\n{traceback.format_exc()}"
logger.error(error_msg)
return Response().error(f"更新会话人格失败: {str(e)}").__dict__
async def _update_single_session_provider(
self, session_id: str, provider_id: str, provider_type_enum
):
"""更新单个会话的 provider 的内部方法"""
provider_manager = self.core_lifecycle.star_context.provider_manager
await provider_manager.set_provider(
provider_id=provider_id,
provider_type=provider_type_enum,
umo=session_id,
)
async def update_session_provider(self):
"""更新指定会话的 provider"""
"""更新指定会话的 provider,支持批量操作"""
try:
data = await request.get_json()
session_id = data.get("session_id")
is_batch = data.get("is_batch", False)
provider_id = data.get("provider_id")
# "chat_completion", "speech_to_text", "text_to_speech"
provider_type = data.get("provider_type")
if not session_id or not provider_id or not provider_type:
if not provider_id or not provider_type:
return (
Response()
.error("缺少必要参数: session_id, provider_id, provider_type")
.error("缺少必要参数: provider_id, provider_type")
.__dict__
)
@@ -251,23 +324,35 @@ class SessionManagementRoute(Route):
.__dict__
)
# 设置 provider
provider_manager = self.core_lifecycle.star_context.provider_manager
await provider_manager.set_provider(
provider_id=provider_id,
provider_type=provider_type_enum,
umo=session_id,
)
if is_batch:
session_ids = data.get("session_ids", [])
if not session_ids:
return Response().error("缺少必要参数: session_ids").__dict__
return (
Response()
.ok(
{
"message": f"成功更新会话 {session_id}{provider_type} 提供商为 {provider_id}"
}
return await self._handle_batch_operation(
session_ids,
self._update_single_session_provider,
f"更新 {provider_type} 提供商",
provider_id=provider_id,
provider_type_enum=provider_type_enum,
)
else:
session_id = data.get("session_id")
if not session_id:
return Response().error("缺少必要参数: session_id").__dict__
await self._update_single_session_provider(
session_id, provider_id, provider_type_enum
)
return (
Response()
.ok(
{
"message": f"成功更新会话 {session_id}{provider_type} 提供商为 {provider_id}"
}
)
.__dict__
)
.__dict__
)
except Exception as e:
error_msg = f"更新会话提供商失败: {str(e)}\n{traceback.format_exc()}"
@@ -376,66 +461,98 @@ class SessionManagementRoute(Route):
logger.error(error_msg)
return Response().error(f"更新会话插件状态失败: {str(e)}").__dict__
async def _update_single_session_llm(self, session_id: str, enabled: bool):
"""更新单个会话的LLM状态的内部方法"""
SessionServiceManager.set_llm_status_for_session(session_id, enabled)
async def update_session_llm(self):
"""更新指定会话的LLM启停状态"""
"""更新指定会话的LLM启停状态,支持批量操作"""
try:
data = await request.get_json()
session_id = data.get("session_id")
is_batch = data.get("is_batch", False)
enabled = data.get("enabled")
if not session_id:
return Response().error("缺少必要参数: session_id").__dict__
if enabled is None:
return Response().error("缺少必要参数: enabled").__dict__
# 使用 SessionServiceManager 更新LLM状态
SessionServiceManager.set_llm_status_for_session(session_id, enabled)
if is_batch:
session_ids = data.get("session_ids", [])
if not session_ids:
return Response().error("缺少必要参数: session_ids").__dict__
return (
Response()
.ok(
{
"message": f"LLM已{'启用' if enabled else '禁用'}",
"session_id": session_id,
"llm_enabled": enabled,
}
result = await self._handle_batch_operation(
session_ids,
self._update_single_session_llm,
f"{'启用' if enabled else '禁用'}LLM",
enabled=enabled,
)
return result
else:
session_id = data.get("session_id")
if not session_id:
return Response().error("缺少必要参数: session_id").__dict__
await self._update_single_session_llm(session_id, enabled)
return (
Response()
.ok(
{
"message": f"LLM已{'启用' if enabled else '禁用'}",
"session_id": session_id,
"llm_enabled": enabled,
}
)
.__dict__
)
.__dict__
)
except Exception as e:
error_msg = f"更新会话LLM状态失败: {str(e)}\n{traceback.format_exc()}"
logger.error(error_msg)
return Response().error(f"更新会话LLM状态失败: {str(e)}").__dict__
async def _update_single_session_tts(self, session_id: str, enabled: bool):
"""更新单个会话的TTS状态的内部方法"""
SessionServiceManager.set_tts_status_for_session(session_id, enabled)
async def update_session_tts(self):
"""更新指定会话的TTS启停状态"""
"""更新指定会话的TTS启停状态,支持批量操作"""
try:
data = await request.get_json()
session_id = data.get("session_id")
is_batch = data.get("is_batch", False)
enabled = data.get("enabled")
if not session_id:
return Response().error("缺少必要参数: session_id").__dict__
if enabled is None:
return Response().error("缺少必要参数: enabled").__dict__
# 使用 SessionServiceManager 更新TTS状态
SessionServiceManager.set_tts_status_for_session(session_id, enabled)
if is_batch:
session_ids = data.get("session_ids", [])
if not session_ids:
return Response().error("缺少必要参数: session_ids").__dict__
return (
Response()
.ok(
{
"message": f"TTS已{'启用' if enabled else '禁用'}",
"session_id": session_id,
"tts_enabled": enabled,
}
result = await self._handle_batch_operation(
session_ids,
self._update_single_session_tts,
f"{'启用' if enabled else '禁用'}TTS",
enabled=enabled,
)
return result
else:
session_id = data.get("session_id")
if not session_id:
return Response().error("缺少必要参数: session_id").__dict__
await self._update_single_session_tts(session_id, enabled)
return (
Response()
.ok(
{
"message": f"TTS已{'启用' if enabled else '禁用'}",
"session_id": session_id,
"tts_enabled": enabled,
}
)
.__dict__
)
.__dict__
)
except Exception as e:
error_msg = f"更新会话TTS状态失败: {str(e)}\n{traceback.format_exc()}"
@@ -507,3 +624,43 @@ class SessionManagementRoute(Route):
error_msg = f"更新会话整体状态失败: {str(e)}\n{traceback.format_exc()}"
logger.error(error_msg)
return Response().error(f"更新会话整体状态失败: {str(e)}").__dict__
async def delete_session(self):
"""删除指定会话及其所有相关数据"""
try:
data = await request.get_json()
session_id = data.get("session_id")
if not session_id:
return Response().error("缺少必要参数: session_id").__dict__
# 删除会话的所有相关数据
conversation_manager = self.core_lifecycle.conversation_manager
# 1. 删除会话的所有对话
try:
await conversation_manager.delete_conversations_by_user_id(session_id)
except Exception as e:
logger.warning(f"删除会话 {session_id} 的对话失败: {str(e)}")
# 2. 清除会话的偏好设置数据(清空该会话的所有配置)
try:
await sp.clear_async("umo", session_id)
except Exception as e:
logger.warning(f"清除会话 {session_id} 的偏好设置失败: {str(e)}")
return (
Response()
.ok(
{
"message": f"会话 {session_id} 及其相关所有对话数据已成功删除",
"session_id": session_id,
}
)
.__dict__
)
except Exception as e:
error_msg = f"删除会话失败: {str(e)}\n{traceback.format_exc()}"
logger.error(error_msg)
return Response().error(f"删除会话失败: {str(e)}").__dict__
@@ -7,7 +7,8 @@
"apply": "Apply Batch Settings",
"editName": "Edit Session Name",
"save": "Save",
"cancel": "Cancel"
"cancel": "Cancel",
"delete": "Delete"
},
"sessions": {
"activeSessions": "Active Sessions",
@@ -29,7 +30,8 @@
"ttsProvider": "TTS Provider",
"llmStatus": "LLM Status",
"ttsStatus": "TTS Status",
"pluginManagement": "Plugin Management"
"pluginManagement": "Plugin Management",
"actions": "Actions"
}
},
"status": {
@@ -65,6 +67,10 @@
"fullSessionId": "Full Session ID",
"hint": "Custom names help you easily identify sessions. The small information icon (!) will show the actual UMO when hovering."
},
"deleteConfirm": {
"message": "Are you sure you want to delete session {sessionName}?",
"warning": "This action will permanently delete all chat history and preference settings for this session (except for data linked via plugins), and this cannot be undone. Continue?"
},
"messages": {
"refreshSuccess": "Session list refreshed",
"personaUpdateSuccess": "Persona updated successfully",
@@ -82,6 +88,8 @@
"pluginStatusSuccess": "Plugin {name} {status}",
"pluginStatusError": "Failed to update plugin status",
"nameUpdateSuccess": "Session name updated successfully",
"nameUpdateError": "Failed to update session name"
"nameUpdateError": "Failed to update session name",
"deleteSuccess": "Session deleted successfully",
"deleteError": "Failed to delete session"
}
}
@@ -7,7 +7,8 @@
"apply": "应用批量设置",
"editName": "备注",
"save": "保存",
"cancel": "取消"
"cancel": "取消",
"delete": "删除"
},
"sessions": {
"activeSessions": "活跃会话",
@@ -29,7 +30,8 @@
"ttsProvider": "语音合成模型",
"llmStatus": "启用 LLM",
"ttsStatus": "启用 TTS",
"pluginManagement": "插件管理"
"pluginManagement": "插件管理",
"actions": "操作"
}
},
"status": {
@@ -65,6 +67,10 @@
"fullSessionId": "完整会话ID",
"hint": "自定义名称帮助您轻松识别会话。当设置了自定义名称时,会显示一个小感叹号标识(!),鼠标悬停时会显示实际的UMO。"
},
"deleteConfirm": {
"message": "确定要删除会话 {sessionName} 吗?",
"warning": "此操作将永久删除本次会话的「全部对话记录」与「偏好设置」(插件对会话的关联数据除外),且无法恢复。确认继续?"
},
"messages": {
"refreshSuccess": "会话列表已刷新",
"personaUpdateSuccess": "人格更新成功",
@@ -82,6 +88,8 @@
"pluginStatusSuccess": "插件 {name} {status}",
"pluginStatusError": "插件状态更新失败",
"nameUpdateSuccess": "会话名称更新成功",
"nameUpdateError": "会话名称更新失败"
"nameUpdateError": "会话名称更新失败",
"deleteSuccess": "会话删除成功",
"deleteError": "会话删除失败"
}
}
+1 -5
View File
@@ -601,19 +601,15 @@ export default {
}
if (this.search) {
params.search = this.search;
params.search = this.search.trim();
}
// 添加排除条件
params.exclude_ids = 'astrbot';
params.exclude_platforms = 'webchat';
console.log(`正在请求对话列表: /api/conversation/list 参数:`, params);
const response = await axios.get('/api/conversation/list', { params });
console.log('收到对话列表响应:', response.data);
this.lastAppliedFilters = { ...this.currentFilters }; // 记录已应用的筛选条件
if (response.data.status === "ok") {
+268 -129
View File
@@ -141,6 +141,17 @@
</v-btn>
</template>
<!-- 操作按钮 -->
<template v-slot:item.actions="{ item }">
<v-btn size="x-small" variant="tonal" color="error" @click="deleteSession(item)"
:loading="item.deleting" icon>
<v-icon>mdi-delete</v-icon>
<v-tooltip activator="parent" location="top">
{{ tm('buttons.delete') }}
</v-tooltip>
</v-btn>
</template>
<!-- 空状态 -->
<template v-slot:no-data>
<div class="text-center py-8">
@@ -409,6 +420,7 @@ export default {
{ title: this.tm('table.headers.llmStatus'), key: 'llm_enabled', sortable: false, minWidth: '120px' },
{ title: this.tm('table.headers.ttsStatus'), key: 'tts_enabled', sortable: false, minWidth: '120px' },
{ title: this.tm('table.headers.pluginManagement'), key: 'plugins', sortable: false, minWidth: '120px' },
{ title: this.tm('table.headers.actions'), key: 'actions', sortable: false, minWidth: '100px' },
]
},
@@ -418,12 +430,13 @@ export default {
// 搜索筛选
if (this.searchQuery) {
const query = this.searchQuery.toLowerCase();
const query = this.searchQuery.toLowerCase().trim();
filtered = filtered.filter(session =>
session.session_name.toLowerCase().includes(query) ||
session.platform.toLowerCase().includes(query) ||
session.persona_name?.toLowerCase().includes(query) ||
session.chat_provider_name?.toLowerCase().includes(query)
session.chat_provider_name?.toLowerCase().includes(query) ||
session.session_id.toLowerCase().includes(query)
);
}
@@ -487,7 +500,8 @@ export default {
this.sessions = data.sessions.map(session => ({
...session,
updating: false, // 添加更新状态标志
loadingPlugins: false // 添加插件加载状态标志
loadingPlugins: false, // 添加插件加载状态标志
deleting: false // 添加删除状态标志
}));
this.availablePersonas = data.available_personas;
this.availableChatProviders = data.available_chat_providers;
@@ -508,60 +522,131 @@ export default {
},
async updatePersona(session, personaName) {
session.updating = true;
try {
const response = await axios.post('/api/session/update_persona', {
session_id: session.session_id,
persona_name: personaName
});
if (response.data.status === 'ok') {
session.persona_id = personaName;
session.persona_name = personaName === '[%None]' ? this.tm('persona.none') :
return this._updateSession('persona', session, { persona_name: personaName }, (s, success) => {
if (success) {
s.persona_id = personaName;
s.persona_name = personaName === '[%None]' ? this.tm('persona.none') :
this.availablePersonas.find(p => p.name === personaName)?.name || personaName;
this.showSuccess(this.tm('messages.personaUpdateSuccess'));
} else {
this.showError(response.data.message || this.tm('messages.personaUpdateError'));
}
} catch (error) {
this.showError(error.response?.data?.message || this.tm('messages.personaUpdateError'));
}
session.updating = false;
});
},
async updateProvider(session, providerId, providerType) {
session.updating = true;
try {
const response = await axios.post('/api/session/update_provider', {
session_id: session.session_id,
provider_id: providerId,
provider_type: providerType
});
if (response.data.status === 'ok') {
// 更新本地数据
return this._updateSession('provider', session, {
provider_id: providerId,
provider_type: providerType
}, (s, success) => {
if (success) {
if (providerType === 'chat_completion') {
session.chat_provider_id = providerId;
s.chat_provider_id = providerId;
const provider = this.availableChatProviders.find(p => p.id === providerId);
session.chat_provider_name = provider?.name || providerId;
s.chat_provider_name = provider?.name || providerId;
} else if (providerType === 'speech_to_text') {
session.stt_provider_id = providerId;
s.stt_provider_id = providerId;
const provider = this.availableSttProviders.find(p => p.id === providerId);
session.stt_provider_name = provider?.name || providerId;
s.stt_provider_name = provider?.name || providerId;
} else if (providerType === 'text_to_speech') {
session.tts_provider_id = providerId;
s.tts_provider_id = providerId;
const provider = this.availableTtsProviders.find(p => p.id === providerId);
session.tts_provider_name = provider?.name || providerId;
s.tts_provider_name = provider?.name || providerId;
}
this.showSuccess(this.tm('messages.providerUpdateSuccess'));
} else {
this.showError(response.data.message || this.tm('messages.providerUpdateError'));
}
} catch (error) {
this.showError(error.response?.data?.message || this.tm('messages.providerUpdateError'));
} session.updating = false;
});
},
async updateLLM(session, enabled) {
return this._updateSession('llm', session, { enabled }, (s, success) => {
if (success) s.llm_enabled = enabled;
});
},
async updateTTS(session, enabled) {
return this._updateSession('tts', session, { enabled }, (s, success) => {
if (success) s.tts_enabled = enabled;
});
},
// 通用的更新会话方法,支持单个和批量操作
async _updateSession(type, sessionOrSessions, params, updateLocalData) {
const isBatch = Array.isArray(sessionOrSessions);
if (!isBatch) {
// 单个操作
const session = sessionOrSessions;
session.updating = true;
try {
const payload = {
is_batch: false,
session_id: session.session_id,
...params
};
const response = await axios.post(`/api/session/update_${type}`, payload);
if (response.data.status === 'ok') {
updateLocalData(session, true);
this.showSuccess(this.tm(`messages.${type}UpdateSuccess`));
return { success: true };
} else {
this.showError(response.data.message || this.tm(`messages.${type}UpdateError`));
return { success: false, error: response.data.message };
}
} catch (error) {
this.showError(error.response?.data?.message || this.tm(`messages.${type}UpdateError`));
return { success: false, error: error.message };
} finally {
session.updating = false;
}
} else {
// 批量操作
const sessions = sessionOrSessions;
const sessionIds = sessions.map(s => s.session_id);
try {
const payload = {
is_batch: true,
session_ids: sessionIds,
...params
};
const response = await axios.post(`/api/session/update_${type}`, payload);
if (response.data.status === 'ok') {
const data = response.data.data;
// 更新成功的会话的本地数据
sessions.forEach(session => {
const wasSuccessful = !data.error_sessions || !data.error_sessions.includes(session.session_id);
updateLocalData(session, wasSuccessful);
});
return {
success: true,
successCount: data.success_count || 0,
errorCount: data.error_count || 0,
errorSessions: data.error_sessions || []
};
} else {
return {
success: false,
error: response.data.message,
errorCount: sessionIds.length,
successCount: 0
};
}
} catch (error) {
return {
success: false,
error: error.response?.data?.message || error.message,
errorCount: sessionIds.length,
successCount: 0
};
}
}
},
// 单独的会话状态更新方法(不支持批量操作)
async updateSessionStatus(session, enabled) {
session.updating = true;
try {
@@ -572,47 +657,9 @@ export default {
if (response.data.status === 'ok') {
session.session_enabled = enabled;
this.showSuccess(this.tm('messages.sessionStatusSuccess', { status: enabled ? this.tm('status.enabled') : this.tm('status.disabled') }));
} else {
this.showError(response.data.message || this.tm('messages.statusUpdateError'));
}
} catch (error) {
this.showError(error.response?.data?.message || this.tm('messages.statusUpdateError'));
}
session.updating = false;
},
async updateLLM(session, enabled) {
session.updating = true;
try {
const response = await axios.post('/api/session/update_llm', {
session_id: session.session_id,
enabled: enabled
});
if (response.data.status === 'ok') {
session.llm_enabled = enabled;
this.showSuccess(this.tm('messages.llmStatusSuccess', { status: enabled ? this.tm('status.enabled') : this.tm('status.disabled') }));
} else {
this.showError(response.data.message || this.tm('messages.statusUpdateError'));
}
} catch (error) {
this.showError(error.response?.data?.message || this.tm('messages.statusUpdateError'));
}
session.updating = false;
},
async updateTTS(session, enabled) {
session.updating = true;
try {
const response = await axios.post('/api/session/update_tts', {
session_id: session.session_id,
enabled: enabled
});
if (response.data.status === 'ok') {
session.tts_enabled = enabled;
this.showSuccess(this.tm('messages.ttsStatusSuccess', { status: enabled ? this.tm('status.enabled') : this.tm('status.disabled') }));
this.showSuccess(this.tm('messages.sessionStatusSuccess', {
status: enabled ? this.tm('status.enabled') : this.tm('status.disabled')
}));
} else {
this.showError(response.data.message || this.tm('messages.statusUpdateError'));
}
@@ -628,60 +675,120 @@ export default {
}
this.batchUpdating = true;
let successCount = 0;
let errorCount = 0;
let totalSuccessCount = 0;
let totalErrorCount = 0;
let allErrorSessions = [];
// 使用过滤后的会话数据进行批量操作
for (const session of this.filteredSessions) {
try {
// 批量更新人格
if (this.batchPersona) {
await this.updatePersona(session, this.batchPersona);
successCount++;
}
const sessions = this.filteredSessions;
// 批量更新 Chat Provider
if (this.batchChatProvider) {
await this.updateProvider(session, this.batchChatProvider, 'chat_completion');
successCount++;
}
try {
// 定义批量操作任务
const batchTasks = [];
// 批量更新 STT Provider
if (this.batchSttProvider) {
await this.updateProvider(session, this.batchSttProvider, 'speech_to_text');
successCount++;
}
// 批量更新 TTS Provider
if (this.batchTtsProvider) {
await this.updateProvider(session, this.batchTtsProvider, 'text_to_speech');
successCount++;
}
// 批量更新 LLM 状态
if (this.batchLlmStatus !== null) {
await this.updateLLM(session, this.batchLlmStatus);
successCount++;
}
// 批量更新 TTS 状态
if (this.batchTtsStatus !== null) {
await this.updateTTS(session, this.batchTtsStatus);
successCount++;
}
} catch (error) {
errorCount++;
if (this.batchPersona) {
batchTasks.push({
type: 'persona',
params: { persona_name: this.batchPersona }
});
}
if (this.batchChatProvider) {
batchTasks.push({
type: 'provider',
params: { provider_id: this.batchChatProvider, provider_type: 'chat_completion' }
});
}
if (this.batchSttProvider) {
batchTasks.push({
type: 'provider',
params: { provider_id: this.batchSttProvider, provider_type: 'speech_to_text' }
});
}
if (this.batchTtsProvider) {
batchTasks.push({
type: 'provider',
params: { provider_id: this.batchTtsProvider, provider_type: 'text_to_speech' }
});
}
if (this.batchLlmStatus !== null) {
batchTasks.push({
type: 'llm',
params: { enabled: this.batchLlmStatus }
});
}
if (this.batchTtsStatus !== null) {
batchTasks.push({
type: 'tts',
params: { enabled: this.batchTtsStatus }
});
}
// 执行所有批量任务
for (const task of batchTasks) {
let updateLocalData;
// 定义本地数据更新逻辑
switch (task.type) {
case 'persona':
updateLocalData = (s, success) => {
if (success) s.persona_id = task.params.persona_name;
};
break;
case 'provider':
updateLocalData = (s, success) => {
if (!success) return;
const { provider_id, provider_type } = task.params;
if (provider_type === 'chat_completion') {
s.chat_provider_id = provider_id;
} else if (provider_type === 'speech_to_text') {
s.stt_provider_id = provider_id;
} else if (provider_type === 'text_to_speech') {
s.tts_provider_id = provider_id;
}
};
break;
case 'llm':
updateLocalData = (s, success) => {
if (success) s.llm_enabled = task.params.enabled;
};
break;
case 'tts':
updateLocalData = (s, success) => {
if (success) s.tts_enabled = task.params.enabled;
};
break;
}
const result = await this._updateSession(task.type, sessions, task.params, updateLocalData);
totalSuccessCount += result.successCount || 0;
totalErrorCount += result.errorCount || 0;
if (result.errorSessions) {
allErrorSessions.push(...result.errorSessions);
}
}
// 显示最终结果
if (totalErrorCount === 0) {
this.showSuccess(this.tm('messages.batchUpdateSuccess', { count: totalSuccessCount }));
} else {
const uniqueErrorSessions = [...new Set(allErrorSessions)];
this.showError(this.tm('messages.batchUpdatePartial', {
success: totalSuccessCount,
error: uniqueErrorSessions.length
}));
}
} catch (error) {
this.showError(this.tm('messages.batchUpdateError'));
}
this.batchUpdating = false;
if (errorCount === 0) {
this.showSuccess(this.tm('messages.batchUpdateSuccess', { count: successCount }));
} else {
this.showError(this.tm('messages.batchUpdatePartial', { success: successCount, error: errorCount }));
}
// 清空批量设置
this.batchPersona = null;
this.batchChatProvider = null;
@@ -797,6 +904,38 @@ export default {
this.snackbarColor = 'error';
this.snackbar = true;
},
async deleteSession(session) {
const confirmMessage = this.tm('deleteConfirm.message', {
sessionName: session.session_name || session.session_id
}) + '\n\n' + this.tm('deleteConfirm.warning');
if (!confirm(confirmMessage)) {
return;
}
session.deleting = true;
try {
const response = await axios.post('/api/session/delete', {
session_id: session.session_id
});
if (response.data.status === 'ok') {
this.showSuccess(response.data.data.message || this.tm('messages.deleteSuccess'));
// 从列表中移除已删除的会话
const index = this.sessions.findIndex(s => s.session_id === session.session_id);
if (index > -1) {
this.sessions.splice(index, 1);
}
} else {
this.showError(response.data.message || this.tm('messages.deleteError'));
}
} catch (error) {
this.showError(error.response?.data?.message || this.tm('messages.deleteError'));
}
session.deleting = false;
},
},
}
</script>