diff --git a/astrbot/dashboard/routes/session_management.py b/astrbot/dashboard/routes/session_management.py index ffe5372a0..a938d662d 100644 --- a/astrbot/dashboard/routes/session_management.py +++ b/astrbot/dashboard/routes/session_management.py @@ -35,14 +35,6 @@ def __init__( "/session/delete-rule": ("POST", self.delete_session_rule), "/session/batch-delete-rule": ("POST", self.batch_delete_session_rule), "/session/active-umos": ("GET", self.list_umos), - "/session/list-all-with-status": ("GET", self.list_all_umos_with_status), - "/session/batch-update-service": ("POST", self.batch_update_service), - "/session/batch-update-provider": ("POST", self.batch_update_provider), - # 分组管理 API - "/session/groups": ("GET", self.list_groups), - "/session/group/create": ("POST", self.create_group), - "/session/group/update": ("POST", self.update_group), - "/session/group/delete": ("POST", self.delete_group), } self.conv_mgr = core_lifecycle.conversation_manager self.core_lifecycle = core_lifecycle @@ -399,540 +391,3 @@ async def list_umos(self): except Exception as e: logger.error(f"获取 UMO 列表失败: {e!s}") return Response().error(f"获取 UMO 列表失败: {e!s}").__dict__ - - async def list_all_umos_with_status(self): - """获取所有有对话记录的 UMO 及其服务状态(支持分页、搜索、筛选) - - Query 参数: - page: 页码,默认为 1 - page_size: 每页数量,默认为 20 - search: 搜索关键词 - message_type: 筛选消息类型 (group/private/all) - platform: 筛选平台 - """ - try: - page = request.args.get("page", 1, type=int) - page_size = request.args.get("page_size", 20, type=int) - search = request.args.get("search", "", type=str).strip() - message_type = request.args.get("message_type", "all", type=str) - platform = request.args.get("platform", "", type=str) - - if page < 1: - page = 1 - if page_size < 1: - page_size = 20 - if page_size > 100: - page_size = 100 - - # 从 Conversation 表获取所有 distinct user_id (即 umo) - async with self.db_helper.get_db() as session: - session: AsyncSession - result = await session.execute( - select(ConversationV2.user_id) - .distinct() - .order_by(ConversationV2.user_id) - ) - all_umos = [row[0] for row in result.fetchall()] - - # 获取所有 umo 的规则配置 - umo_rules, _ = await self._get_umo_rules(page=1, page_size=99999, search="") - - # 构建带状态的 umo 列表 - umos_with_status = [] - for umo in all_umos: - parts = umo.split(":") - umo_platform = parts[0] if len(parts) >= 1 else "unknown" - umo_message_type = parts[1] if len(parts) >= 2 else "unknown" - umo_session_id = parts[2] if len(parts) >= 3 else umo - - # 筛选消息类型 - if message_type != "all": - if message_type == "group" and umo_message_type not in [ - "group", - "GroupMessage", - ]: - continue - if message_type == "private" and umo_message_type not in [ - "private", - "FriendMessage", - "friend", - ]: - continue - - # 筛选平台 - if platform and umo_platform != platform: - continue - - # 获取服务配置 - rules = umo_rules.get(umo, {}) - svc_config = rules.get("session_service_config", {}) - - custom_name = svc_config.get("custom_name", "") if svc_config else "" - session_enabled = ( - svc_config.get("session_enabled", True) if svc_config else True - ) - llm_enabled = ( - svc_config.get("llm_enabled", True) if svc_config else True - ) - tts_enabled = ( - svc_config.get("tts_enabled", True) if svc_config else True - ) - - # 搜索过滤 - if search: - search_lower = search.lower() - if ( - search_lower not in umo.lower() - and search_lower not in custom_name.lower() - ): - continue - - # 获取 provider 配置 - chat_provider_key = ( - f"provider_perf_{ProviderType.CHAT_COMPLETION.value}" - ) - tts_provider_key = f"provider_perf_{ProviderType.TEXT_TO_SPEECH.value}" - stt_provider_key = f"provider_perf_{ProviderType.SPEECH_TO_TEXT.value}" - - umos_with_status.append( - { - "umo": umo, - "platform": umo_platform, - "message_type": umo_message_type, - "session_id": umo_session_id, - "custom_name": custom_name, - "session_enabled": session_enabled, - "llm_enabled": llm_enabled, - "tts_enabled": tts_enabled, - "has_rules": umo in umo_rules, - "chat_provider": rules.get(chat_provider_key), - "tts_provider": rules.get(tts_provider_key), - "stt_provider": rules.get(stt_provider_key), - } - ) - - # 分页 - total = len(umos_with_status) - start_idx = (page - 1) * page_size - end_idx = start_idx + page_size - paginated = umos_with_status[start_idx:end_idx] - - # 获取可用的平台列表 - platforms = list({u["platform"] for u in umos_with_status}) - - # 获取可用的 providers - provider_manager = self.core_lifecycle.provider_manager - available_chat_providers = [ - {"id": p.meta().id, "name": p.meta().id, "model": p.meta().model} - for p in provider_manager.provider_insts - ] - available_tts_providers = [ - {"id": p.meta().id, "name": p.meta().id, "model": p.meta().model} - for p in provider_manager.tts_provider_insts - ] - available_stt_providers = [ - {"id": p.meta().id, "name": p.meta().id, "model": p.meta().model} - for p in provider_manager.stt_provider_insts - ] - - return ( - Response() - .ok( - { - "sessions": paginated, - "total": total, - "page": page, - "page_size": page_size, - "platforms": platforms, - "available_chat_providers": available_chat_providers, - "available_tts_providers": available_tts_providers, - "available_stt_providers": available_stt_providers, - } - ) - .__dict__ - ) - except Exception as e: - logger.error(f"获取会话状态列表失败: {e!s}") - return Response().error(f"获取会话状态列表失败: {e!s}").__dict__ - - async def batch_update_service(self): - """批量更新多个 UMO 的服务状态 (LLM/TTS/Session) - - 请求体: - { - "umos": ["平台:消息类型:会话ID", ...], // 可选,如果不传则根据 scope 筛选 - "scope": "all" | "group" | "private" | "custom_group", // 可选,批量范围 - "group_id": "分组ID", // 当 scope 为 custom_group 时必填 - "llm_enabled": true/false/null, // 可选,null表示不修改 - "tts_enabled": true/false/null, // 可选 - "session_enabled": true/false/null // 可选 - } - """ - try: - data = await request.get_json() - umos = data.get("umos", []) - scope = data.get("scope", "") - group_id = data.get("group_id", "") - llm_enabled = data.get("llm_enabled") - tts_enabled = data.get("tts_enabled") - session_enabled = data.get("session_enabled") - - # 如果没有任何修改 - if llm_enabled is None and tts_enabled is None and session_enabled is None: - return Response().error("至少需要指定一个要修改的状态").__dict__ - - # 如果指定了 scope,获取符合条件的所有 umo - if scope and not umos: - # 如果是自定义分组 - if scope == "custom_group": - if not group_id: - return Response().error("请指定分组 ID").__dict__ - groups = self._get_groups() - if group_id not in groups: - return Response().error(f"分组 '{group_id}' 不存在").__dict__ - umos = groups[group_id].get("umos", []) - else: - async with self.db_helper.get_db() as session: - session: AsyncSession - result = await session.execute( - select(ConversationV2.user_id).distinct() - ) - all_umos = [row[0] for row in result.fetchall()] - - if scope == "group": - umos = [ - u - for u in all_umos - if ":group:" in u.lower() or ":groupmessage:" in u.lower() - ] - elif scope == "private": - umos = [ - u - for u in all_umos - if ":private:" in u.lower() or ":friend" in u.lower() - ] - elif scope == "all": - umos = all_umos - - if not umos: - return Response().error("没有找到符合条件的会话").__dict__ - - # 批量更新 - success_count = 0 - failed_umos = [] - - for umo in umos: - try: - # 获取现有配置 - session_config = ( - sp.get("session_service_config", {}, scope="umo", scope_id=umo) - or {} - ) - - # 更新状态 - if llm_enabled is not None: - session_config["llm_enabled"] = llm_enabled - if tts_enabled is not None: - session_config["tts_enabled"] = tts_enabled - if session_enabled is not None: - session_config["session_enabled"] = session_enabled - - # 保存 - sp.put( - "session_service_config", - session_config, - scope="umo", - scope_id=umo, - ) - success_count += 1 - except Exception as e: - logger.error(f"更新 {umo} 服务状态失败: {e!s}") - failed_umos.append(umo) - - status_changes = [] - if llm_enabled is not None: - status_changes.append(f"LLM={'启用' if llm_enabled else '禁用'}") - if tts_enabled is not None: - status_changes.append(f"TTS={'启用' if tts_enabled else '禁用'}") - if session_enabled is not None: - status_changes.append(f"会话={'启用' if session_enabled else '禁用'}") - - return ( - Response() - .ok( - { - "message": f"已更新 {success_count} 个会话 ({', '.join(status_changes)})", - "success_count": success_count, - "failed_count": len(failed_umos), - "failed_umos": failed_umos, - } - ) - .__dict__ - ) - except Exception as e: - logger.error(f"批量更新服务状态失败: {e!s}") - return Response().error(f"批量更新服务状态失败: {e!s}").__dict__ - - async def batch_update_provider(self): - """批量更新多个 UMO 的 Provider 配置 - - 请求体: - { - "umos": ["平台:消息类型:会话ID", ...], // 可选 - "scope": "all" | "group" | "private", // 可选 - "provider_type": "chat_completion" | "text_to_speech" | "speech_to_text", - "provider_id": "provider_id" - } - """ - try: - data = await request.get_json() - umos = data.get("umos", []) - scope = data.get("scope", "") - provider_type = data.get("provider_type") - provider_id = data.get("provider_id") - - if not provider_type or not provider_id: - return ( - Response() - .error("缺少必要参数: provider_type, provider_id") - .__dict__ - ) - - # 转换 provider_type - provider_type_map = { - "chat_completion": ProviderType.CHAT_COMPLETION, - "text_to_speech": ProviderType.TEXT_TO_SPEECH, - "speech_to_text": ProviderType.SPEECH_TO_TEXT, - } - if provider_type not in provider_type_map: - return ( - Response() - .error(f"不支持的 provider_type: {provider_type}") - .__dict__ - ) - - provider_type_enum = provider_type_map[provider_type] - - # 如果指定了 scope,获取符合条件的所有 umo - group_id = data.get("group_id", "") - if scope and not umos: - # 如果是自定义分组 - if scope == "custom_group": - if not group_id: - return Response().error("请指定分组 ID").__dict__ - groups = self._get_groups() - if group_id not in groups: - return Response().error(f"分组 '{group_id}' 不存在").__dict__ - umos = groups[group_id].get("umos", []) - else: - async with self.db_helper.get_db() as session: - session: AsyncSession - result = await session.execute( - select(ConversationV2.user_id).distinct() - ) - all_umos = [row[0] for row in result.fetchall()] - - if scope == "group": - umos = [ - u - for u in all_umos - if ":group:" in u.lower() or ":groupmessage:" in u.lower() - ] - elif scope == "private": - umos = [ - u - for u in all_umos - if ":private:" in u.lower() or ":friend" in u.lower() - ] - elif scope == "all": - umos = all_umos - - if not umos: - return Response().error("没有找到符合条件的会话").__dict__ - - # 批量更新 - success_count = 0 - failed_umos = [] - provider_manager = self.core_lifecycle.provider_manager - - for umo in umos: - try: - await provider_manager.set_provider( - provider_id=provider_id, - provider_type=provider_type_enum, - umo=umo, - ) - success_count += 1 - except Exception as e: - logger.error(f"更新 {umo} Provider 失败: {e!s}") - failed_umos.append(umo) - - return ( - Response() - .ok( - { - "message": f"已更新 {success_count} 个会话的 {provider_type} 为 {provider_id}", - "success_count": success_count, - "failed_count": len(failed_umos), - "failed_umos": failed_umos, - } - ) - .__dict__ - ) - except Exception as e: - logger.error(f"批量更新 Provider 失败: {e!s}") - return Response().error(f"批量更新 Provider 失败: {e!s}").__dict__ - - # ==================== 分组管理 API ==================== - - def _get_groups(self) -> dict: - """获取所有分组""" - return sp.get("session_groups", {}) - - def _save_groups(self, groups: dict) -> None: - """保存分组""" - sp.put("session_groups", groups) - - async def list_groups(self): - """获取所有分组列表""" - try: - groups = self._get_groups() - # 转换为列表格式,方便前端使用 - groups_list = [] - for group_id, group_data in groups.items(): - groups_list.append( - { - "id": group_id, - "name": group_data.get("name", ""), - "umos": group_data.get("umos", []), - "umo_count": len(group_data.get("umos", [])), - } - ) - return Response().ok({"groups": groups_list}).__dict__ - except Exception as e: - logger.error(f"获取分组列表失败: {e!s}") - return Response().error(f"获取分组列表失败: {e!s}").__dict__ - - async def create_group(self): - """创建新分组""" - try: - data = await request.json - name = data.get("name", "").strip() - umos = data.get("umos", []) - - if not name: - return Response().error("分组名称不能为空").__dict__ - - groups = self._get_groups() - - # 生成唯一 ID - import uuid - - group_id = str(uuid.uuid4())[:8] - - groups[group_id] = { - "name": name, - "umos": umos, - } - - self._save_groups(groups) - - return ( - Response() - .ok( - { - "message": f"分组 '{name}' 创建成功", - "group": { - "id": group_id, - "name": name, - "umos": umos, - "umo_count": len(umos), - }, - } - ) - .__dict__ - ) - except Exception as e: - logger.error(f"创建分组失败: {e!s}") - return Response().error(f"创建分组失败: {e!s}").__dict__ - - async def update_group(self): - """更新分组(改名、增删成员)""" - try: - data = await request.json - group_id = data.get("id") - name = data.get("name") - umos = data.get("umos") - add_umos = data.get("add_umos", []) - remove_umos = data.get("remove_umos", []) - - if not group_id: - return Response().error("分组 ID 不能为空").__dict__ - - groups = self._get_groups() - - if group_id not in groups: - return Response().error(f"分组 '{group_id}' 不存在").__dict__ - - group = groups[group_id] - - # 更新名称 - if name is not None: - group["name"] = name.strip() - - # 直接设置 umos 列表 - if umos is not None: - group["umos"] = umos - else: - # 增量更新 - current_umos = set(group.get("umos", [])) - if add_umos: - current_umos.update(add_umos) - if remove_umos: - current_umos.difference_update(remove_umos) - group["umos"] = list(current_umos) - - self._save_groups(groups) - - return ( - Response() - .ok( - { - "message": f"分组 '{group['name']}' 更新成功", - "group": { - "id": group_id, - "name": group["name"], - "umos": group["umos"], - "umo_count": len(group["umos"]), - }, - } - ) - .__dict__ - ) - except Exception as e: - logger.error(f"更新分组失败: {e!s}") - return Response().error(f"更新分组失败: {e!s}").__dict__ - - async def delete_group(self): - """删除分组""" - try: - data = await request.json - group_id = data.get("id") - - if not group_id: - return Response().error("分组 ID 不能为空").__dict__ - - groups = self._get_groups() - - if group_id not in groups: - return Response().error(f"分组 '{group_id}' 不存在").__dict__ - - group_name = groups[group_id].get("name", group_id) - del groups[group_id] - - self._save_groups(groups) - - return Response().ok({"message": f"分组 '{group_name}' 已删除"}).__dict__ - except Exception as e: - logger.error(f"删除分组失败: {e!s}") - return Response().error(f"删除分组失败: {e!s}").__dict__ diff --git a/dashboard/src/i18n/locales/en-US/features/session-management.json b/dashboard/src/i18n/locales/en-US/features/session-management.json index fdd6b4f82..dc0d3c4a8 100644 --- a/dashboard/src/i18n/locales/en-US/features/session-management.json +++ b/dashboard/src/i18n/locales/en-US/features/session-management.json @@ -1,4 +1,4 @@ -{ +{ "title": "Custom Rules", "subtitle": "Set custom rules for specific sessions, which take priority over global settings", "buttons": { @@ -93,42 +93,6 @@ "batchDeleteConfirm": { "title": "Confirm Batch Delete", "message": "Are you sure you want to delete {count} selected rules? Global settings will be used after deletion." - }, - "batchOperations": { - "title": "Batch Operations", - "hint": "Quick batch modify session settings", - "scope": "Apply to", - "scopeSelected": "Selected sessions", - "scopeAll": "All sessions", - "scopeGroup": "All groups", - "scopePrivate": "All private chats", - "llmStatus": "LLM Status", - "ttsStatus": "TTS Status", - "chatProvider": "Chat Model", - "ttsProvider": "TTS Model", - "apply": "Apply Changes" - }, - "status": { - "enabled": "Enabled", - "disabled": "Disabled" - }, - "batchOperations": { - "title": "Batch Operations", - "hint": "Quick batch modify session settings", - "scope": "Apply to", - "scopeSelected": "Selected sessions", - "scopeAll": "All sessions", - "scopeGroup": "All groups", - "scopePrivate": "All private chats", - "llmStatus": "LLM Status", - "ttsStatus": "TTS Status", - "chatProvider": "Chat Model", - "ttsProvider": "TTS Model", - "apply": "Apply Changes" - }, - "status": { - "enabled": "Enabled", - "disabled": "Disabled" }, "messages": { "refreshSuccess": "Data refreshed", @@ -141,8 +105,6 @@ "deleteError": "Failed to delete", "noChanges": "No changes to save", "batchDeleteSuccess": "Batch delete successful", - "batchDeleteError": "Batch delete failed", - "batchUpdateError": "Batch update failed", - "batchUpdateSuccess": "Batch update success" + "batchDeleteError": "Batch delete failed" } } diff --git a/dashboard/src/i18n/locales/zh-CN/features/session-management.json b/dashboard/src/i18n/locales/zh-CN/features/session-management.json index 33b387cd2..4b9053ebf 100644 --- a/dashboard/src/i18n/locales/zh-CN/features/session-management.json +++ b/dashboard/src/i18n/locales/zh-CN/features/session-management.json @@ -1,4 +1,4 @@ -{ +{ "title": "自定义规则", "subtitle": "为特定会话设置自定义规则,优先级高于全局配置", "buttons": { @@ -94,24 +94,6 @@ "title": "确认批量删除", "message": "确定要删除选中的 {count} 条规则吗?删除后将恢复使用全局配置。" }, - "batchOperations": { - "title": "批量操作", - "hint": "快速批量修改会话配置", - "scope": "应用范围", - "scopeSelected": "选中的会话", - "scopeAll": "所有会话", - "scopeGroup": "所有群聊", - "scopePrivate": "所有私聊", - "llmStatus": "LLM 状态", - "ttsStatus": "TTS 状态", - "chatProvider": "聊天模型", - "ttsProvider": "TTS 模型", - "apply": "应用更改" - }, - "status": { - "enabled": "启用", - "disabled": "禁用" - }, "messages": { "refreshSuccess": "数据已刷新", "loadError": "加载数据失败", diff --git a/dashboard/src/views/SessionManagementPage.vue b/dashboard/src/views/SessionManagementPage.vue index b754f8c1c..2f7bb124b 100644 --- a/dashboard/src/views/SessionManagementPage.vue +++ b/dashboard/src/views/SessionManagementPage.vue @@ -1,4 +1,4 @@ -