Compare commits
3 Commits
master
...
refactor/c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf5a6aeaff | ||
|
|
3989a6669c | ||
|
|
0b53b8f96a |
@@ -145,7 +145,16 @@ DEFAULT_CONFIG = {
|
||||
}
|
||||
|
||||
|
||||
# 配置项的中文描述、值类型
|
||||
"""
|
||||
AstrBot v3 时代的配置元数据,目前仅承担以下功能:
|
||||
|
||||
1. 保存配置时,配置项的类型验证
|
||||
2. WebUI 展示提供商和平台适配器模版
|
||||
|
||||
WebUI 的配置文件在 `CONFIG_METADATA_3` 中。
|
||||
|
||||
未来将会逐步淘汰此配置元数据。
|
||||
"""
|
||||
CONFIG_METADATA_2 = {
|
||||
"platform_group": {
|
||||
"metadata": {
|
||||
@@ -1091,7 +1100,7 @@ CONFIG_METADATA_2 = {
|
||||
"api_base": "",
|
||||
"model": "whisper-1",
|
||||
},
|
||||
"Whisper(本地加载)": {
|
||||
"Whisper(Local)": {
|
||||
"hint": "启用前请 pip 安装 openai-whisper 库(N卡用户大约下载 2GB,主要是 torch 和 cuda,CPU 用户大约下载 1 GB),并且安装 ffmpeg。否则将无法正常转文字。",
|
||||
"provider": "openai",
|
||||
"type": "openai_whisper_selfhost",
|
||||
@@ -1100,7 +1109,7 @@ CONFIG_METADATA_2 = {
|
||||
"id": "whisper_selfhost",
|
||||
"model": "tiny",
|
||||
},
|
||||
"SenseVoice(本地加载)": {
|
||||
"SenseVoice(Local)": {
|
||||
"hint": "启用前请 pip 安装 funasr、funasr_onnx、torchaudio、torch、modelscope、jieba 库(默认使用CPU,大约下载 1 GB),并且安装 ffmpeg。否则将无法正常转文字。",
|
||||
"type": "sensevoice_stt_selfhost",
|
||||
"provider": "sensevoice",
|
||||
@@ -1135,7 +1144,7 @@ CONFIG_METADATA_2 = {
|
||||
"pitch": "+0Hz",
|
||||
"timeout": 20,
|
||||
},
|
||||
"GSV TTS(本地加载)": {
|
||||
"GSV TTS(Local)": {
|
||||
"id": "gsv_tts",
|
||||
"enable": False,
|
||||
"provider": "gpt_sovits",
|
||||
@@ -2189,6 +2198,14 @@ CONFIG_METADATA_2 = {
|
||||
}
|
||||
|
||||
|
||||
"""
|
||||
v4.7.0 之后,name, description, hint 等字段已经实现 i18n 国际化。国际化资源文件位于:
|
||||
|
||||
- dashboard/src/i18n/locales/en-US/features/config-metadata.json
|
||||
- dashboard/src/i18n/locales/zh-CN/features/config-metadata.json
|
||||
|
||||
如果在此文件中添加了新的配置字段,请务必同步更新上述两个国际化资源文件。
|
||||
"""
|
||||
CONFIG_METADATA_3 = {
|
||||
"ai_group": {
|
||||
"name": "AI 配置",
|
||||
|
||||
110
astrbot/core/config/i18n_utils.py
Normal file
110
astrbot/core/config/i18n_utils.py
Normal file
@@ -0,0 +1,110 @@
|
||||
"""
|
||||
配置元数据国际化工具
|
||||
|
||||
提供配置元数据的国际化键转换功能
|
||||
"""
|
||||
|
||||
from typing import Any
|
||||
|
||||
|
||||
class ConfigMetadataI18n:
|
||||
"""配置元数据国际化转换器"""
|
||||
|
||||
@staticmethod
|
||||
def _get_i18n_key(group: str, section: str, field: str, attr: str) -> str:
|
||||
"""
|
||||
生成国际化键
|
||||
|
||||
Args:
|
||||
group: 配置组,如 'ai_group', 'platform_group'
|
||||
section: 配置节,如 'agent_runner', 'general'
|
||||
field: 字段名,如 'enable', 'default_provider'
|
||||
attr: 属性类型,如 'description', 'hint', 'labels'
|
||||
|
||||
Returns:
|
||||
国际化键,格式如: 'ai_group.agent_runner.enable.description'
|
||||
"""
|
||||
if field:
|
||||
return f"{group}.{section}.{field}.{attr}"
|
||||
else:
|
||||
return f"{group}.{section}.{attr}"
|
||||
|
||||
@staticmethod
|
||||
def convert_to_i18n_keys(metadata: dict[str, Any]) -> dict[str, Any]:
|
||||
"""
|
||||
将配置元数据转换为使用国际化键
|
||||
|
||||
Args:
|
||||
metadata: 原始配置元数据字典
|
||||
|
||||
Returns:
|
||||
使用国际化键的配置元数据字典
|
||||
"""
|
||||
result = {}
|
||||
|
||||
for group_key, group_data in metadata.items():
|
||||
group_result = {
|
||||
"name": f"{group_key}.name",
|
||||
"metadata": {},
|
||||
}
|
||||
|
||||
for section_key, section_data in group_data.get("metadata", {}).items():
|
||||
section_result = {
|
||||
"description": f"{group_key}.{section_key}.description",
|
||||
"type": section_data.get("type"),
|
||||
}
|
||||
|
||||
# 复制其他属性
|
||||
for key in ["items", "condition", "_special", "invisible"]:
|
||||
if key in section_data:
|
||||
section_result[key] = section_data[key]
|
||||
|
||||
# 处理 hint
|
||||
if "hint" in section_data:
|
||||
section_result["hint"] = f"{group_key}.{section_key}.hint"
|
||||
|
||||
# 处理 items 中的字段
|
||||
if "items" in section_data and isinstance(section_data["items"], dict):
|
||||
items_result = {}
|
||||
for field_key, field_data in section_data["items"].items():
|
||||
# 处理嵌套的点号字段名(如 provider_settings.enable)
|
||||
field_name = field_key
|
||||
|
||||
field_result = {}
|
||||
|
||||
# 复制基本属性
|
||||
for attr in [
|
||||
"type",
|
||||
"condition",
|
||||
"_special",
|
||||
"invisible",
|
||||
"options",
|
||||
]:
|
||||
if attr in field_data:
|
||||
field_result[attr] = field_data[attr]
|
||||
|
||||
# 转换文本属性为国际化键
|
||||
if "description" in field_data:
|
||||
field_result["description"] = (
|
||||
f"{group_key}.{section_key}.{field_name}.description"
|
||||
)
|
||||
|
||||
if "hint" in field_data:
|
||||
field_result["hint"] = (
|
||||
f"{group_key}.{section_key}.{field_name}.hint"
|
||||
)
|
||||
|
||||
if "labels" in field_data:
|
||||
field_result["labels"] = (
|
||||
f"{group_key}.{section_key}.{field_name}.labels"
|
||||
)
|
||||
|
||||
items_result[field_key] = field_result
|
||||
|
||||
section_result["items"] = items_result
|
||||
|
||||
group_result["metadata"][section_key] = section_result
|
||||
|
||||
result[group_key] = group_result
|
||||
|
||||
return result
|
||||
@@ -14,6 +14,7 @@ from astrbot.core.config.default import (
|
||||
DEFAULT_CONFIG,
|
||||
DEFAULT_VALUE_MAP,
|
||||
)
|
||||
from astrbot.core.config.i18n_utils import ConfigMetadataI18n
|
||||
from astrbot.core.core_lifecycle import AstrBotCoreLifecycle
|
||||
from astrbot.core.platform.register import platform_cls_map, platform_registry
|
||||
from astrbot.core.provider import Provider
|
||||
@@ -133,7 +134,9 @@ def save_config(post_config: dict, config: AstrBotConfig, is_core: bool = False)
|
||||
is_core,
|
||||
)
|
||||
else:
|
||||
errors, post_config = validate_config(post_config, config.schema, is_core)
|
||||
errors, post_config = validate_config(
|
||||
post_config, getattr(config, "schema", {}), is_core
|
||||
)
|
||||
except BaseException as e:
|
||||
logger.error(traceback.format_exc())
|
||||
logger.warning(f"验证配置时出现异常: {e}")
|
||||
@@ -247,11 +250,8 @@ class ConfigRoute(Route):
|
||||
|
||||
async def get_default_config(self):
|
||||
"""获取默认配置文件"""
|
||||
return (
|
||||
Response()
|
||||
.ok({"config": DEFAULT_CONFIG, "metadata": CONFIG_METADATA_3})
|
||||
.__dict__
|
||||
)
|
||||
metadata = ConfigMetadataI18n.convert_to_i18n_keys(CONFIG_METADATA_3)
|
||||
return Response().ok({"config": DEFAULT_CONFIG, "metadata": metadata}).__dict__
|
||||
|
||||
async def get_abconf_list(self):
|
||||
"""获取所有 AstrBot 配置文件的列表"""
|
||||
@@ -282,17 +282,15 @@ class ConfigRoute(Route):
|
||||
try:
|
||||
if system_config:
|
||||
abconf = self.acm.confs["default"]
|
||||
return (
|
||||
Response()
|
||||
.ok({"config": abconf, "metadata": CONFIG_METADATA_3_SYSTEM})
|
||||
.__dict__
|
||||
metadata = ConfigMetadataI18n.convert_to_i18n_keys(
|
||||
CONFIG_METADATA_3_SYSTEM
|
||||
)
|
||||
return Response().ok({"config": abconf, "metadata": metadata}).__dict__
|
||||
if abconf_id is None:
|
||||
raise ValueError("abconf_id cannot be None")
|
||||
abconf = self.acm.confs[abconf_id]
|
||||
return (
|
||||
Response()
|
||||
.ok({"config": abconf, "metadata": CONFIG_METADATA_3})
|
||||
.__dict__
|
||||
)
|
||||
metadata = ConfigMetadataI18n.convert_to_i18n_keys(CONFIG_METADATA_3)
|
||||
return Response().ok({"config": abconf, "metadata": metadata}).__dict__
|
||||
except ValueError as e:
|
||||
return Response().error(str(e)).__dict__
|
||||
|
||||
@@ -598,9 +596,15 @@ class ConfigRoute(Route):
|
||||
return Response().error("缺少参数 provider_id").__dict__
|
||||
|
||||
prov_mgr = self.core_lifecycle.provider_manager
|
||||
provider: Provider | None = prov_mgr.inst_map.get(provider_id, None)
|
||||
provider = prov_mgr.inst_map.get(provider_id, None)
|
||||
if not provider:
|
||||
return Response().error(f"未找到 ID 为 {provider_id} 的提供商").__dict__
|
||||
if not isinstance(provider, Provider):
|
||||
return (
|
||||
Response()
|
||||
.error(f"提供商 {provider_id} 类型不支持获取模型列表")
|
||||
.__dict__
|
||||
)
|
||||
|
||||
try:
|
||||
models = await provider.get_models()
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
import traceback
|
||||
|
||||
from quart import request
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlmodel import col, select
|
||||
|
||||
from astrbot.core import logger, sp
|
||||
from astrbot.core.core_lifecycle import AstrBotCoreLifecycle
|
||||
from astrbot.core.db import BaseDatabase
|
||||
from astrbot.core.db.po import ConversationV2, Preference
|
||||
from astrbot.core.provider.entities import ProviderType
|
||||
from astrbot.core.star.session_llm_manager import SessionServiceManager
|
||||
from astrbot.core.star.session_plugin_manager import SessionPluginManager
|
||||
|
||||
from .route import Response, Route, RouteContext
|
||||
|
||||
AVAILABLE_SESSION_RULE_KEYS = [
|
||||
"session_service_config",
|
||||
"session_plugin_config",
|
||||
"kb_config",
|
||||
f"provider_perf_{ProviderType.CHAT_COMPLETION.value}",
|
||||
f"provider_perf_{ProviderType.SPEECH_TO_TEXT.value}",
|
||||
f"provider_perf_{ProviderType.TEXT_TO_SPEECH.value}",
|
||||
]
|
||||
|
||||
|
||||
class SessionManagementRoute(Route):
|
||||
def __init__(
|
||||
@@ -22,667 +30,325 @@ class SessionManagementRoute(Route):
|
||||
super().__init__(context)
|
||||
self.db_helper = db_helper
|
||||
self.routes = {
|
||||
"/session/list": ("GET", self.list_sessions),
|
||||
"/session/update_persona": ("POST", self.update_session_persona),
|
||||
"/session/update_provider": ("POST", self.update_session_provider),
|
||||
"/session/plugins": ("GET", self.get_session_plugins),
|
||||
"/session/update_plugin": ("POST", self.update_session_plugin),
|
||||
"/session/update_llm": ("POST", self.update_session_llm),
|
||||
"/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),
|
||||
"/session/list-rule": ("GET", self.list_session_rule),
|
||||
"/session/update-rule": ("POST", self.update_session_rule),
|
||||
"/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),
|
||||
}
|
||||
self.conv_mgr = core_lifecycle.conversation_manager
|
||||
self.core_lifecycle = core_lifecycle
|
||||
self.register_routes()
|
||||
|
||||
async def list_sessions(self):
|
||||
"""获取所有会话的列表,包括 persona 和 provider 信息"""
|
||||
try:
|
||||
page = int(request.args.get("page", 1))
|
||||
page_size = int(request.args.get("page_size", 20))
|
||||
search_query = request.args.get("search", "")
|
||||
platform = request.args.get("platform", "")
|
||||
async def _get_umo_rules(
|
||||
self, page: int = 1, page_size: int = 10, search: str = ""
|
||||
) -> tuple[dict, int]:
|
||||
"""获取所有带有自定义规则的 umo 及其规则内容(支持分页和搜索)。
|
||||
|
||||
# 获取活跃的会话数据(处于对话内的会话)
|
||||
sessions_data, total = await self.db_helper.get_session_conversations(
|
||||
page,
|
||||
page_size,
|
||||
search_query,
|
||||
platform,
|
||||
如果某个 umo 在 preference 中有以下字段,则表示有自定义规则:
|
||||
|
||||
1. session_service_config (包含了 是否启用这个umo, 这个umo是否启用 llm, 这个umo是否启用tts, umo自定义名称。)
|
||||
2. session_plugin_config (包含了 这个 umo 的 plugin set)
|
||||
3. provider_perf_{ProviderType.value} (包含了这个 umo 所选择使用的 provider 信息)
|
||||
4. kb_config (包含了这个 umo 的知识库相关配置)
|
||||
|
||||
Args:
|
||||
page: 页码,从 1 开始
|
||||
page_size: 每页数量
|
||||
search: 搜索关键词,匹配 umo 或 custom_name
|
||||
|
||||
Returns:
|
||||
tuple[dict, int]: (umo_rules, total) - 分页后的 umo 规则和总数
|
||||
"""
|
||||
umo_rules = {}
|
||||
async with self.db_helper.get_db() as session:
|
||||
session: AsyncSession
|
||||
result = await session.execute(
|
||||
select(Preference).where(
|
||||
col(Preference.scope) == "umo",
|
||||
col(Preference.key).in_(AVAILABLE_SESSION_RULE_KEYS),
|
||||
)
|
||||
)
|
||||
prefs = result.scalars().all()
|
||||
for pref in prefs:
|
||||
umo_id = pref.scope_id
|
||||
if umo_id not in umo_rules:
|
||||
umo_rules[umo_id] = {}
|
||||
umo_rules[umo_id][pref.key] = pref.value["val"]
|
||||
|
||||
# 搜索过滤
|
||||
if search:
|
||||
search_lower = search.lower()
|
||||
filtered_rules = {}
|
||||
for umo_id, rules in umo_rules.items():
|
||||
# 匹配 umo
|
||||
if search_lower in umo_id.lower():
|
||||
filtered_rules[umo_id] = rules
|
||||
continue
|
||||
# 匹配 custom_name
|
||||
svc_config = rules.get("session_service_config", {})
|
||||
custom_name = svc_config.get("custom_name", "") if svc_config else ""
|
||||
if custom_name and search_lower in custom_name.lower():
|
||||
filtered_rules[umo_id] = rules
|
||||
umo_rules = filtered_rules
|
||||
|
||||
# 获取总数
|
||||
total = len(umo_rules)
|
||||
|
||||
# 分页处理
|
||||
all_umo_ids = list(umo_rules.keys())
|
||||
start_idx = (page - 1) * page_size
|
||||
end_idx = start_idx + page_size
|
||||
paginated_umo_ids = all_umo_ids[start_idx:end_idx]
|
||||
|
||||
# 只返回分页后的数据
|
||||
paginated_rules = {umo_id: umo_rules[umo_id] for umo_id in paginated_umo_ids}
|
||||
|
||||
return paginated_rules, total
|
||||
|
||||
async def list_session_rule(self):
|
||||
"""获取所有自定义的规则(支持分页和搜索)
|
||||
|
||||
返回已配置规则的 umo 列表及其规则内容,以及可用的 personas 和 providers
|
||||
|
||||
Query 参数:
|
||||
page: 页码,默认为 1
|
||||
page_size: 每页数量,默认为 10
|
||||
search: 搜索关键词,匹配 umo 或 custom_name
|
||||
"""
|
||||
try:
|
||||
# 获取分页和搜索参数
|
||||
page = request.args.get("page", 1, type=int)
|
||||
page_size = request.args.get("page_size", 10, type=int)
|
||||
search = request.args.get("search", "", type=str).strip()
|
||||
|
||||
# 参数校验
|
||||
if page < 1:
|
||||
page = 1
|
||||
if page_size < 1:
|
||||
page_size = 10
|
||||
if page_size > 100:
|
||||
page_size = 100
|
||||
|
||||
umo_rules, total = await self._get_umo_rules(
|
||||
page=page, page_size=page_size, search=search
|
||||
)
|
||||
|
||||
# 构建规则列表
|
||||
rules_list = []
|
||||
for umo, rules in umo_rules.items():
|
||||
rule_info = {
|
||||
"umo": umo,
|
||||
"rules": rules,
|
||||
}
|
||||
# 解析 umo 格式: 平台:消息类型:会话ID
|
||||
parts = umo.split(":")
|
||||
if len(parts) >= 3:
|
||||
rule_info["platform"] = parts[0]
|
||||
rule_info["message_type"] = parts[1]
|
||||
rule_info["session_id"] = parts[2]
|
||||
rules_list.append(rule_info)
|
||||
|
||||
# 获取可用的 providers 和 personas
|
||||
provider_manager = self.core_lifecycle.provider_manager
|
||||
persona_mgr = self.core_lifecycle.persona_mgr
|
||||
personas = persona_mgr.personas_v3
|
||||
|
||||
sessions = []
|
||||
|
||||
# 循环补充非数据库信息,如 provider 和 session 状态
|
||||
for data in sessions_data:
|
||||
session_id = data["session_id"]
|
||||
conversation_id = data["conversation_id"]
|
||||
conv_persona_id = data["persona_id"]
|
||||
title = data["title"]
|
||||
persona_name = data["persona_name"]
|
||||
|
||||
# 处理 persona 显示
|
||||
if persona_name is None:
|
||||
if conv_persona_id is None:
|
||||
if default_persona := persona_mgr.selected_default_persona_v3:
|
||||
persona_name = default_persona["name"]
|
||||
else:
|
||||
persona_name = "[%None]"
|
||||
|
||||
session_info = {
|
||||
"session_id": session_id,
|
||||
"conversation_id": conversation_id,
|
||||
"persona_id": persona_name,
|
||||
"chat_provider_id": None,
|
||||
"stt_provider_id": None,
|
||||
"tts_provider_id": None,
|
||||
"session_enabled": SessionServiceManager.is_session_enabled(
|
||||
session_id,
|
||||
),
|
||||
"llm_enabled": SessionServiceManager.is_llm_enabled_for_session(
|
||||
session_id,
|
||||
),
|
||||
"tts_enabled": SessionServiceManager.is_tts_enabled_for_session(
|
||||
session_id,
|
||||
),
|
||||
"platform": session_id.split(":")[0]
|
||||
if ":" in session_id
|
||||
else "unknown",
|
||||
"message_type": session_id.split(":")[1]
|
||||
if session_id.count(":") >= 1
|
||||
else "unknown",
|
||||
"session_name": SessionServiceManager.get_session_display_name(
|
||||
session_id,
|
||||
),
|
||||
"session_raw_name": session_id.split(":")[2]
|
||||
if session_id.count(":") >= 2
|
||||
else session_id,
|
||||
"title": title,
|
||||
}
|
||||
|
||||
# 获取 provider 信息
|
||||
chat_provider = provider_manager.get_using_provider(
|
||||
provider_type=ProviderType.CHAT_COMPLETION,
|
||||
umo=session_id,
|
||||
)
|
||||
tts_provider = provider_manager.get_using_provider(
|
||||
provider_type=ProviderType.TEXT_TO_SPEECH,
|
||||
umo=session_id,
|
||||
)
|
||||
stt_provider = provider_manager.get_using_provider(
|
||||
provider_type=ProviderType.SPEECH_TO_TEXT,
|
||||
umo=session_id,
|
||||
)
|
||||
if chat_provider:
|
||||
meta = chat_provider.meta()
|
||||
session_info["chat_provider_id"] = meta.id
|
||||
if tts_provider:
|
||||
meta = tts_provider.meta()
|
||||
session_info["tts_provider_id"] = meta.id
|
||||
if stt_provider:
|
||||
meta = stt_provider.meta()
|
||||
session_info["stt_provider_id"] = meta.id
|
||||
|
||||
sessions.append(session_info)
|
||||
|
||||
# 获取可用的 personas 和 providers 列表
|
||||
available_personas = [
|
||||
{"name": p["name"], "prompt": p.get("prompt", "")} for p in personas
|
||||
{"name": p["name"], "prompt": p.get("prompt", "")}
|
||||
for p in persona_mgr.personas_v3
|
||||
]
|
||||
|
||||
available_chat_providers = []
|
||||
for provider in provider_manager.provider_insts:
|
||||
meta = provider.meta()
|
||||
available_chat_providers.append(
|
||||
{
|
||||
"id": meta.id,
|
||||
"name": meta.id,
|
||||
"model": meta.model,
|
||||
"type": meta.type,
|
||||
},
|
||||
)
|
||||
|
||||
available_stt_providers = []
|
||||
for provider in provider_manager.stt_provider_insts:
|
||||
meta = provider.meta()
|
||||
available_stt_providers.append(
|
||||
{
|
||||
"id": meta.id,
|
||||
"name": meta.id,
|
||||
"model": meta.model,
|
||||
"type": meta.type,
|
||||
},
|
||||
)
|
||||
|
||||
available_tts_providers = []
|
||||
for provider in provider_manager.tts_provider_insts:
|
||||
meta = provider.meta()
|
||||
available_tts_providers.append(
|
||||
{
|
||||
"id": meta.id,
|
||||
"name": meta.id,
|
||||
"model": meta.model,
|
||||
"type": meta.type,
|
||||
},
|
||||
)
|
||||
|
||||
result = {
|
||||
"sessions": sessions,
|
||||
"available_personas": available_personas,
|
||||
"available_chat_providers": available_chat_providers,
|
||||
"available_stt_providers": available_stt_providers,
|
||||
"available_tts_providers": available_tts_providers,
|
||||
"pagination": {
|
||||
"page": page,
|
||||
"page_size": page_size,
|
||||
"total": total,
|
||||
"total_pages": (total + page_size - 1) // page_size
|
||||
if page_size > 0
|
||||
else 0,
|
||||
},
|
||||
}
|
||||
|
||||
return Response().ok(result).__dict__
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"获取会话列表失败: {e!s}\n{traceback.format_exc()}"
|
||||
logger.error(error_msg)
|
||||
return Response().error(f"获取会话列表失败: {e!s}").__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} 失败: {e!s}")
|
||||
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__
|
||||
)
|
||||
return (
|
||||
Response()
|
||||
.ok(
|
||||
available_chat_providers = [
|
||||
{
|
||||
"message": f"成功批量{operation_name} {success_count} 个会话",
|
||||
"success_count": success_count,
|
||||
},
|
||||
)
|
||||
.__dict__
|
||||
)
|
||||
"id": p.meta().id,
|
||||
"name": p.meta().id,
|
||||
"model": p.meta().model,
|
||||
}
|
||||
for p in provider_manager.provider_insts
|
||||
]
|
||||
|
||||
async def update_session_persona(self):
|
||||
"""更新指定会话的 persona,支持批量操作"""
|
||||
try:
|
||||
data = await request.get_json()
|
||||
is_batch = data.get("is_batch", False)
|
||||
persona_name = data.get("persona_name")
|
||||
available_stt_providers = [
|
||||
{
|
||||
"id": p.meta().id,
|
||||
"name": p.meta().id,
|
||||
"model": p.meta().model,
|
||||
}
|
||||
for p in provider_manager.stt_provider_insts
|
||||
]
|
||||
|
||||
if persona_name is None:
|
||||
return Response().error("缺少必要参数: persona_name").__dict__
|
||||
available_tts_providers = [
|
||||
{
|
||||
"id": p.meta().id,
|
||||
"name": p.meta().id,
|
||||
"model": p.meta().model,
|
||||
}
|
||||
for p in provider_manager.tts_provider_insts
|
||||
]
|
||||
|
||||
if is_batch:
|
||||
session_ids = data.get("session_ids", [])
|
||||
if not session_ids:
|
||||
return Response().error("缺少必要参数: session_ids").__dict__
|
||||
|
||||
return await self._handle_batch_operation(
|
||||
session_ids,
|
||||
self._update_single_session_persona,
|
||||
"更新人格",
|
||||
persona_name=persona_name,
|
||||
)
|
||||
session_id = data.get("session_id")
|
||||
if not session_id:
|
||||
return Response().error("缺少必要参数: session_id").__dict__
|
||||
|
||||
await self._update_single_session_persona(session_id, persona_name)
|
||||
return (
|
||||
Response()
|
||||
.ok(
|
||||
{
|
||||
"message": f"成功更新会话 {session_id} 的人格为 {persona_name}",
|
||||
},
|
||||
"rules": rules_list,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": page_size,
|
||||
"available_personas": available_personas,
|
||||
"available_chat_providers": available_chat_providers,
|
||||
"available_stt_providers": available_stt_providers,
|
||||
"available_tts_providers": available_tts_providers,
|
||||
"available_rule_keys": AVAILABLE_SESSION_RULE_KEYS,
|
||||
}
|
||||
)
|
||||
.__dict__
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"更新会话人格失败: {e!s}\n{traceback.format_exc()}"
|
||||
logger.error(error_msg)
|
||||
return Response().error(f"更新会话人格失败: {e!s}").__dict__
|
||||
logger.error(f"获取规则列表失败: {e!s}")
|
||||
return Response().error(f"获取规则列表失败: {e!s}").__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_rule(self):
|
||||
"""更新某个 umo 的自定义规则
|
||||
|
||||
async def update_session_provider(self):
|
||||
"""更新指定会话的 provider,支持批量操作"""
|
||||
请求体:
|
||||
{
|
||||
"umo": "平台:消息类型:会话ID",
|
||||
"rule_key": "session_service_config" | "session_plugin_config" | "kb_config" | "provider_perf_xxx",
|
||||
"rule_value": {...} // 规则值,具体结构根据 rule_key 不同而不同
|
||||
}
|
||||
"""
|
||||
try:
|
||||
data = await request.get_json()
|
||||
is_batch = data.get("is_batch", False)
|
||||
provider_id = data.get("provider_id")
|
||||
provider_type = data.get("provider_type")
|
||||
umo = data.get("umo")
|
||||
rule_key = data.get("rule_key")
|
||||
rule_value = data.get("rule_value")
|
||||
|
||||
if not provider_id or not provider_type:
|
||||
if not umo:
|
||||
return Response().error("缺少必要参数: umo").__dict__
|
||||
if not rule_key:
|
||||
return Response().error("缺少必要参数: rule_key").__dict__
|
||||
if rule_key not in AVAILABLE_SESSION_RULE_KEYS:
|
||||
return Response().error(f"不支持的规则键: {rule_key}").__dict__
|
||||
|
||||
# 使用 shared preferences 更新规则
|
||||
await sp.session_put(umo, rule_key, rule_value)
|
||||
|
||||
return (
|
||||
Response()
|
||||
.ok({"message": f"规则 {rule_key} 已更新", "umo": umo})
|
||||
.__dict__
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"更新会话规则失败: {e!s}")
|
||||
return Response().error(f"更新会话规则失败: {e!s}").__dict__
|
||||
|
||||
async def delete_session_rule(self):
|
||||
"""删除某个 umo 的自定义规则
|
||||
|
||||
请求体:
|
||||
{
|
||||
"umo": "平台:消息类型:会话ID",
|
||||
"rule_key": "session_service_config" | "session_plugin_config" | ... (可选,不传则删除所有规则)
|
||||
}
|
||||
"""
|
||||
try:
|
||||
data = await request.get_json()
|
||||
umo = data.get("umo")
|
||||
rule_key = data.get("rule_key")
|
||||
|
||||
if not umo:
|
||||
return Response().error("缺少必要参数: umo").__dict__
|
||||
|
||||
if rule_key:
|
||||
# 删除单个规则
|
||||
if rule_key not in AVAILABLE_SESSION_RULE_KEYS:
|
||||
return Response().error(f"不支持的规则键: {rule_key}").__dict__
|
||||
await sp.session_remove(umo, rule_key)
|
||||
return (
|
||||
Response()
|
||||
.error("缺少必要参数: provider_id, provider_type")
|
||||
.ok({"message": f"规则 {rule_key} 已删除", "umo": umo})
|
||||
.__dict__
|
||||
)
|
||||
else:
|
||||
# 删除该 umo 的所有规则
|
||||
await sp.clear_async("umo", umo)
|
||||
return Response().ok({"message": "所有规则已删除", "umo": umo}).__dict__
|
||||
except Exception as e:
|
||||
logger.error(f"删除会话规则失败: {e!s}")
|
||||
return Response().error(f"删除会话规则失败: {e!s}").__dict__
|
||||
|
||||
# 转换 provider_type 字符串为枚举
|
||||
if provider_type == "chat_completion":
|
||||
provider_type_enum = ProviderType.CHAT_COMPLETION
|
||||
elif provider_type == "speech_to_text":
|
||||
provider_type_enum = ProviderType.SPEECH_TO_TEXT
|
||||
elif provider_type == "text_to_speech":
|
||||
provider_type_enum = ProviderType.TEXT_TO_SPEECH
|
||||
async def batch_delete_session_rule(self):
|
||||
"""批量删除多个 umo 的自定义规则
|
||||
|
||||
请求体:
|
||||
{
|
||||
"umos": ["平台:消息类型:会话ID", ...] // umo 列表
|
||||
}
|
||||
"""
|
||||
try:
|
||||
data = await request.get_json()
|
||||
umos = data.get("umos", [])
|
||||
|
||||
if not umos:
|
||||
return Response().error("缺少必要参数: umos").__dict__
|
||||
|
||||
if not isinstance(umos, list):
|
||||
return Response().error("参数 umos 必须是数组").__dict__
|
||||
|
||||
# 批量删除
|
||||
deleted_count = 0
|
||||
failed_umos = []
|
||||
for umo in umos:
|
||||
try:
|
||||
await sp.clear_async("umo", umo)
|
||||
deleted_count += 1
|
||||
except Exception as e:
|
||||
logger.error(f"删除 umo {umo} 的规则失败: {e!s}")
|
||||
failed_umos.append(umo)
|
||||
|
||||
if failed_umos:
|
||||
return (
|
||||
Response()
|
||||
.ok(
|
||||
{
|
||||
"message": f"已删除 {deleted_count} 条规则,{len(failed_umos)} 条删除失败",
|
||||
"deleted_count": deleted_count,
|
||||
"failed_umos": failed_umos,
|
||||
}
|
||||
)
|
||||
.__dict__
|
||||
)
|
||||
else:
|
||||
return (
|
||||
Response()
|
||||
.error(f"不支持的 provider_type: {provider_type}")
|
||||
.__dict__
|
||||
)
|
||||
|
||||
if is_batch:
|
||||
session_ids = data.get("session_ids", [])
|
||||
if not session_ids:
|
||||
return Response().error("缺少必要参数: session_ids").__dict__
|
||||
|
||||
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,
|
||||
)
|
||||
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__
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"更新会话提供商失败: {e!s}\n{traceback.format_exc()}"
|
||||
logger.error(error_msg)
|
||||
return Response().error(f"更新会话提供商失败: {e!s}").__dict__
|
||||
|
||||
async def get_session_plugins(self):
|
||||
"""获取指定会话的插件配置信息"""
|
||||
try:
|
||||
session_id = request.args.get("session_id")
|
||||
|
||||
if not session_id:
|
||||
return Response().error("缺少必要参数: session_id").__dict__
|
||||
|
||||
# 获取所有已激活的插件
|
||||
all_plugins = []
|
||||
plugin_manager = self.core_lifecycle.plugin_manager
|
||||
|
||||
for plugin in plugin_manager.context.get_all_stars():
|
||||
# 只显示已激活的插件,不包括保留插件
|
||||
if plugin.activated and not plugin.reserved:
|
||||
plugin_name = plugin.name or ""
|
||||
plugin_enabled = SessionPluginManager.is_plugin_enabled_for_session(
|
||||
session_id,
|
||||
plugin_name,
|
||||
)
|
||||
|
||||
all_plugins.append(
|
||||
.ok(
|
||||
{
|
||||
"name": plugin_name,
|
||||
"author": plugin.author,
|
||||
"desc": plugin.desc,
|
||||
"enabled": plugin_enabled,
|
||||
},
|
||||
"message": f"已删除 {deleted_count} 条规则",
|
||||
"deleted_count": deleted_count,
|
||||
}
|
||||
)
|
||||
|
||||
return (
|
||||
Response()
|
||||
.ok(
|
||||
{
|
||||
"session_id": session_id,
|
||||
"plugins": all_plugins,
|
||||
},
|
||||
)
|
||||
.__dict__
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"获取会话插件配置失败: {e!s}\n{traceback.format_exc()}"
|
||||
logger.error(error_msg)
|
||||
return Response().error(f"获取会话插件配置失败: {e!s}").__dict__
|
||||
|
||||
async def update_session_plugin(self):
|
||||
"""更新指定会话的插件启停状态"""
|
||||
try:
|
||||
data = await request.get_json()
|
||||
session_id = data.get("session_id")
|
||||
plugin_name = data.get("plugin_name")
|
||||
enabled = data.get("enabled")
|
||||
|
||||
if not session_id:
|
||||
return Response().error("缺少必要参数: session_id").__dict__
|
||||
|
||||
if not plugin_name:
|
||||
return Response().error("缺少必要参数: plugin_name").__dict__
|
||||
|
||||
if enabled is None:
|
||||
return Response().error("缺少必要参数: enabled").__dict__
|
||||
|
||||
# 验证插件是否存在且已激活
|
||||
plugin_manager = self.core_lifecycle.plugin_manager
|
||||
plugin = plugin_manager.context.get_registered_star(plugin_name)
|
||||
|
||||
if not plugin:
|
||||
return Response().error(f"插件 {plugin_name} 不存在").__dict__
|
||||
|
||||
if not plugin.activated:
|
||||
return Response().error(f"插件 {plugin_name} 未激活").__dict__
|
||||
|
||||
if plugin.reserved:
|
||||
return (
|
||||
Response()
|
||||
.error(f"插件 {plugin_name} 是系统保留插件,无法管理")
|
||||
.__dict__
|
||||
)
|
||||
|
||||
# 使用 SessionPluginManager 更新插件状态
|
||||
SessionPluginManager.set_plugin_status_for_session(
|
||||
session_id,
|
||||
plugin_name,
|
||||
enabled,
|
||||
)
|
||||
|
||||
return (
|
||||
Response()
|
||||
.ok(
|
||||
{
|
||||
"message": f"插件 {plugin_name} 已{'启用' if enabled else '禁用'}",
|
||||
"session_id": session_id,
|
||||
"plugin_name": plugin_name,
|
||||
"enabled": enabled,
|
||||
},
|
||||
)
|
||||
.__dict__
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"更新会话插件状态失败: {e!s}\n{traceback.format_exc()}"
|
||||
logger.error(error_msg)
|
||||
return Response().error(f"更新会话插件状态失败: {e!s}").__dict__
|
||||
logger.error(f"批量删除会话规则失败: {e!s}")
|
||||
return Response().error(f"批量删除会话规则失败: {e!s}").__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 list_umos(self):
|
||||
"""列出所有有对话记录的 umo,从 Conversations 表中找
|
||||
|
||||
async def update_session_llm(self):
|
||||
"""更新指定会话的LLM启停状态,支持批量操作"""
|
||||
仅返回 umo 字符串列表,用于用户在创建规则时选择 umo
|
||||
"""
|
||||
try:
|
||||
data = await request.get_json()
|
||||
is_batch = data.get("is_batch", False)
|
||||
enabled = data.get("enabled")
|
||||
|
||||
if enabled is None:
|
||||
return Response().error("缺少必要参数: enabled").__dict__
|
||||
|
||||
if is_batch:
|
||||
session_ids = data.get("session_ids", [])
|
||||
if not session_ids:
|
||||
return Response().error("缺少必要参数: session_ids").__dict__
|
||||
|
||||
result = await self._handle_batch_operation(
|
||||
session_ids,
|
||||
self._update_single_session_llm,
|
||||
f"{'启用' if enabled else '禁用'}LLM",
|
||||
enabled=enabled,
|
||||
# 从 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)
|
||||
)
|
||||
return result
|
||||
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__
|
||||
)
|
||||
umos = [row[0] for row in result.fetchall()]
|
||||
|
||||
return Response().ok({"umos": umos}).__dict__
|
||||
except Exception as e:
|
||||
error_msg = f"更新会话LLM状态失败: {e!s}\n{traceback.format_exc()}"
|
||||
logger.error(error_msg)
|
||||
return Response().error(f"更新会话LLM状态失败: {e!s}").__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启停状态,支持批量操作"""
|
||||
try:
|
||||
data = await request.get_json()
|
||||
is_batch = data.get("is_batch", False)
|
||||
enabled = data.get("enabled")
|
||||
|
||||
if enabled is None:
|
||||
return Response().error("缺少必要参数: enabled").__dict__
|
||||
|
||||
if is_batch:
|
||||
session_ids = data.get("session_ids", [])
|
||||
if not session_ids:
|
||||
return Response().error("缺少必要参数: session_ids").__dict__
|
||||
|
||||
result = await self._handle_batch_operation(
|
||||
session_ids,
|
||||
self._update_single_session_tts,
|
||||
f"{'启用' if enabled else '禁用'}TTS",
|
||||
enabled=enabled,
|
||||
)
|
||||
return result
|
||||
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__
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"更新会话TTS状态失败: {e!s}\n{traceback.format_exc()}"
|
||||
logger.error(error_msg)
|
||||
return Response().error(f"更新会话TTS状态失败: {e!s}").__dict__
|
||||
|
||||
async def update_session_name(self):
|
||||
"""更新指定会话的自定义名称"""
|
||||
try:
|
||||
data = await request.get_json()
|
||||
session_id = data.get("session_id")
|
||||
custom_name = data.get("custom_name", "")
|
||||
|
||||
if not session_id:
|
||||
return Response().error("缺少必要参数: session_id").__dict__
|
||||
|
||||
# 使用 SessionServiceManager 更新会话名称
|
||||
SessionServiceManager.set_session_custom_name(session_id, custom_name)
|
||||
|
||||
return (
|
||||
Response()
|
||||
.ok(
|
||||
{
|
||||
"message": f"会话名称已更新为: {custom_name if custom_name.strip() else '已清除自定义名称'}",
|
||||
"session_id": session_id,
|
||||
"custom_name": custom_name,
|
||||
"display_name": SessionServiceManager.get_session_display_name(
|
||||
session_id,
|
||||
),
|
||||
},
|
||||
)
|
||||
.__dict__
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"更新会话名称失败: {e!s}\n{traceback.format_exc()}"
|
||||
logger.error(error_msg)
|
||||
return Response().error(f"更新会话名称失败: {e!s}").__dict__
|
||||
|
||||
async def update_session_status(self):
|
||||
"""更新指定会话的整体启停状态"""
|
||||
try:
|
||||
data = await request.get_json()
|
||||
session_id = data.get("session_id")
|
||||
session_enabled = data.get("session_enabled")
|
||||
|
||||
if not session_id:
|
||||
return Response().error("缺少必要参数: session_id").__dict__
|
||||
|
||||
if session_enabled is None:
|
||||
return Response().error("缺少必要参数: session_enabled").__dict__
|
||||
|
||||
# 使用 SessionServiceManager 更新会话整体状态
|
||||
SessionServiceManager.set_session_status(session_id, session_enabled)
|
||||
|
||||
return (
|
||||
Response()
|
||||
.ok(
|
||||
{
|
||||
"message": f"会话整体状态已更新为: {'启用' if session_enabled else '禁用'}",
|
||||
"session_id": session_id,
|
||||
"session_enabled": session_enabled,
|
||||
},
|
||||
)
|
||||
.__dict__
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"更新会话整体状态失败: {e!s}\n{traceback.format_exc()}"
|
||||
logger.error(error_msg)
|
||||
return Response().error(f"更新会话整体状态失败: {e!s}").__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} 的对话失败: {e!s}")
|
||||
|
||||
# 2. 清除会话的偏好设置数据(清空该会话的所有配置)
|
||||
try:
|
||||
await sp.clear_async("umo", session_id)
|
||||
except Exception as e:
|
||||
logger.warning(f"清除会话 {session_id} 的偏好设置失败: {e!s}")
|
||||
|
||||
return (
|
||||
Response()
|
||||
.ok(
|
||||
{
|
||||
"message": f"会话 {session_id} 及其相关所有对话数据已成功删除",
|
||||
"session_id": session_id,
|
||||
},
|
||||
)
|
||||
.__dict__
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"删除会话失败: {e!s}\n{traceback.format_exc()}"
|
||||
logger.error(error_msg)
|
||||
return Response().error(f"删除会话失败: {e!s}").__dict__
|
||||
logger.error(f"获取 UMO 列表失败: {e!s}")
|
||||
return Response().error(f"获取 UMO 列表失败: {e!s}").__dict__
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
:align-tabs="$vuetify.display.mobile ? 'left' : 'start'" color="deep-purple-accent-4" class="config-tabs">
|
||||
<v-tab v-for="(val, key, index) in metadata" :key="index" :value="index"
|
||||
style="font-weight: 1000; font-size: 15px">
|
||||
{{ metadata[key]['name'] }}
|
||||
{{ tm(metadata[key]['name']) }}
|
||||
</v-tab>
|
||||
</v-tabs>
|
||||
<v-tabs-window v-model="tab" class="config-tabs-window" :style="readonly ? 'pointer-events: none; opacity: 0.6;' : ''">
|
||||
@@ -59,7 +59,17 @@ export default {
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
const { tm } = useModuleI18n('features/config');
|
||||
const { tm: tmConfig } = useModuleI18n('features/config');
|
||||
const { tm: tmMetadata } = useModuleI18n('features/config-metadata');
|
||||
|
||||
const tm = (key) => {
|
||||
const metadataResult = tmMetadata(key);
|
||||
if (!metadataResult.startsWith('[MISSING:') && !metadataResult.startsWith('[INVALID:')) {
|
||||
return metadataResult;
|
||||
}
|
||||
return tmConfig(key);
|
||||
};
|
||||
|
||||
return {
|
||||
tm
|
||||
};
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
</v-col>
|
||||
<v-col v-if="Object.keys(getTemplatesByType(tabType)).length === 0" cols="12">
|
||||
<v-alert type="info" variant="tonal">
|
||||
{{ t('dialogs.addProvider.noTemplates') }}
|
||||
{{ tm('dialogs.addProvider.noTemplates') }}
|
||||
</v-alert>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
@@ -8,7 +8,7 @@ import PersonaSelector from './PersonaSelector.vue'
|
||||
import KnowledgeBaseSelector from './KnowledgeBaseSelector.vue'
|
||||
import PluginSetSelector from './PluginSetSelector.vue'
|
||||
import T2ITemplateEditor from './T2ITemplateEditor.vue'
|
||||
import { useI18n } from '@/i18n/composables'
|
||||
import { useI18n, useModuleI18n } from '@/i18n/composables'
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
@@ -27,6 +27,34 @@ const props = defineProps({
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
const { tm } = useModuleI18n('features/config-metadata')
|
||||
|
||||
// 翻译器函数 - 如果是国际化键则翻译,否则原样返回
|
||||
const translateIfKey = (value) => {
|
||||
if (!value || typeof value !== 'string') return value
|
||||
return tm(value)
|
||||
}
|
||||
|
||||
// 处理labels翻译 - labels可以是数组或国际化键
|
||||
const getTranslatedLabels = (itemMeta) => {
|
||||
if (!itemMeta?.labels) return null
|
||||
|
||||
// 如果labels是字符串(国际化键)
|
||||
if (typeof itemMeta.labels === 'string') {
|
||||
const translatedLabels = tm(itemMeta.labels)
|
||||
// 如果翻译成功且是数组,返回翻译结果
|
||||
if (Array.isArray(translatedLabels)) {
|
||||
return translatedLabels
|
||||
}
|
||||
}
|
||||
|
||||
// 如果labels是数组,直接返回
|
||||
if (Array.isArray(itemMeta.labels)) {
|
||||
return itemMeta.labels
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
const dialog = ref(false)
|
||||
const currentEditingKey = ref('')
|
||||
@@ -158,11 +186,11 @@ function getSpecialSubtype(value) {
|
||||
rounded="md" variant="outlined">
|
||||
<v-card-text class="config-section" v-if="metadata[metadataKey]?.type === 'object'" style="padding-bottom: 8px;">
|
||||
<v-list-item-title class="config-title">
|
||||
{{ metadata[metadataKey]?.description }}
|
||||
{{ translateIfKey(metadata[metadataKey]?.description) }}
|
||||
</v-list-item-title>
|
||||
<v-list-item-subtitle class="config-hint">
|
||||
<span v-if="metadata[metadataKey]?.obvious_hint && metadata[metadataKey]?.hint" class="important-hint">‼️</span>
|
||||
{{ metadata[metadataKey]?.hint }}
|
||||
{{ translateIfKey(metadata[metadataKey]?.hint) }}
|
||||
</v-list-item-subtitle>
|
||||
</v-card-text>
|
||||
|
||||
@@ -176,13 +204,13 @@ function getSpecialSubtype(value) {
|
||||
<v-col cols="12" sm="6" class="property-info">
|
||||
<v-list-item density="compact">
|
||||
<v-list-item-title class="property-name">
|
||||
{{ itemMeta?.description || itemKey }}
|
||||
{{ translateIfKey(itemMeta?.description) || itemKey }}
|
||||
<span class="property-key">({{ itemKey }})</span>
|
||||
</v-list-item-title>
|
||||
|
||||
<v-list-item-subtitle class="property-hint">
|
||||
<span v-if="itemMeta?.obvious_hint && itemMeta?.hint" class="important-hint">‼️</span>
|
||||
{{ itemMeta?.hint }}
|
||||
{{ translateIfKey(itemMeta?.hint) }}
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
</v-col>
|
||||
@@ -190,7 +218,12 @@ function getSpecialSubtype(value) {
|
||||
<div class="w-100" v-if="!itemMeta?._special">
|
||||
<!-- Select input for JSON selector -->
|
||||
<v-select v-if="itemMeta?.options" v-model="createSelectorModel(itemKey).value"
|
||||
:items="itemMeta?.labels ? itemMeta.options.map((value, index) => ({ title: itemMeta.labels[index] || value, value: value })) : itemMeta.options"
|
||||
:items="(() => {
|
||||
const labels = getTranslatedLabels(itemMeta);
|
||||
return labels
|
||||
? itemMeta.options.map((value, index) => ({ title: labels[index] || value, value: value }))
|
||||
: itemMeta.options;
|
||||
})()"
|
||||
:disabled="itemMeta?.readonly" density="compact" variant="outlined"
|
||||
class="config-field" hide-details></v-select>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div style="flex: 1; min-width: 0; overflow: hidden;">
|
||||
<span v-if="!modelValue || (Array.isArray(modelValue) && modelValue.length === 0)"
|
||||
style="color: rgb(var(--v-theme-primaryText));">
|
||||
未选择
|
||||
{{ tm('knowledgeBaseSelector.notSelected') }}
|
||||
</span>
|
||||
<div v-else class="d-flex flex-wrap gap-1">
|
||||
<v-chip
|
||||
@@ -28,7 +28,7 @@
|
||||
<v-dialog v-model="dialog" max-width="600px">
|
||||
<v-card>
|
||||
<v-card-title class="text-h3 py-4" style="font-weight: normal;">
|
||||
选择知识库
|
||||
{{ tm('knowledgeBaseSelector.dialogTitle') }}
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text class="pa-0" style="max-height: 400px; overflow-y: auto;">
|
||||
@@ -50,9 +50,9 @@
|
||||
</template>
|
||||
<v-list-item-title>{{ kb.kb_name }}</v-list-item-title>
|
||||
<v-list-item-subtitle>
|
||||
{{ kb.description || '无描述' }}
|
||||
<span v-if="kb.doc_count !== undefined"> - {{ kb.doc_count }} 个文档</span>
|
||||
<span v-if="kb.chunk_count !== undefined"> - {{ kb.chunk_count }} 个块</span>
|
||||
{{ kb.description || tm('knowledgeBaseSelector.noDescription') }}
|
||||
<span v-if="kb.doc_count !== undefined"> - {{ tm('knowledgeBaseSelector.documentCount', { count: kb.doc_count }) }}</span>
|
||||
<span v-if="kb.chunk_count !== undefined"> - {{ tm('knowledgeBaseSelector.chunkCount', { count: kb.chunk_count }) }}</span>
|
||||
</v-list-item-subtitle>
|
||||
|
||||
<template v-slot:append>
|
||||
@@ -68,9 +68,9 @@
|
||||
<!-- 当没有知识库时显示创建提示 -->
|
||||
<div v-if="knowledgeBaseList.length === 0" class="text-center py-8">
|
||||
<v-icon size="64" color="grey-lighten-1">mdi-database-off</v-icon>
|
||||
<p class="text-grey mt-4 mb-4">暂无知识库</p>
|
||||
<p class="text-grey mt-4 mb-4">{{ tm('knowledgeBaseSelector.noKnowledgeBases') }}</p>
|
||||
<v-btn color="primary" variant="tonal" @click="goToKnowledgeBasePage">
|
||||
创建知识库
|
||||
{{ tm('knowledgeBaseSelector.createKnowledgeBase') }}
|
||||
</v-btn>
|
||||
</div>
|
||||
</v-list>
|
||||
@@ -78,14 +78,14 @@
|
||||
|
||||
<v-card-actions class="pa-4">
|
||||
<div v-if="selectedKnowledgeBases.length > 0" class="text-caption text-grey">
|
||||
已选择 {{ selectedKnowledgeBases.length }} 个知识库
|
||||
{{ tm('knowledgeBaseSelector.selectedCount', { count: selectedKnowledgeBases.length }) }}
|
||||
</div>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn variant="text" @click="cancelSelection">取消</v-btn>
|
||||
<v-btn variant="text" @click="cancelSelection">{{ tm('knowledgeBaseSelector.cancelSelection') }}</v-btn>
|
||||
<v-btn
|
||||
color="primary"
|
||||
@click="confirmSelection">
|
||||
确认选择
|
||||
{{ tm('knowledgeBaseSelector.confirmSelection') }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
@@ -96,6 +96,7 @@
|
||||
import { ref, watch } from 'vue'
|
||||
import axios from 'axios'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useModuleI18n } from '@/i18n/composables'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
@@ -110,6 +111,7 @@ const props = defineProps({
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const router = useRouter()
|
||||
const { tm } = useModuleI18n('core.shared')
|
||||
|
||||
const dialog = ref(false)
|
||||
const knowledgeBaseList = ref([])
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
<div class="d-flex align-center justify-space-between mb-2">
|
||||
<div class="flex-grow-1">
|
||||
<span v-if="!modelValue || modelValue.length === 0" style="color: rgb(var(--v-theme-primaryText));">
|
||||
未启用任何插件
|
||||
{{ tm('pluginSetSelector.notSelected') }}
|
||||
</span>
|
||||
<span v-else-if="isAllPlugins" style="color: rgb(var(--v-theme-primaryText));">
|
||||
启用所有插件 (*)
|
||||
{{ tm('pluginSetSelector.allPlugins') }}
|
||||
</span>
|
||||
<span v-else style="color: rgb(var(--v-theme-primaryText));">
|
||||
已选择 {{ modelValue.length }} 个插件
|
||||
{{ tm('pluginSetSelector.selectedCount', { count: modelValue.length }) }}
|
||||
</span>
|
||||
</div>
|
||||
<v-btn size="small" color="primary" variant="tonal" @click="openDialog">
|
||||
@@ -23,7 +23,7 @@
|
||||
<v-dialog v-model="dialog" max-width="700px">
|
||||
<v-card>
|
||||
<v-card-title class="text-h3 py-4" style="font-weight: normal;">
|
||||
选择插件集合
|
||||
{{ tm('pluginSetSelector.dialogTitle') }}
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text class="pa-4">
|
||||
@@ -34,17 +34,17 @@
|
||||
<v-radio-group v-model="selectionMode" class="mb-4" hide-details>
|
||||
<v-radio
|
||||
value="all"
|
||||
label="启用所有插件"
|
||||
:label="tm('pluginSetSelector.enableAll')"
|
||||
color="primary"
|
||||
></v-radio>
|
||||
<v-radio
|
||||
value="none"
|
||||
label="不启用任何插件"
|
||||
:label="tm('pluginSetSelector.enableNone')"
|
||||
color="primary"
|
||||
></v-radio>
|
||||
<v-radio
|
||||
value="custom"
|
||||
label="自定义选择"
|
||||
:label="tm('pluginSetSelector.customSelect')"
|
||||
color="primary"
|
||||
></v-radio>
|
||||
</v-radio-group>
|
||||
@@ -68,21 +68,21 @@
|
||||
|
||||
<v-list-item-title>{{ plugin.name }}</v-list-item-title>
|
||||
<v-list-item-subtitle>
|
||||
{{ plugin.desc || '无描述' }}
|
||||
{{ plugin.desc || tm('pluginSetSelector.noDescription') }}
|
||||
<v-chip v-if="!plugin.activated" size="x-small" color="grey" class="ml-1">
|
||||
未激活
|
||||
{{ tm('pluginSetSelector.notActivated') }}
|
||||
</v-chip>
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
|
||||
<div class="pl-8 pt-2">
|
||||
<small>*不显示系统插件和已经在插件页禁用的插件。</small>
|
||||
<small>{{ tm('pluginSetSelector.note') }}</small>
|
||||
</div>
|
||||
</v-list>
|
||||
|
||||
<div v-else class="text-center py-8">
|
||||
<v-icon size="64" color="grey-lighten-1">mdi-puzzle-outline</v-icon>
|
||||
<p class="text-grey mt-4">暂无可用的插件</p>
|
||||
<p class="text-grey mt-4">{{ tm('pluginSetSelector.noPlugins') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -90,11 +90,11 @@
|
||||
|
||||
<v-card-actions class="pa-4">
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn variant="text" @click="cancelSelection">取消</v-btn>
|
||||
<v-btn variant="text" @click="cancelSelection">{{ tm('pluginSetSelector.cancelSelection') }}</v-btn>
|
||||
<v-btn
|
||||
color="primary"
|
||||
@click="confirmSelection">
|
||||
确认选择
|
||||
{{ tm('pluginSetSelector.confirmSelection') }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
@@ -104,6 +104,7 @@
|
||||
<script setup>
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import axios from 'axios'
|
||||
import { useModuleI18n } from '@/i18n/composables'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
@@ -121,6 +122,7 @@ const props = defineProps({
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const { tm } = useModuleI18n('core.shared')
|
||||
|
||||
const dialog = ref(false)
|
||||
const pluginList = ref([])
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="d-flex align-center justify-space-between">
|
||||
<span v-if="!modelValue" style="color: rgb(var(--v-theme-primaryText));">
|
||||
未选择
|
||||
{{ tm('providerSelector.notSelected') }}
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ modelValue }}
|
||||
@@ -15,7 +15,7 @@
|
||||
<v-dialog v-model="dialog" max-width="600px">
|
||||
<v-card>
|
||||
<v-card-title class="text-h3 py-4" style="font-weight: normal;">
|
||||
选择提供商
|
||||
{{ tm('providerSelector.dialogTitle') }}
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text class="pa-0" style="max-height: 400px; overflow-y: auto;">
|
||||
@@ -30,8 +30,8 @@
|
||||
:active="selectedProvider === ''"
|
||||
rounded="md"
|
||||
class="ma-1">
|
||||
<v-list-item-title>不选择</v-list-item-title>
|
||||
<v-list-item-subtitle>清除当前选择</v-list-item-subtitle>
|
||||
<v-list-item-title>{{ tm('providerSelector.clearSelection') }}</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ tm('providerSelector.clearSelectionSubtitle') }}</v-list-item-subtitle>
|
||||
|
||||
<template v-slot:append>
|
||||
<v-icon v-if="selectedProvider === ''" color="primary">mdi-check-circle</v-icon>
|
||||
@@ -50,7 +50,7 @@
|
||||
class="ma-1">
|
||||
<v-list-item-title>{{ provider.id }}</v-list-item-title>
|
||||
<v-list-item-subtitle>
|
||||
{{ provider.type || provider.provider_type || '未知类型' }}
|
||||
{{ provider.type || provider.provider_type || tm('providerSelector.unknownType') }}
|
||||
<span v-if="provider.model_config?.model">- {{ provider.model_config.model }}</span>
|
||||
</v-list-item-subtitle>
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
|
||||
<div v-else-if="!loading && providerList.length === 0" class="text-center py-8">
|
||||
<v-icon size="64" color="grey-lighten-1">mdi-api-off</v-icon>
|
||||
<p class="text-grey mt-4">暂无可用的提供商</p>
|
||||
<p class="text-grey mt-4">{{ tm('providerSelector.noProviders') }}</p>
|
||||
</div>
|
||||
</v-card-text>
|
||||
|
||||
@@ -70,11 +70,11 @@
|
||||
|
||||
<v-card-actions class="pa-4">
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn variant="text" @click="cancelSelection">取消</v-btn>
|
||||
<v-btn variant="text" @click="cancelSelection">{{ tm('providerSelector.cancelSelection') }}</v-btn>
|
||||
<v-btn
|
||||
color="primary"
|
||||
@click="confirmSelection">
|
||||
确认选择
|
||||
{{ tm('providerSelector.confirmSelection') }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
@@ -84,6 +84,7 @@
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue'
|
||||
import axios from 'axios'
|
||||
import { useModuleI18n } from '@/i18n/composables'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
@@ -105,6 +106,7 @@ const props = defineProps({
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const { tm } = useModuleI18n('core.shared')
|
||||
|
||||
const dialog = ref(false)
|
||||
const providerList = ref([])
|
||||
|
||||
@@ -33,6 +33,7 @@ export class I18nLoader {
|
||||
{ name: 'core/status', path: 'core/status.json' },
|
||||
{ name: 'core/navigation', path: 'core/navigation.json' },
|
||||
{ name: 'core/header', path: 'core/header.json' },
|
||||
{ name: 'core/shared', path: 'core/shared.json' },
|
||||
|
||||
// 功能模块
|
||||
{ name: 'features/chat', path: 'features/chat.json' },
|
||||
@@ -43,6 +44,7 @@ export class I18nLoader {
|
||||
{ name: 'features/provider', path: 'features/provider.json' },
|
||||
{ name: 'features/platform', path: 'features/platform.json' },
|
||||
{ name: 'features/config', path: 'features/config.json' },
|
||||
{ name: 'features/config-metadata', path: 'features/config-metadata.json' },
|
||||
{ name: 'features/console', path: 'features/console.json' },
|
||||
{ name: 'features/about', path: 'features/about.json' },
|
||||
{ name: 'features/settings', path: 'features/settings.json' },
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"chat": "Chat",
|
||||
"extension": "Extensions",
|
||||
"conversation": "Conversations",
|
||||
"sessionManagement": "Session Management",
|
||||
"sessionManagement": "Custom Rules",
|
||||
"console": "Console",
|
||||
"alkaid": "Alkaid Lab",
|
||||
"knowledgeBase": "Knowledge Base",
|
||||
|
||||
45
dashboard/src/i18n/locales/en-US/core/shared.json
Normal file
45
dashboard/src/i18n/locales/en-US/core/shared.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"knowledgeBaseSelector": {
|
||||
"notSelected": "Not selected",
|
||||
"buttonText": "Select Knowledge Base...",
|
||||
"dialogTitle": "Select Knowledge Base",
|
||||
"loading": "Loading...",
|
||||
"noKnowledgeBases": "No knowledge bases available",
|
||||
"createKnowledgeBase": "Create Knowledge Base",
|
||||
"selectedCount": "{count} knowledge base(s) selected",
|
||||
"confirmSelection": "Confirm Selection",
|
||||
"cancelSelection": "Cancel",
|
||||
"noDescription": "No description",
|
||||
"documentCount": "{count} document(s)",
|
||||
"chunkCount": "{count} chunk(s)"
|
||||
},
|
||||
"pluginSetSelector": {
|
||||
"notSelected": "No plugins enabled",
|
||||
"allPlugins": "All plugins enabled (*)",
|
||||
"selectedCount": "{count} plugin(s) selected",
|
||||
"buttonText": "Select Plugin Set...",
|
||||
"dialogTitle": "Select Plugin Set",
|
||||
"loading": "Loading...",
|
||||
"enableAll": "Enable all plugins",
|
||||
"enableNone": "Disable all plugins",
|
||||
"customSelect": "Custom selection",
|
||||
"noPlugins": "No plugins available",
|
||||
"confirmSelection": "Confirm Selection",
|
||||
"cancelSelection": "Cancel",
|
||||
"noDescription": "No description",
|
||||
"notActivated": "Not activated",
|
||||
"note": "*System plugins and disabled plugins are not shown."
|
||||
},
|
||||
"providerSelector": {
|
||||
"notSelected": "Not selected",
|
||||
"buttonText": "Select Provider...",
|
||||
"dialogTitle": "Select Provider",
|
||||
"loading": "Loading...",
|
||||
"noProviders": "No providers available",
|
||||
"confirmSelection": "Confirm Selection",
|
||||
"cancelSelection": "Cancel",
|
||||
"clearSelection": "None",
|
||||
"clearSelectionSubtitle": "Clear current selection",
|
||||
"unknownType": "Unknown type"
|
||||
}
|
||||
}
|
||||
452
dashboard/src/i18n/locales/en-US/features/config-metadata.json
Normal file
452
dashboard/src/i18n/locales/en-US/features/config-metadata.json
Normal file
@@ -0,0 +1,452 @@
|
||||
{
|
||||
"ai_group": {
|
||||
"name": "AI",
|
||||
"agent_runner": {
|
||||
"description": "Agent Runner",
|
||||
"hint": "Select the runner for AI conversations. Defaults to AstrBot's built-in Agent runner, which supports knowledge base, persona, and tool calling features. You don't need to modify this section unless you plan to integrate third-party Agent runners like Dify or Coze.",
|
||||
"provider_settings": {
|
||||
"enable": {
|
||||
"description": "Enable",
|
||||
"hint": "Master switch for AI conversations"
|
||||
},
|
||||
"agent_runner_type": {
|
||||
"description": "Runner",
|
||||
"labels": ["Built-in Agent", "Dify", "Coze", "Alibaba Cloud Bailian Application"]
|
||||
},
|
||||
"coze_agent_runner_provider_id": {
|
||||
"description": "Coze Agent Runner Provider ID"
|
||||
},
|
||||
"dify_agent_runner_provider_id": {
|
||||
"description": "Dify Agent Runner Provider ID"
|
||||
},
|
||||
"dashscope_agent_runner_provider_id": {
|
||||
"description": "Alibaba Cloud Bailian Application Agent Runner Provider ID"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ai": {
|
||||
"description": "Model",
|
||||
"hint": "When using non-built-in Agent runners, the default chat model and default image caption model may not take effect, but some plugins rely on these settings to invoke AI capabilities.",
|
||||
"provider_settings": {
|
||||
"default_provider_id": {
|
||||
"description": "Default Chat Model",
|
||||
"hint": "Uses the first model when left empty"
|
||||
},
|
||||
"default_image_caption_provider_id": {
|
||||
"description": "Default Image Caption Model",
|
||||
"hint": "Leave empty to disable; useful for non-multimodal models"
|
||||
},
|
||||
"image_caption_prompt": {
|
||||
"description": "Image Caption Prompt"
|
||||
}
|
||||
},
|
||||
"provider_stt_settings": {
|
||||
"enable": {
|
||||
"description": "Enable Speech-to-Text",
|
||||
"hint": "Master switch for STT"
|
||||
},
|
||||
"provider_id": {
|
||||
"description": "Default Speech-to-Text Model",
|
||||
"hint": "Users can also select session-specific STT models using the /provider command."
|
||||
}
|
||||
},
|
||||
"provider_tts_settings": {
|
||||
"enable": {
|
||||
"description": "Enable Text-to-Speech",
|
||||
"hint": "Master switch for TTS"
|
||||
},
|
||||
"provider_id": {
|
||||
"description": "Default Text-to-Speech Model"
|
||||
}
|
||||
}
|
||||
},
|
||||
"persona": {
|
||||
"description": "Persona",
|
||||
"provider_settings": {
|
||||
"default_personality": {
|
||||
"description": "Default Persona"
|
||||
}
|
||||
}
|
||||
},
|
||||
"knowledgebase": {
|
||||
"description": "Knowledge Base",
|
||||
"kb_names": {
|
||||
"description": "Knowledge Base List",
|
||||
"hint": "Supports multiple selections"
|
||||
},
|
||||
"kb_fusion_top_k": {
|
||||
"description": "Fusion Search Results Count",
|
||||
"hint": "Number of results returned after fusing search results from multiple knowledge bases"
|
||||
},
|
||||
"kb_final_top_k": {
|
||||
"description": "Final Results Count",
|
||||
"hint": "Number of results retrieved from the knowledge base. Higher values may provide more relevant information but could also introduce noise. Adjust based on actual needs"
|
||||
},
|
||||
"kb_agentic_mode": {
|
||||
"description": "Agentic Knowledge Base Retrieval",
|
||||
"hint": "When enabled, knowledge base retrieval becomes an LLM Tool, allowing the model to autonomously decide when to query the knowledge base. Requires the model to support function calling."
|
||||
}
|
||||
},
|
||||
"websearch": {
|
||||
"description": "Web Search",
|
||||
"provider_settings": {
|
||||
"web_search": {
|
||||
"description": "Enable Web Search"
|
||||
},
|
||||
"websearch_provider": {
|
||||
"description": "Web Search Provider"
|
||||
},
|
||||
"websearch_tavily_key": {
|
||||
"description": "Tavily API Key",
|
||||
"hint": "Multiple keys can be added for rotation."
|
||||
},
|
||||
"websearch_baidu_app_builder_key": {
|
||||
"description": "Baidu Qianfan Smart Cloud APP Builder API Key",
|
||||
"hint": "Reference: https://console.bce.baidu.com/iam/#/iam/apikey/list"
|
||||
},
|
||||
"web_search_link": {
|
||||
"description": "Display Source Citations"
|
||||
}
|
||||
}
|
||||
},
|
||||
"others": {
|
||||
"description": "Other Settings",
|
||||
"provider_settings": {
|
||||
"display_reasoning_text": {
|
||||
"description": "Display Reasoning Content"
|
||||
},
|
||||
"identifier": {
|
||||
"description": "User Identification",
|
||||
"hint": "When enabled, user ID information will be included in the prompt."
|
||||
},
|
||||
"group_name_display": {
|
||||
"description": "Display Group Name",
|
||||
"hint": "When enabled, group name information will be included in the prompt on supported platforms (OneBot v11)."
|
||||
},
|
||||
"datetime_system_prompt": {
|
||||
"description": "Real-world Time Awareness",
|
||||
"hint": "When enabled, current time information will be appended to the system prompt."
|
||||
},
|
||||
"show_tool_use_status": {
|
||||
"description": "Output Function Call Status"
|
||||
},
|
||||
"max_agent_step": {
|
||||
"description": "Maximum Tool Call Rounds"
|
||||
},
|
||||
"tool_call_timeout": {
|
||||
"description": "Tool Call Timeout (seconds)"
|
||||
},
|
||||
"streaming_response": {
|
||||
"description": "Streaming Output"
|
||||
},
|
||||
"unsupported_streaming_strategy": {
|
||||
"description": "Platforms Without Streaming Support",
|
||||
"hint": "Select the handling method for platforms that don't support streaming responses. Real-time segmented reply sends content immediately when the system detects segment points like punctuation during streaming reception",
|
||||
"labels": ["Real-time Segmented Reply", "Disable Streaming Response"]
|
||||
},
|
||||
"max_context_length": {
|
||||
"description": "Maximum Conversation Rounds",
|
||||
"hint": "Discards the oldest parts when this count is exceeded. One conversation round counts as 1, -1 means unlimited"
|
||||
},
|
||||
"dequeue_context_length": {
|
||||
"description": "Dequeue Conversation Rounds",
|
||||
"hint": "Number of conversation rounds to discard at once when maximum context length is exceeded"
|
||||
},
|
||||
"wake_prefix": {
|
||||
"description": "Additional LLM Chat Wake Prefix",
|
||||
"hint": "If the wake prefix is / and the additional chat wake prefix is chat, then /chat is required to trigger LLM requests"
|
||||
},
|
||||
"prompt_prefix": {
|
||||
"description": "User Prompt",
|
||||
"hint": "You can use {{prompt}} as a placeholder for user input. If no placeholder is provided, it will be added before the user input."
|
||||
}
|
||||
},
|
||||
"provider_tts_settings": {
|
||||
"dual_output": {
|
||||
"description": "Output Both Voice and Text When TTS is Enabled"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"platform_group": {
|
||||
"name": "Platform",
|
||||
"general": {
|
||||
"description": "General",
|
||||
"admins_id": {
|
||||
"description": "Administrator IDs"
|
||||
},
|
||||
"platform_settings": {
|
||||
"unique_session": {
|
||||
"description": "Isolate Sessions",
|
||||
"hint": "When enabled, group members have independent contexts."
|
||||
},
|
||||
"friend_message_needs_wake_prefix": {
|
||||
"description": "Private Messages Require Wake Word"
|
||||
},
|
||||
"reply_prefix": {
|
||||
"description": "Reply Text Prefix"
|
||||
},
|
||||
"reply_with_mention": {
|
||||
"description": "Mention Sender in Reply"
|
||||
},
|
||||
"reply_with_quote": {
|
||||
"description": "Quote Sender's Message in Reply"
|
||||
},
|
||||
"forward_threshold": {
|
||||
"description": "Forward Message Word Count Threshold"
|
||||
},
|
||||
"empty_mention_waiting": {
|
||||
"description": "Trigger Waiting on Mention-only Messages"
|
||||
}
|
||||
},
|
||||
"wake_prefix": {
|
||||
"description": "Wake Word"
|
||||
}
|
||||
},
|
||||
"whitelist": {
|
||||
"description": "Whitelist",
|
||||
"platform_settings": {
|
||||
"enable_id_white_list": {
|
||||
"description": "Enable Whitelist",
|
||||
"hint": "When enabled, only sessions in the whitelist will be responded to."
|
||||
},
|
||||
"id_whitelist": {
|
||||
"description": "Whitelist ID List",
|
||||
"hint": "Use /sid to get IDs."
|
||||
},
|
||||
"id_whitelist_log": {
|
||||
"description": "Output Logs",
|
||||
"hint": "When enabled, INFO level logs will be output when a message doesn't pass the whitelist."
|
||||
},
|
||||
"wl_ignore_admin_on_group": {
|
||||
"description": "Administrator Group Messages Bypass ID Whitelist"
|
||||
},
|
||||
"wl_ignore_admin_on_friend": {
|
||||
"description": "Administrator Private Messages Bypass ID Whitelist"
|
||||
}
|
||||
}
|
||||
},
|
||||
"rate_limit": {
|
||||
"description": "Rate Limiting",
|
||||
"platform_settings": {
|
||||
"rate_limit": {
|
||||
"time": {
|
||||
"description": "Message Rate Limit Time (seconds)"
|
||||
},
|
||||
"count": {
|
||||
"description": "Message Rate Limit Count"
|
||||
},
|
||||
"strategy": {
|
||||
"description": "Rate Limit Strategy"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"content_safety": {
|
||||
"description": "Content Safety",
|
||||
"content_safety": {
|
||||
"also_use_in_response": {
|
||||
"description": "Also Check Model Response Content"
|
||||
},
|
||||
"baidu_aip": {
|
||||
"enable": {
|
||||
"description": "Use Baidu Content Safety Moderation",
|
||||
"hint": "You need to manually install the baidu-aip library."
|
||||
},
|
||||
"app_id": {
|
||||
"description": "App ID"
|
||||
},
|
||||
"api_key": {
|
||||
"description": "API Key"
|
||||
},
|
||||
"secret_key": {
|
||||
"description": "Secret Key"
|
||||
}
|
||||
},
|
||||
"internal_keywords": {
|
||||
"enable": {
|
||||
"description": "Keyword Check"
|
||||
},
|
||||
"extra_keywords": {
|
||||
"description": "Additional Keywords",
|
||||
"hint": "Additional keyword blocklist, supports regular expressions."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"t2i": {
|
||||
"description": "Text-to-Image",
|
||||
"t2i": {
|
||||
"description": "Text-to-Image Output"
|
||||
},
|
||||
"t2i_word_threshold": {
|
||||
"description": "Text-to-Image Word Count Threshold"
|
||||
}
|
||||
},
|
||||
"others": {
|
||||
"description": "Other Settings",
|
||||
"platform_settings": {
|
||||
"ignore_bot_self_message": {
|
||||
"description": "Ignore Bot's Own Messages"
|
||||
},
|
||||
"ignore_at_all": {
|
||||
"description": "Ignore @All Events"
|
||||
},
|
||||
"no_permission_reply": {
|
||||
"description": "Reply When User Has Insufficient Permissions"
|
||||
}
|
||||
},
|
||||
"platform_specific": {
|
||||
"lark": {
|
||||
"pre_ack_emoji": {
|
||||
"enable": {
|
||||
"description": "[Lark] Enable Pre-acknowledgment Emoji"
|
||||
},
|
||||
"emojis": {
|
||||
"description": "Emoji List (Lark Emoji Enum Names)",
|
||||
"hint": "Emoji enum names reference: https://open.feishu.cn/document/server-docs/im-v1/message-reaction/emojis-introduce"
|
||||
}
|
||||
}
|
||||
},
|
||||
"telegram": {
|
||||
"pre_ack_emoji": {
|
||||
"enable": {
|
||||
"description": "[Telegram] Enable Pre-acknowledgment Emoji"
|
||||
},
|
||||
"emojis": {
|
||||
"description": "Emoji List (Unicode)",
|
||||
"hint": "Telegram only supports a fixed reaction set, reference: https://gist.github.com/Soulter/3f22c8e5f9c7e152e967e8bc28c97fc9"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"plugin_group": {
|
||||
"name": "Plugin",
|
||||
"plugin": {
|
||||
"description": "Plugins",
|
||||
"plugin_set": {
|
||||
"description": "Available Plugins",
|
||||
"hint": "All non-disabled plugins are enabled by default. If a plugin is disabled on the plugins page, selections here will not take effect."
|
||||
}
|
||||
}
|
||||
},
|
||||
"ext_group": {
|
||||
"name": "Ext.",
|
||||
"segmented_reply": {
|
||||
"description": "Segmented Reply",
|
||||
"platform_settings": {
|
||||
"segmented_reply": {
|
||||
"enable": {
|
||||
"description": "Enable Segmented Reply"
|
||||
},
|
||||
"only_llm_result": {
|
||||
"description": "Segment Only LLM Results"
|
||||
},
|
||||
"interval_method": {
|
||||
"description": "Interval Method"
|
||||
},
|
||||
"interval": {
|
||||
"description": "Random Interval Time",
|
||||
"hint": "Format: minimum,maximum (e.g., 1.5,3.5)"
|
||||
},
|
||||
"log_base": {
|
||||
"description": "Logarithm Base",
|
||||
"hint": "Base for logarithmic intervals, defaults to 2.0. Value range: 1.0-10.0."
|
||||
},
|
||||
"words_count_threshold": {
|
||||
"description": "Segmented Reply Word Count Threshold"
|
||||
},
|
||||
"regex": {
|
||||
"description": "Segmentation Regular Expression"
|
||||
},
|
||||
"content_cleanup_rule": {
|
||||
"description": "Content Filtering Regular Expression",
|
||||
"hint": "Remove specified content from segmented content. For example, `[。?!]` will remove all periods, question marks, and exclamation marks."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ltm": {
|
||||
"description": "Group Chat Context Awareness (formerly Chat Memory Enhancement)",
|
||||
"provider_ltm_settings": {
|
||||
"group_icl_enable": {
|
||||
"description": "Enable Group Chat Context Awareness"
|
||||
},
|
||||
"group_message_max_cnt": {
|
||||
"description": "Maximum Message Count"
|
||||
},
|
||||
"image_caption": {
|
||||
"description": "Auto-understand Images",
|
||||
"hint": "Requires setting a default image caption model."
|
||||
},
|
||||
"active_reply": {
|
||||
"enable": {
|
||||
"description": "Active Reply"
|
||||
},
|
||||
"method": {
|
||||
"description": "Active Reply Method"
|
||||
},
|
||||
"possibility_reply": {
|
||||
"description": "Reply Probability",
|
||||
"hint": "Value between 0.0-1.0"
|
||||
},
|
||||
"whitelist": {
|
||||
"description": "Active Reply Whitelist",
|
||||
"hint": "Whitelist filtering is disabled when empty. Use /sid to get IDs."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"system_group": {
|
||||
"name": "System",
|
||||
"system": {
|
||||
"description": "System Settings",
|
||||
"t2i_strategy": {
|
||||
"description": "Text-to-Image Strategy",
|
||||
"hint": "Text-to-image strategy. `remote` uses a remote HTML-based rendering service, `local` uses PIL for local rendering. When using local, place a TTF font named 'font.ttf' in the data/ directory to customize the font."
|
||||
},
|
||||
"t2i_endpoint": {
|
||||
"description": "Text-to-Image Service API Endpoint",
|
||||
"hint": "Uses AstrBot API service when empty"
|
||||
},
|
||||
"t2i_template": {
|
||||
"description": "Text-to-Image Custom Template",
|
||||
"hint": "When enabled, you can customize HTML templates for text-to-image rendering."
|
||||
},
|
||||
"t2i_active_template": {
|
||||
"description": "Currently Active Text-to-Image Rendering Template",
|
||||
"hint": "This value is maintained by the text-to-image template management page."
|
||||
},
|
||||
"log_level": {
|
||||
"description": "Console Log Level",
|
||||
"hint": "Log level for console output."
|
||||
},
|
||||
"pip_install_arg": {
|
||||
"description": "Additional pip Installation Arguments",
|
||||
"hint": "When installing plugin dependencies, Python's pip tool will be used. Additional arguments can be provided here, such as `--break-system-package`."
|
||||
},
|
||||
"pypi_index_url": {
|
||||
"description": "PyPI Repository URL",
|
||||
"hint": "PyPI repository URL for installing Python dependencies. Defaults to https://mirrors.aliyun.com/pypi/simple/"
|
||||
},
|
||||
"callback_api_base": {
|
||||
"description": "Externally Accessible Callback API Address",
|
||||
"hint": "External services may access AstrBot's backend through callback links generated by AstrBot (such as file download links). Since AstrBot cannot automatically determine the externally accessible host address in the deployment environment, this configuration item is needed to explicitly specify how external services should access AstrBot's address. Examples: http://localhost:6185, https://example.com, etc."
|
||||
},
|
||||
"timezone": {
|
||||
"description": "Timezone",
|
||||
"hint": "Timezone setting. Please enter an IANA timezone name, such as Asia/Shanghai. Uses system default timezone when empty. For all timezones, see: https://data.iana.org/time-zones/tzdb-2021a/zone1970.tab"
|
||||
},
|
||||
"http_proxy": {
|
||||
"description": "HTTP Proxy",
|
||||
"hint": "When enabled, proxy will be set by adding environment variables. Format: `http://ip:port`"
|
||||
},
|
||||
"no_proxy": {
|
||||
"description": "Direct Connection Address List"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,5 +62,33 @@
|
||||
"allowedHosts": "Allowed Hosts",
|
||||
"rateLimit": "Rate Limit",
|
||||
"encryption": "Encryption Settings"
|
||||
},
|
||||
"configSelection": {
|
||||
"selectConfig": "Select Configuration",
|
||||
"normalConfig": "Basic",
|
||||
"systemConfig": "System"
|
||||
},
|
||||
"configManagement": {
|
||||
"title": "Configuration Management",
|
||||
"description": "AstrBot supports separate configuration files for different bots. The `default` configuration is used by default.",
|
||||
"newConfig": "New Configuration",
|
||||
"editConfig": "Edit Configuration",
|
||||
"manageConfigs": "Manage Configurations...",
|
||||
"configName": "Name",
|
||||
"fillConfigName": "Enter configuration name",
|
||||
"confirmDelete": "Are you sure you want to delete the configuration \"{name}\"? This action cannot be undone.",
|
||||
"pleaseEnterName": "Please enter a configuration name",
|
||||
"createFailed": "Failed to create new configuration",
|
||||
"deleteFailed": "Failed to delete configuration",
|
||||
"updateFailed": "Failed to update configuration"
|
||||
},
|
||||
"buttons": {
|
||||
"cancel": "Cancel",
|
||||
"create": "Create",
|
||||
"update": "Update"
|
||||
},
|
||||
"codeEditor": {
|
||||
"title": "Edit Configuration File"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,124 +1,96 @@
|
||||
{
|
||||
"title": "Session Management",
|
||||
"subtitle": "Manage active sessions and configurations",
|
||||
"title": "Custom Rules",
|
||||
"subtitle": "Set custom rules for specific sessions, which take priority over global settings",
|
||||
"buttons": {
|
||||
"refresh": "Refresh",
|
||||
"edit": "Edit",
|
||||
"apply": "Apply Batch Settings",
|
||||
"editName": "Edit Session Name",
|
||||
"editRule": "Edit Rules",
|
||||
"deleteAllRules": "Delete All Rules",
|
||||
"addRule": "Add Rule",
|
||||
"save": "Save",
|
||||
"cancel": "Cancel",
|
||||
"delete": "Delete"
|
||||
"delete": "Delete",
|
||||
"clear": "Clear",
|
||||
"next": "Next",
|
||||
"editCustomName": "Edit Note",
|
||||
"batchDelete": "Batch Delete"
|
||||
},
|
||||
"sessions": {
|
||||
"activeSessions": "Active Sessions",
|
||||
"sessionCount": "sessions",
|
||||
"noActiveSessions": "No active sessions",
|
||||
"noActiveSessionsDesc": "Sessions will appear here when users interact with the bot"
|
||||
"customRules": {
|
||||
"title": "Custom Rules",
|
||||
"rulesCount": "rules",
|
||||
"hasRules": "Configured",
|
||||
"noRules": "No Custom Rules",
|
||||
"noRulesDesc": "Click 'Add Rule' to configure custom rules for specific sessions",
|
||||
"serviceConfig": "Service Config",
|
||||
"pluginConfig": "Plugin Config",
|
||||
"kbConfig": "Knowledge Base",
|
||||
"providerConfig": "Provider Config",
|
||||
"configured": "Configured",
|
||||
"noCustomName": "No note set"
|
||||
},
|
||||
"quickEditName": {
|
||||
"title": "Edit Note"
|
||||
},
|
||||
"search": {
|
||||
"placeholder": "Search sessions...",
|
||||
"platformFilter": "Platform Filter"
|
||||
"placeholder": "Search sessions..."
|
||||
},
|
||||
"table": {
|
||||
"headers": {
|
||||
"sessionStatus": "Session Status",
|
||||
"sessionInfo": "Session Info",
|
||||
"persona": "Persona",
|
||||
"chatProvider": "Chat Provider",
|
||||
"sttProvider": "STT Provider",
|
||||
"ttsProvider": "TTS Provider",
|
||||
"llmStatus": "LLM Status",
|
||||
"ttsStatus": "TTS Status",
|
||||
"knowledgeBase": "Knowledge Base",
|
||||
"pluginManagement": "Plugin Management",
|
||||
"umoInfo": "Session Info",
|
||||
"rulesOverview": "Rules Overview",
|
||||
"actions": "Actions"
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"enabled": "Enabled",
|
||||
"disabled": "Disabled"
|
||||
},
|
||||
"persona": {
|
||||
"none": "No Persona"
|
||||
},
|
||||
"batchOperations": {
|
||||
"title": "Batch Operations",
|
||||
"setPersona": "Batch Set Persona",
|
||||
"setChatProvider": "Batch Set Chat Provider",
|
||||
"setSttProvider": "Batch Set STT Provider",
|
||||
"setTtsProvider": "Batch Set TTS Provider",
|
||||
"setLlmStatus": "Batch Set LLM Status",
|
||||
"setTtsStatus": "Batch Set TTS Status",
|
||||
"noSttProvider": "No STT Provider Available",
|
||||
"noTtsProvider": "No TTS Provider Available"
|
||||
"addRule": {
|
||||
"title": "Add Custom Rule",
|
||||
"description": "Select a session (UMO) to configure custom rules. Custom rules take priority over global settings.",
|
||||
"selectUmo": "Select Session",
|
||||
"noUmos": "No sessions available"
|
||||
},
|
||||
"pluginManagement": {
|
||||
"title": "Plugin Management",
|
||||
"noPlugins": "No available plugins",
|
||||
"noPluginsDesc": "Currently no active plugins",
|
||||
"loading": "Loading plugin list...",
|
||||
"author": "Author"
|
||||
},
|
||||
"nameEditor": {
|
||||
"title": "Edit Session Name",
|
||||
"customName": "Custom Name",
|
||||
"placeholder": "Enter custom session name (leave empty to use original name)",
|
||||
"originalName": "Original Name",
|
||||
"fullSessionId": "Full Session ID",
|
||||
"hint": "Custom names help you easily identify sessions. The small information icon (!) will show the actual UMO when hovering."
|
||||
},
|
||||
"knowledgeBase": {
|
||||
"title": "Knowledge Base Configuration",
|
||||
"configure": "Configure",
|
||||
"selectKB": "Select Knowledge Bases",
|
||||
"selectMultiple": "You can select multiple knowledge bases",
|
||||
"noKBAvailable": "No knowledge bases available",
|
||||
"noKBDesc": "No knowledge bases have been created yet",
|
||||
"createKB": "Create Knowledge Base",
|
||||
"advancedSettings": "Advanced Settings",
|
||||
"topK": "Result Count",
|
||||
"topKHint": "Number of results to retrieve from knowledge base",
|
||||
"enableRerank": "Enable Reranking",
|
||||
"enableRerankHint": "Use reranking model to improve retrieval quality",
|
||||
"clearConfig": "Clear Configuration",
|
||||
"save": "Save",
|
||||
"cancel": "Cancel",
|
||||
"loading": "Loading knowledge base configuration...",
|
||||
"description": "Configure knowledge bases for this session. The session will use configured knowledge bases to enhance conversation context.",
|
||||
"saveSuccess": "Knowledge base configuration saved successfully",
|
||||
"saveFailed": "Failed to save knowledge base configuration",
|
||||
"loadFailed": "Failed to load knowledge base configuration",
|
||||
"clearSuccess": "Knowledge base configuration cleared",
|
||||
"clearFailed": "Failed to clear knowledge base configuration",
|
||||
"clearConfirm": "Are you sure you want to clear the knowledge base configuration for this session?"
|
||||
},
|
||||
"list": {
|
||||
"documents": "documents"
|
||||
"ruleEditor": {
|
||||
"title": "Edit Custom Rules",
|
||||
"description": "Configure custom rules for this session. These rules take priority over global settings.",
|
||||
"serviceConfig": {
|
||||
"title": "Service Configuration",
|
||||
"sessionEnabled": "Enable Session",
|
||||
"llmEnabled": "Enable LLM",
|
||||
"ttsEnabled": "Enable TTS",
|
||||
"customName": "Custom Name"
|
||||
},
|
||||
"providerConfig": {
|
||||
"title": "Provider Configuration",
|
||||
"chatProvider": "Chat Provider",
|
||||
"sttProvider": "STT Provider",
|
||||
"ttsProvider": "TTS Provider"
|
||||
},
|
||||
"personaConfig": {
|
||||
"title": "Persona Configuration",
|
||||
"selectPersona": "Select Persona",
|
||||
"hint": "Persona settings affect the conversation style and behavior of the LLM"
|
||||
}
|
||||
},
|
||||
"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?"
|
||||
"title": "Confirm Delete",
|
||||
"message": "Are you sure you want to delete all custom rules for this session? Global settings will be used after deletion."
|
||||
},
|
||||
"batchDeleteConfirm": {
|
||||
"title": "Confirm Batch Delete",
|
||||
"message": "Are you sure you want to delete {count} selected rules? Global settings will be used after deletion."
|
||||
},
|
||||
"messages": {
|
||||
"refreshSuccess": "Session list refreshed",
|
||||
"personaUpdateSuccess": "Persona updated successfully",
|
||||
"personaUpdateError": "Failed to update persona",
|
||||
"providerUpdateSuccess": "Provider updated successfully",
|
||||
"providerUpdateError": "Failed to update provider",
|
||||
"sessionStatusSuccess": "Session {status}",
|
||||
"llmStatusSuccess": "LLM {status}",
|
||||
"ttsStatusSuccess": "TTS {status}",
|
||||
"statusUpdateError": "Failed to update status",
|
||||
"loadSessionsError": "Failed to load session list",
|
||||
"batchUpdateSuccess": "Successfully batch updated {count} settings",
|
||||
"batchUpdatePartial": "Batch update completed, {success} successful, {error} failed",
|
||||
"loadPluginsError": "Failed to load plugin list",
|
||||
"pluginStatusSuccess": "Plugin {name} {status}",
|
||||
"pluginStatusError": "Failed to update plugin status",
|
||||
"nameUpdateSuccess": "Session name updated successfully",
|
||||
"nameUpdateError": "Failed to update session name",
|
||||
"deleteSuccess": "Session deleted successfully",
|
||||
"deleteError": "Failed to delete session"
|
||||
"refreshSuccess": "Data refreshed",
|
||||
"loadError": "Failed to load data",
|
||||
"saveSuccess": "Saved successfully",
|
||||
"saveError": "Failed to save",
|
||||
"clearSuccess": "Cleared successfully",
|
||||
"clearError": "Failed to clear",
|
||||
"deleteSuccess": "Deleted successfully",
|
||||
"deleteError": "Failed to delete",
|
||||
"noChanges": "No changes to save",
|
||||
"batchDeleteSuccess": "Batch delete successful",
|
||||
"batchDeleteError": "Batch delete failed"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"config": "配置文件",
|
||||
"chat": "聊天",
|
||||
"conversation": "对话数据",
|
||||
"sessionManagement": "会话管理",
|
||||
"sessionManagement": "自定义规则",
|
||||
"console": "控制台",
|
||||
"alkaid": "Alkaid",
|
||||
"knowledgeBase": "知识库",
|
||||
|
||||
45
dashboard/src/i18n/locales/zh-CN/core/shared.json
Normal file
45
dashboard/src/i18n/locales/zh-CN/core/shared.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"knowledgeBaseSelector": {
|
||||
"notSelected": "未选择",
|
||||
"buttonText": "选择知识库...",
|
||||
"dialogTitle": "选择知识库",
|
||||
"loading": "加载中...",
|
||||
"noKnowledgeBases": "暂无知识库",
|
||||
"createKnowledgeBase": "创建知识库",
|
||||
"selectedCount": "已选择 {count} 个知识库",
|
||||
"confirmSelection": "确认选择",
|
||||
"cancelSelection": "取消",
|
||||
"noDescription": "无描述",
|
||||
"documentCount": "{count} 个文档",
|
||||
"chunkCount": "{count} 个块"
|
||||
},
|
||||
"pluginSetSelector": {
|
||||
"notSelected": "未启用任何插件",
|
||||
"allPlugins": "启用所有插件 (*)",
|
||||
"selectedCount": "已选择 {count} 个插件",
|
||||
"buttonText": "选择插件集合...",
|
||||
"dialogTitle": "选择插件集合",
|
||||
"loading": "加载中...",
|
||||
"enableAll": "启用所有插件",
|
||||
"enableNone": "不启用任何插件",
|
||||
"customSelect": "自定义选择",
|
||||
"noPlugins": "暂无可用的插件",
|
||||
"confirmSelection": "确认选择",
|
||||
"cancelSelection": "取消",
|
||||
"noDescription": "无描述",
|
||||
"notActivated": "未激活",
|
||||
"note": "*不显示系统插件和已经在插件页禁用的插件。"
|
||||
},
|
||||
"providerSelector": {
|
||||
"notSelected": "未选择",
|
||||
"buttonText": "选择提供商...",
|
||||
"dialogTitle": "选择提供商",
|
||||
"loading": "加载中...",
|
||||
"noProviders": "暂无可用的提供商",
|
||||
"confirmSelection": "确认选择",
|
||||
"cancelSelection": "取消",
|
||||
"clearSelection": "不选择",
|
||||
"clearSelectionSubtitle": "清除当前选择",
|
||||
"unknownType": "未知类型"
|
||||
}
|
||||
}
|
||||
452
dashboard/src/i18n/locales/zh-CN/features/config-metadata.json
Normal file
452
dashboard/src/i18n/locales/zh-CN/features/config-metadata.json
Normal file
@@ -0,0 +1,452 @@
|
||||
{
|
||||
"ai_group": {
|
||||
"name": "AI 配置",
|
||||
"agent_runner": {
|
||||
"description": "Agent 执行方式",
|
||||
"hint": "选择 AI 对话的执行器,默认为 AstrBot 内置 Agent 执行器,可使用 AstrBot 内的知识库、人格、工具调用功能。如果不打算接入 Dify 或 Coze 等第三方 Agent 执行器,不需要修改此节。",
|
||||
"provider_settings": {
|
||||
"enable": {
|
||||
"description": "启用",
|
||||
"hint": "AI 对话总开关"
|
||||
},
|
||||
"agent_runner_type": {
|
||||
"description": "执行器",
|
||||
"labels": ["内置 Agent", "Dify", "Coze", "阿里云百炼应用"]
|
||||
},
|
||||
"coze_agent_runner_provider_id": {
|
||||
"description": "Coze Agent 执行器提供商 ID"
|
||||
},
|
||||
"dify_agent_runner_provider_id": {
|
||||
"description": "Dify Agent 执行器提供商 ID"
|
||||
},
|
||||
"dashscope_agent_runner_provider_id": {
|
||||
"description": "阿里云百炼应用 Agent 执行器提供商 ID"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ai": {
|
||||
"description": "模型",
|
||||
"hint": "当使用非内置 Agent 执行器时,默认聊天模型和默认图片转述模型可能会无效,但某些插件会依赖此配置项来调用 AI 能力。",
|
||||
"provider_settings": {
|
||||
"default_provider_id": {
|
||||
"description": "默认聊天模型",
|
||||
"hint": "留空时使用第一个模型"
|
||||
},
|
||||
"default_image_caption_provider_id": {
|
||||
"description": "默认图片转述模型",
|
||||
"hint": "留空代表不使用,可用于非多模态模型"
|
||||
},
|
||||
"image_caption_prompt": {
|
||||
"description": "图片转述提示词"
|
||||
}
|
||||
},
|
||||
"provider_stt_settings": {
|
||||
"enable": {
|
||||
"description": "启用语音转文本",
|
||||
"hint": "STT 总开关"
|
||||
},
|
||||
"provider_id": {
|
||||
"description": "默认语音转文本模型",
|
||||
"hint": "用户也可使用 /provider 指令单独选择会话的 STT 模型。"
|
||||
}
|
||||
},
|
||||
"provider_tts_settings": {
|
||||
"enable": {
|
||||
"description": "启用文本转语音",
|
||||
"hint": "TTS 总开关"
|
||||
},
|
||||
"provider_id": {
|
||||
"description": "默认文本转语音模型"
|
||||
}
|
||||
}
|
||||
},
|
||||
"persona": {
|
||||
"description": "人格",
|
||||
"provider_settings": {
|
||||
"default_personality": {
|
||||
"description": "默认采用的人格"
|
||||
}
|
||||
}
|
||||
},
|
||||
"knowledgebase": {
|
||||
"description": "知识库",
|
||||
"kb_names": {
|
||||
"description": "知识库列表",
|
||||
"hint": "支持多选"
|
||||
},
|
||||
"kb_fusion_top_k": {
|
||||
"description": "融合检索结果数",
|
||||
"hint": "多个知识库检索结果融合后的返回结果数量"
|
||||
},
|
||||
"kb_final_top_k": {
|
||||
"description": "最终返回结果数",
|
||||
"hint": "从知识库中检索到的结果数量,越大可能获得越多相关信息,但也可能引入噪音。建议根据实际需求调整"
|
||||
},
|
||||
"kb_agentic_mode": {
|
||||
"description": "Agentic 知识库检索",
|
||||
"hint": "启用后,知识库检索将作为 LLM Tool,由模型自主决定何时调用知识库进行查询。需要模型支持函数调用能力。"
|
||||
}
|
||||
},
|
||||
"websearch": {
|
||||
"description": "网页搜索",
|
||||
"provider_settings": {
|
||||
"web_search": {
|
||||
"description": "启用网页搜索"
|
||||
},
|
||||
"websearch_provider": {
|
||||
"description": "网页搜索提供商"
|
||||
},
|
||||
"websearch_tavily_key": {
|
||||
"description": "Tavily API Key",
|
||||
"hint": "可添加多个 Key 进行轮询。"
|
||||
},
|
||||
"websearch_baidu_app_builder_key": {
|
||||
"description": "百度千帆智能云 APP Builder API Key",
|
||||
"hint": "参考:https://console.bce.baidu.com/iam/#/iam/apikey/list"
|
||||
},
|
||||
"web_search_link": {
|
||||
"description": "显示来源引用"
|
||||
}
|
||||
}
|
||||
},
|
||||
"others": {
|
||||
"description": "其他配置",
|
||||
"provider_settings": {
|
||||
"display_reasoning_text": {
|
||||
"description": "显示思考内容"
|
||||
},
|
||||
"identifier": {
|
||||
"description": "用户识别",
|
||||
"hint": "启用后,会在提示词前包含用户 ID 信息。"
|
||||
},
|
||||
"group_name_display": {
|
||||
"description": "显示群名称",
|
||||
"hint": "启用后,在支持的平台(OneBot v11)上会在提示词前包含群名称信息。"
|
||||
},
|
||||
"datetime_system_prompt": {
|
||||
"description": "现实世界时间感知",
|
||||
"hint": "启用后,会在系统提示词中附带当前时间信息。"
|
||||
},
|
||||
"show_tool_use_status": {
|
||||
"description": "输出函数调用状态"
|
||||
},
|
||||
"max_agent_step": {
|
||||
"description": "工具调用轮数上限"
|
||||
},
|
||||
"tool_call_timeout": {
|
||||
"description": "工具调用超时时间(秒)"
|
||||
},
|
||||
"streaming_response": {
|
||||
"description": "流式输出"
|
||||
},
|
||||
"unsupported_streaming_strategy": {
|
||||
"description": "不支持流式回复的平台",
|
||||
"hint": "选择在不支持流式回复的平台上的处理方式。实时分段回复会在系统接收流式响应检测到诸如标点符号等分段点时,立即发送当前已接收的内容",
|
||||
"labels": ["实时分段回复", "关闭流式回复"]
|
||||
},
|
||||
"max_context_length": {
|
||||
"description": "最多携带对话轮数",
|
||||
"hint": "超出这个数量时丢弃最旧的部分,一轮聊天记为 1 条,-1 为不限制"
|
||||
},
|
||||
"dequeue_context_length": {
|
||||
"description": "丢弃对话轮数",
|
||||
"hint": "超出最多携带对话轮数时, 一次丢弃的聊天轮数"
|
||||
},
|
||||
"wake_prefix": {
|
||||
"description": "LLM 聊天额外唤醒前缀",
|
||||
"hint": "如果唤醒前缀为 /, 额外聊天唤醒前缀为 chat,则需要 /chat 才会触发 LLM 请求"
|
||||
},
|
||||
"prompt_prefix": {
|
||||
"description": "用户提示词",
|
||||
"hint": "可使用 {{prompt}} 作为用户输入的占位符。如果不输入占位符则代表添加在用户输入的前面。"
|
||||
}
|
||||
},
|
||||
"provider_tts_settings": {
|
||||
"dual_output": {
|
||||
"description": "开启 TTS 时同时输出语音和文字内容"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"platform_group": {
|
||||
"name": "平台配置",
|
||||
"general": {
|
||||
"description": "基本",
|
||||
"admins_id": {
|
||||
"description": "管理员 ID"
|
||||
},
|
||||
"platform_settings": {
|
||||
"unique_session": {
|
||||
"description": "隔离会话",
|
||||
"hint": "启用后,群成员的上下文独立。"
|
||||
},
|
||||
"friend_message_needs_wake_prefix": {
|
||||
"description": "私聊消息需要唤醒词"
|
||||
},
|
||||
"reply_prefix": {
|
||||
"description": "回复时的文本前缀"
|
||||
},
|
||||
"reply_with_mention": {
|
||||
"description": "回复时 @ 发送人"
|
||||
},
|
||||
"reply_with_quote": {
|
||||
"description": "回复时引用发送人消息"
|
||||
},
|
||||
"forward_threshold": {
|
||||
"description": "转发消息的字数阈值"
|
||||
},
|
||||
"empty_mention_waiting": {
|
||||
"description": "只 @ 机器人是否触发等待"
|
||||
}
|
||||
},
|
||||
"wake_prefix": {
|
||||
"description": "唤醒词"
|
||||
}
|
||||
},
|
||||
"whitelist": {
|
||||
"description": "白名单",
|
||||
"platform_settings": {
|
||||
"enable_id_white_list": {
|
||||
"description": "启用白名单",
|
||||
"hint": "启用后,只有在白名单内的会话会被响应。"
|
||||
},
|
||||
"id_whitelist": {
|
||||
"description": "白名单 ID 列表",
|
||||
"hint": "使用 /sid 获取 ID。"
|
||||
},
|
||||
"id_whitelist_log": {
|
||||
"description": "输出日志",
|
||||
"hint": "启用后,当一条消息没通过白名单时,会输出 INFO 级别的日志。"
|
||||
},
|
||||
"wl_ignore_admin_on_group": {
|
||||
"description": "管理员群组消息无视 ID 白名单"
|
||||
},
|
||||
"wl_ignore_admin_on_friend": {
|
||||
"description": "管理员私聊消息无视 ID 白名单"
|
||||
}
|
||||
}
|
||||
},
|
||||
"rate_limit": {
|
||||
"description": "速率限制",
|
||||
"platform_settings": {
|
||||
"rate_limit": {
|
||||
"time": {
|
||||
"description": "消息速率限制时间(秒)"
|
||||
},
|
||||
"count": {
|
||||
"description": "消息速率限制计数"
|
||||
},
|
||||
"strategy": {
|
||||
"description": "速率限制策略"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"content_safety": {
|
||||
"description": "内容安全",
|
||||
"content_safety": {
|
||||
"also_use_in_response": {
|
||||
"description": "同时检查模型的响应内容"
|
||||
},
|
||||
"baidu_aip": {
|
||||
"enable": {
|
||||
"description": "使用百度内容安全审核",
|
||||
"hint": "您需要手动安装 baidu-aip 库。"
|
||||
},
|
||||
"app_id": {
|
||||
"description": "App ID"
|
||||
},
|
||||
"api_key": {
|
||||
"description": "API Key"
|
||||
},
|
||||
"secret_key": {
|
||||
"description": "Secret Key"
|
||||
}
|
||||
},
|
||||
"internal_keywords": {
|
||||
"enable": {
|
||||
"description": "关键词检查"
|
||||
},
|
||||
"extra_keywords": {
|
||||
"description": "额外关键词",
|
||||
"hint": "额外的屏蔽关键词列表,支持正则表达式。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"t2i": {
|
||||
"description": "文本转图像",
|
||||
"t2i": {
|
||||
"description": "文本转图像输出"
|
||||
},
|
||||
"t2i_word_threshold": {
|
||||
"description": "文本转图像字数阈值"
|
||||
}
|
||||
},
|
||||
"others": {
|
||||
"description": "其他配置",
|
||||
"platform_settings": {
|
||||
"ignore_bot_self_message": {
|
||||
"description": "是否忽略机器人自身的消息"
|
||||
},
|
||||
"ignore_at_all": {
|
||||
"description": "是否忽略 @ 全体成员事件"
|
||||
},
|
||||
"no_permission_reply": {
|
||||
"description": "用户权限不足时是否回复"
|
||||
}
|
||||
},
|
||||
"platform_specific": {
|
||||
"lark": {
|
||||
"pre_ack_emoji": {
|
||||
"enable": {
|
||||
"description": "[飞书] 启用预回应表情"
|
||||
},
|
||||
"emojis": {
|
||||
"description": "表情列表(飞书表情枚举名)",
|
||||
"hint": "表情枚举名参考:https://open.feishu.cn/document/server-docs/im-v1/message-reaction/emojis-introduce"
|
||||
}
|
||||
}
|
||||
},
|
||||
"telegram": {
|
||||
"pre_ack_emoji": {
|
||||
"enable": {
|
||||
"description": "[Telegram] 启用预回应表情"
|
||||
},
|
||||
"emojis": {
|
||||
"description": "表情列表(Unicode)",
|
||||
"hint": "Telegram 仅支持固定反应集合,参考:https://gist.github.com/Soulter/3f22c8e5f9c7e152e967e8bc28c97fc9"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"plugin_group": {
|
||||
"name": "插件配置",
|
||||
"plugin": {
|
||||
"description": "插件",
|
||||
"plugin_set": {
|
||||
"description": "可用插件",
|
||||
"hint": "默认启用全部未被禁用的插件。若插件在插件页面被禁用,则此处的选择不会生效。"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ext_group": {
|
||||
"name": "扩展功能",
|
||||
"segmented_reply": {
|
||||
"description": "分段回复",
|
||||
"platform_settings": {
|
||||
"segmented_reply": {
|
||||
"enable": {
|
||||
"description": "启用分段回复"
|
||||
},
|
||||
"only_llm_result": {
|
||||
"description": "仅对 LLM 结果分段"
|
||||
},
|
||||
"interval_method": {
|
||||
"description": "间隔方法"
|
||||
},
|
||||
"interval": {
|
||||
"description": "随机间隔时间",
|
||||
"hint": "格式:最小值,最大值(如:1.5,3.5)"
|
||||
},
|
||||
"log_base": {
|
||||
"description": "对数底数",
|
||||
"hint": "对数间隔的底数,默认为 2.0。取值范围为 1.0-10.0。"
|
||||
},
|
||||
"words_count_threshold": {
|
||||
"description": "分段回复字数阈值"
|
||||
},
|
||||
"regex": {
|
||||
"description": "分段正则表达式"
|
||||
},
|
||||
"content_cleanup_rule": {
|
||||
"description": "内容过滤正则表达式",
|
||||
"hint": "移除分段后内容中的指定内容。如填写 `[。?!]` 将移除所有的句号、问号、感叹号。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ltm": {
|
||||
"description": "群聊上下文感知(原聊天记忆增强)",
|
||||
"provider_ltm_settings": {
|
||||
"group_icl_enable": {
|
||||
"description": "启用群聊上下文感知"
|
||||
},
|
||||
"group_message_max_cnt": {
|
||||
"description": "最大消息数量"
|
||||
},
|
||||
"image_caption": {
|
||||
"description": "自动理解图片",
|
||||
"hint": "需要设置默认图片转述模型。"
|
||||
},
|
||||
"active_reply": {
|
||||
"enable": {
|
||||
"description": "主动回复"
|
||||
},
|
||||
"method": {
|
||||
"description": "主动回复方法"
|
||||
},
|
||||
"possibility_reply": {
|
||||
"description": "回复概率",
|
||||
"hint": "0.0-1.0 之间的数值"
|
||||
},
|
||||
"whitelist": {
|
||||
"description": "主动回复白名单",
|
||||
"hint": "为空时不启用白名单过滤。使用 /sid 获取 ID。"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"system_group": {
|
||||
"name": "系统配置",
|
||||
"system": {
|
||||
"description": "系统配置",
|
||||
"t2i_strategy": {
|
||||
"description": "文本转图像策略",
|
||||
"hint": "文本转图像策略。`remote` 为使用远程基于 HTML 的渲染服务,`local` 为使用 PIL 本地渲染。当使用 local 时,将 ttf 字体命名为 'font.ttf' 放在 data/ 目录下可自定义字体。"
|
||||
},
|
||||
"t2i_endpoint": {
|
||||
"description": "文本转图像服务 API 地址",
|
||||
"hint": "为空时使用 AstrBot API 服务"
|
||||
},
|
||||
"t2i_template": {
|
||||
"description": "文本转图像自定义模版",
|
||||
"hint": "启用后可自定义 HTML 模板用于文转图渲染。"
|
||||
},
|
||||
"t2i_active_template": {
|
||||
"description": "当前应用的文转图渲染模板",
|
||||
"hint": "此处的值由文转图模板管理页面进行维护。"
|
||||
},
|
||||
"log_level": {
|
||||
"description": "控制台日志级别",
|
||||
"hint": "控制台输出日志的级别。"
|
||||
},
|
||||
"pip_install_arg": {
|
||||
"description": "pip 安装额外参数",
|
||||
"hint": "安装插件依赖时,会使用 Python 的 pip 工具。这里可以填写额外的参数,如 `--break-system-package` 等。"
|
||||
},
|
||||
"pypi_index_url": {
|
||||
"description": "PyPI 软件仓库地址",
|
||||
"hint": "安装 Python 依赖时请求的 PyPI 软件仓库地址。默认为 https://mirrors.aliyun.com/pypi/simple/"
|
||||
},
|
||||
"callback_api_base": {
|
||||
"description": "对外可达的回调接口地址",
|
||||
"hint": "外部服务可能会通过 AstrBot 生成的回调链接(如文件下载链接)访问 AstrBot 后端。由于 AstrBot 无法自动判断部署环境中对外可达的主机地址(host),因此需要通过此配置项显式指定外部服务如何访问 AstrBot 的地址。如 http://localhost:6185,https://example.com 等。"
|
||||
},
|
||||
"timezone": {
|
||||
"description": "时区",
|
||||
"hint": "时区设置。请填写 IANA 时区名称, 如 Asia/Shanghai, 为空时使用系统默认时区。所有时区请查看: https://data.iana.org/time-zones/tzdb-2021a/zone1970.tab"
|
||||
},
|
||||
"http_proxy": {
|
||||
"description": "HTTP 代理",
|
||||
"hint": "启用后,会以添加环境变量的方式设置代理。格式为 `http://ip:port`"
|
||||
},
|
||||
"no_proxy": {
|
||||
"description": "直连地址列表"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,5 +62,32 @@
|
||||
"allowedHosts": "允许的主机",
|
||||
"rateLimit": "频率限制",
|
||||
"encryption": "加密设置"
|
||||
},
|
||||
"configSelection": {
|
||||
"selectConfig": "选择配置文件",
|
||||
"normalConfig": "普通",
|
||||
"systemConfig": "系统"
|
||||
},
|
||||
"configManagement": {
|
||||
"title": "配置文件管理",
|
||||
"description": "AstrBot 支持针对不同机器人分别设置配置文件。默认会使用 `default` 配置。",
|
||||
"newConfig": "新建配置文件",
|
||||
"editConfig": "编辑配置文件",
|
||||
"manageConfigs": "管理配置文件...",
|
||||
"configName": "名称",
|
||||
"fillConfigName": "填写配置文件名称",
|
||||
"confirmDelete": "确定要删除配置文件 \"{name}\" 吗?此操作不可恢复。",
|
||||
"pleaseEnterName": "请填写配置名称",
|
||||
"createFailed": "新配置文件创建失败",
|
||||
"deleteFailed": "删除配置文件失败",
|
||||
"updateFailed": "更新配置文件失败"
|
||||
},
|
||||
"buttons": {
|
||||
"cancel": "取消",
|
||||
"create": "创建",
|
||||
"update": "更新"
|
||||
},
|
||||
"codeEditor": {
|
||||
"title": "编辑配置文件"
|
||||
}
|
||||
}
|
||||
@@ -1,124 +1,96 @@
|
||||
{
|
||||
"title": "会话管理",
|
||||
"subtitle": "管理活跃会话和配置",
|
||||
"title": "自定义规则",
|
||||
"subtitle": "为特定会话设置自定义规则,优先级高于全局配置",
|
||||
"buttons": {
|
||||
"refresh": "刷新",
|
||||
"edit": "编辑",
|
||||
"apply": "应用批量设置",
|
||||
"editName": "备注",
|
||||
"editRule": "编辑规则",
|
||||
"deleteAllRules": "删除所有规则",
|
||||
"addRule": "添加规则",
|
||||
"save": "保存",
|
||||
"cancel": "取消",
|
||||
"delete": "删除"
|
||||
"delete": "删除",
|
||||
"clear": "清除",
|
||||
"next": "下一步",
|
||||
"editCustomName": "编辑备注",
|
||||
"batchDelete": "批量删除"
|
||||
},
|
||||
"sessions": {
|
||||
"activeSessions": "活跃会话",
|
||||
"sessionCount": "个会话",
|
||||
"noActiveSessions": "暂无活跃会话",
|
||||
"noActiveSessionsDesc": "当有用户与机器人交互时,会话将会显示在这里"
|
||||
"customRules": {
|
||||
"title": "自定义规则",
|
||||
"rulesCount": "条规则",
|
||||
"hasRules": "已配置",
|
||||
"noRules": "暂无自定义规则",
|
||||
"noRulesDesc": "点击「添加规则」为特定会话配置自定义规则",
|
||||
"serviceConfig": "服务配置",
|
||||
"pluginConfig": "插件配置",
|
||||
"kbConfig": "知识库配置",
|
||||
"providerConfig": "模型配置",
|
||||
"configured": "已配置",
|
||||
"noCustomName": "未设置备注"
|
||||
},
|
||||
"quickEditName": {
|
||||
"title": "编辑备注名"
|
||||
},
|
||||
"search": {
|
||||
"placeholder": "搜索会话...",
|
||||
"platformFilter": "平台筛选"
|
||||
"placeholder": "搜索会话..."
|
||||
},
|
||||
"table": {
|
||||
"headers": {
|
||||
"sessionStatus": "会话状态",
|
||||
"sessionInfo": "消息会话来源",
|
||||
"persona": "人格",
|
||||
"chatProvider": "聊天模型",
|
||||
"sttProvider": "语音识别模型",
|
||||
"ttsProvider": "语音合成模型",
|
||||
"llmStatus": "启用 LLM",
|
||||
"ttsStatus": "启用 TTS",
|
||||
"knowledgeBase": "知识库配置",
|
||||
"pluginManagement": "插件管理",
|
||||
"umoInfo": "消息会话来源",
|
||||
"rulesOverview": "规则概览",
|
||||
"actions": "操作"
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"enabled": "已启用",
|
||||
"disabled": "已禁用"
|
||||
},
|
||||
"persona": {
|
||||
"none": "无人格"
|
||||
},
|
||||
"batchOperations": {
|
||||
"title": "批量操作",
|
||||
"setPersona": "批量设置人格",
|
||||
"setChatProvider": "批量设置 Chat Provider",
|
||||
"setSttProvider": "批量设置 STT Provider",
|
||||
"setTtsProvider": "批量设置 TTS Provider",
|
||||
"setLlmStatus": "批量设置 LLM 状态",
|
||||
"setTtsStatus": "批量设置 TTS 状态",
|
||||
"noSttProvider": "暂无可用 STT Provider",
|
||||
"noTtsProvider": "暂无可用 TTS Provider"
|
||||
"addRule": {
|
||||
"title": "添加自定义规则",
|
||||
"description": "选择一个消息会话来源 (UMO) 来配置自定义规则。自定义规则的优先级高于该来源所属的配置文件中的全局规则。可以使用 /sid 指令获取该来源的 UMO 信息。",
|
||||
"selectUmo": "选择会话",
|
||||
"noUmos": "暂无可用会话"
|
||||
},
|
||||
"pluginManagement": {
|
||||
"title": "插件管理",
|
||||
"noPlugins": "暂无可用插件",
|
||||
"noPluginsDesc": "目前没有激活的插件",
|
||||
"loading": "加载插件列表中...",
|
||||
"author": "作者"
|
||||
},
|
||||
"nameEditor": {
|
||||
"title": "编辑会话名称",
|
||||
"customName": "自定义名称",
|
||||
"placeholder": "输入自定义会话名称(留空则使用原始名称)",
|
||||
"originalName": "原始名称",
|
||||
"fullSessionId": "完整会话ID",
|
||||
"hint": "自定义名称帮助您轻松识别会话。当设置了自定义名称时,会显示一个小感叹号标识(!),鼠标悬停时会显示实际的UMO。"
|
||||
},
|
||||
"knowledgeBase": {
|
||||
"title": "知识库配置",
|
||||
"configure": "配置",
|
||||
"selectKB": "选择知识库",
|
||||
"selectMultiple": "可以选择多个知识库",
|
||||
"noKBAvailable": "暂无可用的知识库",
|
||||
"noKBDesc": "目前没有创建任何知识库",
|
||||
"createKB": "创建知识库",
|
||||
"advancedSettings": "高级配置",
|
||||
"topK": "返回结果数量",
|
||||
"topKHint": "从知识库检索的结果数量",
|
||||
"enableRerank": "启用重排序",
|
||||
"enableRerankHint": "使用重排序模型提高检索质量",
|
||||
"clearConfig": "清除配置",
|
||||
"save": "保存",
|
||||
"cancel": "取消",
|
||||
"loading": "加载知识库配置中...",
|
||||
"description": "为此会话配置使用的知识库。会话将使用配置的知识库来增强对话上下文。",
|
||||
"saveSuccess": "知识库配置保存成功",
|
||||
"saveFailed": "保存知识库配置失败",
|
||||
"loadFailed": "加载知识库配置失败",
|
||||
"clearSuccess": "知识库配置已清除",
|
||||
"clearFailed": "清除知识库配置失败",
|
||||
"clearConfirm": "确定要清除此会话的知识库配置吗?"
|
||||
},
|
||||
"list": {
|
||||
"documents": "篇文档"
|
||||
"ruleEditor": {
|
||||
"title": "编辑自定义规则",
|
||||
"description": "为此会话配置自定义规则,这些规则将优先于全局配置生效。",
|
||||
"serviceConfig": {
|
||||
"title": "服务配置",
|
||||
"sessionEnabled": "启用该消息会话来源的消息处理",
|
||||
"llmEnabled": "启用 LLM",
|
||||
"ttsEnabled": "启用 TTS",
|
||||
"customName": "消息会话来源备注名称"
|
||||
},
|
||||
"providerConfig": {
|
||||
"title": "模型配置",
|
||||
"chatProvider": "聊天模型",
|
||||
"sttProvider": "语音识别模型",
|
||||
"ttsProvider": "语音合成模型"
|
||||
},
|
||||
"personaConfig": {
|
||||
"title": "人格配置",
|
||||
"selectPersona": "选择人格",
|
||||
"hint": "人格配置会影响 LLM 的对话风格和行为"
|
||||
}
|
||||
},
|
||||
"deleteConfirm": {
|
||||
"message": "确定要删除会话 {sessionName} 吗?",
|
||||
"warning": "此操作将永久删除本次会话的「全部对话记录」与「偏好设置」(插件对会话的关联数据除外),且无法恢复。确认继续?"
|
||||
"title": "确认删除",
|
||||
"message": "确定要删除此会话的所有自定义规则吗?删除后将恢复使用全局配置。"
|
||||
},
|
||||
"batchDeleteConfirm": {
|
||||
"title": "确认批量删除",
|
||||
"message": "确定要删除选中的 {count} 条规则吗?删除后将恢复使用全局配置。"
|
||||
},
|
||||
"messages": {
|
||||
"refreshSuccess": "会话列表已刷新",
|
||||
"personaUpdateSuccess": "人格更新成功",
|
||||
"personaUpdateError": "人格更新失败",
|
||||
"providerUpdateSuccess": "Provider 更新成功",
|
||||
"providerUpdateError": "Provider 更新失败",
|
||||
"sessionStatusSuccess": "会话 {status}",
|
||||
"llmStatusSuccess": "LLM {status}",
|
||||
"ttsStatusSuccess": "TTS {status}",
|
||||
"statusUpdateError": "状态更新失败",
|
||||
"loadSessionsError": "加载会话列表失败",
|
||||
"batchUpdateSuccess": "成功批量更新 {count} 项设置",
|
||||
"batchUpdatePartial": "批量更新完成,{success} 项成功,{error} 项失败",
|
||||
"loadPluginsError": "加载插件列表失败",
|
||||
"pluginStatusSuccess": "插件 {name} {status}",
|
||||
"pluginStatusError": "插件状态更新失败",
|
||||
"nameUpdateSuccess": "会话名称更新成功",
|
||||
"nameUpdateError": "会话名称更新失败",
|
||||
"deleteSuccess": "会话删除成功",
|
||||
"deleteError": "会话删除失败"
|
||||
"refreshSuccess": "数据已刷新",
|
||||
"loadError": "加载数据失败",
|
||||
"saveSuccess": "保存成功",
|
||||
"saveError": "保存失败",
|
||||
"clearSuccess": "已清除",
|
||||
"clearError": "清除失败",
|
||||
"deleteSuccess": "删除成功",
|
||||
"deleteError": "删除失败",
|
||||
"noChanges": "没有需要保存的更改",
|
||||
"batchDeleteSuccess": "批量删除成功",
|
||||
"batchDeleteError": "批量删除失败"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import zhCNActions from './locales/zh-CN/core/actions.json';
|
||||
import zhCNStatus from './locales/zh-CN/core/status.json';
|
||||
import zhCNNavigation from './locales/zh-CN/core/navigation.json';
|
||||
import zhCNHeader from './locales/zh-CN/core/header.json';
|
||||
import zhCNShared from './locales/zh-CN/core/shared.json';
|
||||
|
||||
import zhCNChat from './locales/zh-CN/features/chat.json';
|
||||
import zhCNExtension from './locales/zh-CN/features/extension.json';
|
||||
@@ -16,6 +17,7 @@ import zhCNToolUse from './locales/zh-CN/features/tool-use.json';
|
||||
import zhCNProvider from './locales/zh-CN/features/provider.json';
|
||||
import zhCNPlatform from './locales/zh-CN/features/platform.json';
|
||||
import zhCNConfig from './locales/zh-CN/features/config.json';
|
||||
import zhCNConfigMetadata from './locales/zh-CN/features/config-metadata.json';
|
||||
import zhCNConsole from './locales/zh-CN/features/console.json';
|
||||
import zhCNAbout from './locales/zh-CN/features/about.json';
|
||||
import zhCNSettings from './locales/zh-CN/features/settings.json';
|
||||
@@ -41,6 +43,7 @@ import enUSActions from './locales/en-US/core/actions.json';
|
||||
import enUSStatus from './locales/en-US/core/status.json';
|
||||
import enUSNavigation from './locales/en-US/core/navigation.json';
|
||||
import enUSHeader from './locales/en-US/core/header.json';
|
||||
import enUSShared from './locales/en-US/core/shared.json';
|
||||
|
||||
import enUSChat from './locales/en-US/features/chat.json';
|
||||
import enUSExtension from './locales/en-US/features/extension.json';
|
||||
@@ -50,6 +53,7 @@ import enUSToolUse from './locales/en-US/features/tool-use.json';
|
||||
import enUSProvider from './locales/en-US/features/provider.json';
|
||||
import enUSPlatform from './locales/en-US/features/platform.json';
|
||||
import enUSConfig from './locales/en-US/features/config.json';
|
||||
import enUSConfigMetadata from './locales/en-US/features/config-metadata.json';
|
||||
import enUSConsole from './locales/en-US/features/console.json';
|
||||
import enUSAbout from './locales/en-US/features/about.json';
|
||||
import enUSSettings from './locales/en-US/features/settings.json';
|
||||
@@ -77,7 +81,8 @@ export const translations = {
|
||||
actions: zhCNActions,
|
||||
status: zhCNStatus,
|
||||
navigation: zhCNNavigation,
|
||||
header: zhCNHeader
|
||||
header: zhCNHeader,
|
||||
shared: zhCNShared
|
||||
},
|
||||
features: {
|
||||
chat: zhCNChat,
|
||||
@@ -88,6 +93,7 @@ export const translations = {
|
||||
provider: zhCNProvider,
|
||||
platform: zhCNPlatform,
|
||||
config: zhCNConfig,
|
||||
'config-metadata': zhCNConfigMetadata,
|
||||
console: zhCNConsole,
|
||||
about: zhCNAbout,
|
||||
settings: zhCNSettings,
|
||||
@@ -119,7 +125,8 @@ export const translations = {
|
||||
actions: enUSActions,
|
||||
status: enUSStatus,
|
||||
navigation: enUSNavigation,
|
||||
header: enUSHeader
|
||||
header: enUSHeader,
|
||||
shared: enUSShared
|
||||
},
|
||||
features: {
|
||||
chat: enUSChat,
|
||||
@@ -130,6 +137,7 @@ export const translations = {
|
||||
provider: enUSProvider,
|
||||
platform: enUSPlatform,
|
||||
config: enUSConfig,
|
||||
'config-metadata': enUSConfigMetadata,
|
||||
console: enUSConsole,
|
||||
about: enUSAbout,
|
||||
settings: enUSSettings,
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
style="margin-bottom: 16px; align-items: center; gap: 12px; justify-content: space-between; width: 100%;">
|
||||
<div class="d-flex flex-row align-center" style="gap: 12px;">
|
||||
<v-select style="min-width: 130px;" v-model="selectedConfigID" :items="configSelectItems" item-title="name" :disabled="initialConfigId !== null"
|
||||
v-if="!isSystemConfig" item-value="id" label="选择配置文件" hide-details density="compact" rounded="md"
|
||||
v-if="!isSystemConfig" item-value="id" :label="tm('configSelection.selectConfig')" hide-details density="compact" rounded="md"
|
||||
variant="outlined" @update:model-value="onConfigSelect">
|
||||
</v-select>
|
||||
<a style="color: inherit;" href="https://blog.astrbot.app/posts/what-is-changed-in-4.0.0/#%E5%A4%9A%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6" target="_blank"><v-btn icon="mdi-help-circle" size="small" variant="plain"></v-btn></a>
|
||||
@@ -19,10 +19,10 @@
|
||||
<v-btn-toggle v-model="configType" mandatory color="primary" variant="outlined" density="comfortable"
|
||||
rounded="md" @update:model-value="onConfigTypeToggle">
|
||||
<v-btn value="normal" prepend-icon="mdi-cog" size="large">
|
||||
普通
|
||||
{{ tm('configSelection.normalConfig') }}
|
||||
</v-btn>
|
||||
<v-btn value="system" prepend-icon="mdi-cog-outline" size="large">
|
||||
系统
|
||||
{{ tm('configSelection.systemConfig') }}
|
||||
</v-btn>
|
||||
</v-btn-toggle>
|
||||
</div>
|
||||
@@ -59,7 +59,7 @@
|
||||
<v-btn icon @click="codeEditorDialog = false">
|
||||
<v-icon>mdi-close</v-icon>
|
||||
</v-btn>
|
||||
<v-toolbar-title>编辑配置文件</v-toolbar-title>
|
||||
<v-toolbar-title>{{ tm('codeEditor.title') }}</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
<v-toolbar-items style="display: flex; align-items: center;">
|
||||
<v-btn style="margin-left: 16px;" size="small" @click="configToString()">{{
|
||||
@@ -81,15 +81,15 @@
|
||||
<v-dialog v-model="configManageDialog" max-width="800px">
|
||||
<v-card>
|
||||
<v-card-title class="d-flex align-center justify-space-between">
|
||||
<span class="text-h4">配置文件管理</span>
|
||||
<span class="text-h4">{{ tm('configManagement.title') }}</span>
|
||||
<v-btn icon="mdi-close" variant="text" @click="configManageDialog = false"></v-btn>
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text>
|
||||
<small>AstrBot 支持针对不同机器人分别设置配置文件。默认会使用 `default` 配置。</small>
|
||||
<small>{{ tm('configManagement.description') }}</small>
|
||||
<div class="mt-6 mb-4">
|
||||
<v-btn prepend-icon="mdi-plus" @click="startCreateConfig" variant="tonal" color="primary">
|
||||
新建配置文件
|
||||
{{ tm('configManagement.newConfig') }}
|
||||
</v-btn>
|
||||
</div>
|
||||
|
||||
@@ -111,18 +111,18 @@
|
||||
<v-divider v-if="showConfigForm" class="my-6"></v-divider>
|
||||
|
||||
<div v-if="showConfigForm">
|
||||
<h3 class="mb-4">{{ isEditingConfig ? '编辑配置文件' : '新建配置文件' }}</h3>
|
||||
<h3 class="mb-4">{{ isEditingConfig ? tm('configManagement.editConfig') : tm('configManagement.newConfig') }}</h3>
|
||||
|
||||
<h4>名称</h4>
|
||||
<h4>{{ tm('configManagement.configName') }}</h4>
|
||||
|
||||
<v-text-field v-model="configFormData.name" label="填写配置文件名称" variant="outlined" class="mt-4 mb-4"
|
||||
<v-text-field v-model="configFormData.name" :label="tm('configManagement.fillConfigName')" variant="outlined" class="mt-4 mb-4"
|
||||
hide-details></v-text-field>
|
||||
|
||||
<div class="d-flex justify-end mt-4" style="gap: 8px;">
|
||||
<v-btn variant="text" @click="cancelConfigForm">取消</v-btn>
|
||||
<v-btn variant="text" @click="cancelConfigForm">{{ tm('buttons.cancel') }}</v-btn>
|
||||
<v-btn color="primary" @click="saveConfigForm"
|
||||
:disabled="!configFormData.name">
|
||||
{{ isEditingConfig ? '更新' : '创建' }}
|
||||
{{ isEditingConfig ? tm('buttons.update') : tm('buttons.create') }}
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
@@ -188,7 +188,7 @@ export default {
|
||||
const items = [...this.configInfoList];
|
||||
items.push({
|
||||
id: '_%manage%_',
|
||||
name: '管理配置文件...',
|
||||
name: this.tm('configManagement.manageConfigs'),
|
||||
umop: []
|
||||
});
|
||||
return items;
|
||||
@@ -367,7 +367,7 @@ export default {
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
this.save_message = "新配置文件创建失败";
|
||||
this.save_message = this.tm('configManagement.createFailed');
|
||||
this.save_message_snack = true;
|
||||
this.save_message_success = "error";
|
||||
});
|
||||
@@ -410,7 +410,7 @@ export default {
|
||||
},
|
||||
saveConfigForm() {
|
||||
if (!this.configFormData.name) {
|
||||
this.save_message = "请填写配置名称";
|
||||
this.save_message = this.tm('configManagement.pleaseEnterName');
|
||||
this.save_message_snack = true;
|
||||
this.save_message_success = "error";
|
||||
return;
|
||||
@@ -423,7 +423,7 @@ export default {
|
||||
}
|
||||
},
|
||||
confirmDeleteConfig(config) {
|
||||
if (confirm(`确定要删除配置文件 "${config.name}" 吗?此操作不可恢复。`)) {
|
||||
if (confirm(this.tm('configManagement.confirmDelete').replace('{name}', config.name))) {
|
||||
this.deleteConfig(config.id);
|
||||
}
|
||||
},
|
||||
@@ -445,7 +445,7 @@ export default {
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
this.save_message = "删除配置文件失败";
|
||||
this.save_message = this.tm('configManagement.deleteFailed');
|
||||
this.save_message_snack = true;
|
||||
this.save_message_success = "error";
|
||||
});
|
||||
@@ -468,7 +468,7 @@ export default {
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
this.save_message = "更新配置文件失败";
|
||||
this.save_message = this.tm('configManagement.updateFailed');
|
||||
this.save_message_snack = true;
|
||||
this.save_message_success = "error";
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
import builtins
|
||||
|
||||
from astrbot.api import star
|
||||
from astrbot.api import sp, star
|
||||
from astrbot.api.event import AstrMessageEvent, MessageEventResult
|
||||
|
||||
|
||||
@@ -17,6 +17,13 @@ class PersonaCommands:
|
||||
default_persona = await self.context.persona_manager.get_default_persona_v3(
|
||||
umo=umo,
|
||||
)
|
||||
|
||||
force_applied_persona_id = (
|
||||
await sp.get_async(
|
||||
scope="umo", scope_id=umo, key="session_service_config", default={}
|
||||
)
|
||||
).get("persona_id")
|
||||
|
||||
curr_cid_title = "无"
|
||||
if cid:
|
||||
conv = await self.context.conversation_manager.get_conversation(
|
||||
@@ -36,6 +43,9 @@ class PersonaCommands:
|
||||
else:
|
||||
curr_persona_name = conv.persona_id
|
||||
|
||||
if force_applied_persona_id:
|
||||
curr_persona_name = f"{curr_persona_name} (自定义规则)"
|
||||
|
||||
curr_cid_title = conv.title if conv.title else "新对话"
|
||||
curr_cid_title += f"({cid[:4]})"
|
||||
|
||||
@@ -113,9 +123,15 @@ class PersonaCommands:
|
||||
message.unified_msg_origin,
|
||||
ps,
|
||||
)
|
||||
force_warn_msg = ""
|
||||
if force_applied_persona_id:
|
||||
force_warn_msg = (
|
||||
"提醒:由于自定义规则,您现在切换的人格将不会生效。"
|
||||
)
|
||||
|
||||
message.set_result(
|
||||
MessageEventResult().message(
|
||||
"设置成功。如果您正在切换到不同的人格,请注意使用 /reset 来清空上下文,防止原人格对话影响现人格。",
|
||||
f"设置成功。如果您正在切换到不同的人格,请注意使用 /reset 来清空上下文,防止原人格对话影响现人格。{force_warn_msg}",
|
||||
),
|
||||
)
|
||||
else:
|
||||
|
||||
@@ -3,7 +3,7 @@ import copy
|
||||
import datetime
|
||||
import zoneinfo
|
||||
|
||||
from astrbot.api import logger, star
|
||||
from astrbot.api import logger, sp, star
|
||||
from astrbot.api.event import AstrMessageEvent
|
||||
from astrbot.api.message_components import Image, Reply
|
||||
from astrbot.api.provider import Provider, ProviderRequest
|
||||
@@ -21,16 +21,27 @@ class ProcessLLMRequest:
|
||||
else:
|
||||
logger.info(f"Timezone set to: {self.timezone}")
|
||||
|
||||
def _ensure_persona(self, req: ProviderRequest, cfg: dict):
|
||||
async def _ensure_persona(self, req: ProviderRequest, cfg: dict, umo: str):
|
||||
"""确保用户人格已加载"""
|
||||
if not req.conversation:
|
||||
return
|
||||
# persona inject
|
||||
persona_id = req.conversation.persona_id or cfg.get("default_personality")
|
||||
if not persona_id and persona_id != "[%None]": # [%None] 为用户取消人格
|
||||
default_persona = self.ctx.persona_manager.selected_default_persona_v3
|
||||
if default_persona:
|
||||
persona_id = default_persona["name"]
|
||||
|
||||
# custom rule is preferred
|
||||
persona_id = (
|
||||
await sp.get_async(
|
||||
scope="umo", scope_id=umo, key="session_service_config", default={}
|
||||
)
|
||||
).get("persona_id")
|
||||
|
||||
if not persona_id:
|
||||
persona_id = req.conversation.persona_id or cfg.get("default_personality")
|
||||
if not persona_id and persona_id != "[%None]": # [%None] 为用户取消人格
|
||||
default_persona = self.ctx.persona_manager.selected_default_persona_v3
|
||||
if default_persona:
|
||||
persona_id = default_persona["name"]
|
||||
|
||||
|
||||
persona = next(
|
||||
builtins.filter(
|
||||
lambda persona: persona["name"] == persona_id,
|
||||
@@ -152,7 +163,7 @@ class ProcessLLMRequest:
|
||||
img_cap_prov_id: str = cfg.get("default_image_caption_provider_id") or ""
|
||||
if req.conversation:
|
||||
# inject persona for this request
|
||||
self._ensure_persona(req, cfg)
|
||||
await self._ensure_persona(req, cfg, event.unified_msg_origin)
|
||||
|
||||
# image caption
|
||||
if img_cap_prov_id and req.image_urls:
|
||||
|
||||
Reference in New Issue
Block a user