mirror of
https://github.com/zhayujie/chatgpt-on-wechat.git
synced 2026-06-02 00:57:41 +08:00
feat(i18n): add global language resolution and localize user-facing text
This commit is contained in:
@@ -23,6 +23,7 @@ from plugins import Plugin, Event, EventContext, EventAction
|
||||
from bridge.context import ContextType
|
||||
from bridge.reply import Reply, ReplyType
|
||||
from common.log import logger
|
||||
from common.i18n import t as _t
|
||||
from config import conf
|
||||
from cli import __version__
|
||||
|
||||
@@ -280,19 +281,18 @@ class CowCliPlugin(Plugin):
|
||||
|
||||
@staticmethod
|
||||
def _typo_hint(token: str, suggestion) -> str:
|
||||
hint = f"未知命令: /{token}"
|
||||
hint = _t(f"未知命令: /{token}", f"Unknown command: /{token}")
|
||||
if suggestion:
|
||||
hint += f"\n你是不是想输入 /{suggestion} ?"
|
||||
hint += "\n发送 /help 查看全部命令。"
|
||||
hint += _t(f"\n你是不是想输入 /{suggestion} ?", f"\nDid you mean /{suggestion} ?")
|
||||
hint += _t("\n发送 /help 查看全部命令。", "\nSend /help to see all commands.")
|
||||
return hint
|
||||
|
||||
@staticmethod
|
||||
def _ambiguous_hint(token: str, candidates) -> str:
|
||||
options = " ".join(f"/{c}" for c in candidates)
|
||||
return (
|
||||
f"命令不明确: /{token}\n"
|
||||
f"可能想输入: {options}\n"
|
||||
"发送 /help 查看全部命令。"
|
||||
return _t(
|
||||
f"命令不明确: /{token}\n可能想输入: {options}\n发送 /help 查看全部命令。",
|
||||
f"Ambiguous command: /{token}\nDid you mean: {options}\nSend /help to see all commands.",
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
@@ -324,7 +324,10 @@ class CowCliPlugin(Plugin):
|
||||
|
||||
def _dispatch(self, cmd: str, args: str, e_context: EventContext, session_id: str = "") -> str:
|
||||
if cmd in CLI_ONLY_COMMANDS:
|
||||
return f"⚠️ `cow {cmd}` 只能在命令行终端中执行。\n请在终端运行: cow {cmd}"
|
||||
return _t(
|
||||
f"⚠️ `cow {cmd}` 只能在命令行终端中执行。\n请在终端运行: cow {cmd}",
|
||||
f"⚠️ `cow {cmd}` can only run in a terminal.\nRun it in your shell: cow {cmd}",
|
||||
)
|
||||
|
||||
handler_attr = "_cmd_" + cmd.replace("-", "_")
|
||||
handler = getattr(self, handler_attr, None)
|
||||
@@ -333,42 +336,71 @@ class CowCliPlugin(Plugin):
|
||||
return handler(args, e_context, session_id=session_id)
|
||||
except Exception as e:
|
||||
logger.error(f"[CowCli] command '{cmd}' failed: {e}")
|
||||
return f"命令执行失败: {e}"
|
||||
return _t(f"命令执行失败: {e}", f"Command failed: {e}")
|
||||
|
||||
return f"未知命令: {cmd}"
|
||||
return _t(f"未知命令: {cmd}", f"Unknown command: {cmd}")
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# help / version
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _cmd_help(self, args: str, e_context, **_) -> str:
|
||||
lines = [
|
||||
"📋 CowAgent 命令列表",
|
||||
"",
|
||||
" /help 显示此帮助",
|
||||
" /version 查看版本",
|
||||
" /status 查看运行状态",
|
||||
" /cancel 中止当前正在运行的 Agent 任务",
|
||||
" /logs [N] 查看最近N条日志 (默认20)",
|
||||
" /context 查看当前对话上下文信息",
|
||||
" /context clear 清除当前对话上下文",
|
||||
" /skill list 查看已安装的技能",
|
||||
" /skill list --remote 浏览技能广场",
|
||||
" /skill search <关键词> 搜索技能",
|
||||
" /skill install <名称> 安装技能",
|
||||
" /skill info <名称> 查看技能详情",
|
||||
" /config 查看当前配置",
|
||||
" /config <key> 查看某项配置",
|
||||
" /config <key> <val> 修改配置",
|
||||
" /memory status 查看记忆索引状态",
|
||||
" /memory rebuild-index 清空并重建向量索引 (切换 embedding 模型后必须执行)",
|
||||
" /memory dream [N] 手动触发记忆蒸馏 (整理近N天, 默认3, 最多30)",
|
||||
" /knowledge 查看知识库统计",
|
||||
" /knowledge list 查看知识库文件树",
|
||||
" /knowledge on|off 开启/关闭知识库",
|
||||
"",
|
||||
"💡 也可以用 cow <command> 代替 /<command>",
|
||||
]
|
||||
if _t("zh", "en") == "en":
|
||||
lines = [
|
||||
"📋 CowAgent Commands",
|
||||
"",
|
||||
"/help: Show this help",
|
||||
"/version: Show version",
|
||||
"/status: Show running status",
|
||||
"/cancel: Abort the running Agent task",
|
||||
"/logs [N]: Show the last N log lines (default 20)",
|
||||
"/context: Show current conversation context",
|
||||
"/context clear: Clear current conversation context",
|
||||
"/skill list: List installed skills",
|
||||
"/skill list --remote: Browse Skill Hub",
|
||||
"/skill search <keyword>: Search skills",
|
||||
"/skill install <name>: Install a skill",
|
||||
"/skill info <name>: Show skill details",
|
||||
"/config: Show current config",
|
||||
"/config <key>: Show a config item",
|
||||
"/config <key> <val>: Update a config item",
|
||||
"/memory status: Show memory index status",
|
||||
"/memory rebuild-index: Rebuild the vector index (required after switching embedding model)",
|
||||
"/memory dream [N]: Trigger memory distillation (last N days, default 3, max 30)",
|
||||
"/knowledge: Show knowledge base stats",
|
||||
"/knowledge list: Show knowledge base file tree",
|
||||
"/knowledge on|off: Enable/disable knowledge base",
|
||||
"",
|
||||
"💡 You can also use cow <command> instead of /<command>",
|
||||
]
|
||||
else:
|
||||
lines = [
|
||||
"📋 CowAgent 命令列表",
|
||||
"",
|
||||
"/help: 显示此帮助",
|
||||
"/version: 查看版本",
|
||||
"/status: 查看运行状态",
|
||||
"/cancel: 中止当前正在运行的 Agent 任务",
|
||||
"/logs [N]: 查看最近N条日志 (默认20)",
|
||||
"/context: 查看当前对话上下文信息",
|
||||
"/context clear: 清除当前对话上下文",
|
||||
"/skill list: 查看已安装的技能",
|
||||
"/skill list --remote: 浏览技能广场",
|
||||
"/skill search <关键词>: 搜索技能",
|
||||
"/skill install <名称>: 安装技能",
|
||||
"/skill info <名称>: 查看技能详情",
|
||||
"/config: 查看当前配置",
|
||||
"/config <key>: 查看某项配置",
|
||||
"/config <key> <val>: 修改配置",
|
||||
"/memory status: 查看记忆索引状态",
|
||||
"/memory rebuild-index: 清空并重建向量索引 (切换 embedding 模型后必须执行)",
|
||||
"/memory dream [N]: 手动触发记忆蒸馏 (整理近N天, 默认3, 最多30)",
|
||||
"/knowledge: 查看知识库统计",
|
||||
"/knowledge list: 查看知识库文件树",
|
||||
"/knowledge on|off: 开启/关闭知识库",
|
||||
"",
|
||||
"💡 也可以用 cow <command> 代替 /<command>",
|
||||
]
|
||||
return "\n".join(lines)
|
||||
|
||||
def _cmd_version(self, args: str, e_context, **_) -> str:
|
||||
@@ -405,9 +437,9 @@ class CowCliPlugin(Plugin):
|
||||
cancelled = registry.cancel_session(target_session)
|
||||
|
||||
if cancelled <= 0:
|
||||
return "当前没有可中止的任务。"
|
||||
return _t("当前没有可中止的任务。", "Nothing to cancel.")
|
||||
|
||||
return "🛑 已中止"
|
||||
return _t("🛑 已中止", "🛑 Cancelled")
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# status
|
||||
@@ -417,21 +449,21 @@ class CowCliPlugin(Plugin):
|
||||
from config import conf
|
||||
|
||||
cfg = conf()
|
||||
lines = ["📊 CowAgent 运行状态", ""]
|
||||
lines = [_t("📊 CowAgent 运行状态", "📊 CowAgent Status"), ""]
|
||||
|
||||
lines.append(f" 版本: v{__version__}")
|
||||
lines.append(f" 进程: PID {os.getpid()}")
|
||||
lines.append(_t(f" 版本: v{__version__}", f" Version: v{__version__}"))
|
||||
lines.append(_t(f" 进程: PID {os.getpid()}", f" Process: PID {os.getpid()}"))
|
||||
|
||||
channel = cfg.get("channel_type", "unknown")
|
||||
if isinstance(channel, list):
|
||||
channel = ", ".join(channel)
|
||||
lines.append(f" 通道: {channel}")
|
||||
lines.append(_t(f" 通道: {channel}", f" Channel: {channel}"))
|
||||
|
||||
model_name = cfg.get("model", "unknown")
|
||||
lines.append(f" 模型: {model_name}")
|
||||
lines.append(_t(f" 模型: {model_name}", f" Model: {model_name}"))
|
||||
|
||||
mode = "Chat" if cfg.get("agent") is False else "Agent"
|
||||
lines.append(f" 模式: {mode}")
|
||||
lines.append(_t(f" 模式: {mode}", f" Mode: {mode}"))
|
||||
|
||||
session_id = self._get_session_id(e_context, fallback=session_id)
|
||||
agent = self._get_agent(session_id)
|
||||
@@ -439,7 +471,7 @@ class CowCliPlugin(Plugin):
|
||||
lines.append("")
|
||||
with agent.messages_lock:
|
||||
msg_count = len(agent.messages)
|
||||
lines.append(f" 会话消息数: {msg_count}")
|
||||
lines.append(_t(f" 会话消息数: {msg_count}", f" Session messages: {msg_count}"))
|
||||
|
||||
if agent.skill_manager:
|
||||
total = len(agent.skill_manager.skills)
|
||||
@@ -447,10 +479,10 @@ class CowCliPlugin(Plugin):
|
||||
1 for v in agent.skill_manager.skills_config.values()
|
||||
if v.get("enabled", True)
|
||||
)
|
||||
lines.append(f" 已加载技能: {enabled}/{total}")
|
||||
lines.append(_t(f" 已加载技能: {enabled}/{total}", f" Loaded skills: {enabled}/{total}"))
|
||||
else:
|
||||
lines.append("")
|
||||
lines.append(f" Agent: 未初始化 (首次对话后自动创建)")
|
||||
lines.append(_t(" Agent: 未初始化 (首次对话后自动创建)", " Agent: not initialized (created on first chat)"))
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
@@ -465,7 +497,7 @@ class CowCliPlugin(Plugin):
|
||||
|
||||
log_file = self._find_log_file()
|
||||
if not log_file:
|
||||
return "未找到日志文件"
|
||||
return _t("未找到日志文件", "No log file found")
|
||||
|
||||
try:
|
||||
with open(log_file, "r", encoding="utf-8", errors="replace") as f:
|
||||
@@ -473,10 +505,10 @@ class CowCliPlugin(Plugin):
|
||||
tail = all_lines[-num_lines:]
|
||||
content = "".join(tail).strip()
|
||||
if not content:
|
||||
return "日志为空"
|
||||
return f"📄 最近 {len(tail)} 条日志:\n\n{content}"
|
||||
return _t("日志为空", "Log is empty")
|
||||
return _t(f"📄 最近 {len(tail)} 条日志:\n\n{content}", f"📄 Last {len(tail)} log lines:\n\n{content}")
|
||||
except Exception as e:
|
||||
return f"读取日志失败: {e}"
|
||||
return _t(f"读取日志失败: {e}", f"Failed to read log: {e}")
|
||||
|
||||
def _find_log_file(self) -> str:
|
||||
project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
@@ -507,13 +539,13 @@ class CowCliPlugin(Plugin):
|
||||
|
||||
def _context_info(self, agent, session_id: str) -> str:
|
||||
if not agent:
|
||||
return "⚠️ Agent 未初始化,暂无上下文信息"
|
||||
return _t("⚠️ Agent 未初始化,暂无上下文信息", "⚠️ Agent not initialized, no context yet")
|
||||
|
||||
with agent.messages_lock:
|
||||
messages = agent.messages.copy()
|
||||
|
||||
if not messages:
|
||||
return "当前对话上下文为空"
|
||||
return _t("当前对话上下文为空", "Current conversation context is empty")
|
||||
|
||||
user_msgs = sum(1 for m in messages if m.get("role") == "user")
|
||||
assistant_msgs = sum(1 for m in messages if m.get("role") == "assistant")
|
||||
@@ -521,29 +553,43 @@ class CowCliPlugin(Plugin):
|
||||
|
||||
total_chars = sum(len(str(m.get("content", ""))) for m in messages)
|
||||
|
||||
lines = [
|
||||
"💬 当前对话上下文",
|
||||
"",
|
||||
f" 会话: {session_id or 'default'}",
|
||||
f" 总消息数: {len(messages)}",
|
||||
f" 用户消息: {user_msgs}",
|
||||
f" 助手回复: {assistant_msgs}",
|
||||
f" 工具调用: {tool_msgs}",
|
||||
f" 内容总长度: ~{total_chars} 字符",
|
||||
"",
|
||||
" 发送 /context clear 可清除对话上下文",
|
||||
]
|
||||
if _t("zh", "en") == "en":
|
||||
lines = [
|
||||
"💬 Current Conversation Context",
|
||||
"",
|
||||
f" Session: {session_id or 'default'}",
|
||||
f" Total messages: {len(messages)}",
|
||||
f" User messages: {user_msgs}",
|
||||
f" Assistant replies: {assistant_msgs}",
|
||||
f" Tool calls: {tool_msgs}",
|
||||
f" Total content length: ~{total_chars} chars",
|
||||
"",
|
||||
" Send /context clear to clear the conversation context",
|
||||
]
|
||||
else:
|
||||
lines = [
|
||||
"💬 当前对话上下文",
|
||||
"",
|
||||
f" 会话: {session_id or 'default'}",
|
||||
f" 总消息数: {len(messages)}",
|
||||
f" 用户消息: {user_msgs}",
|
||||
f" 助手回复: {assistant_msgs}",
|
||||
f" 工具调用: {tool_msgs}",
|
||||
f" 内容总长度: ~{total_chars} 字符",
|
||||
"",
|
||||
" 发送 /context clear 可清除对话上下文",
|
||||
]
|
||||
return "\n".join(lines)
|
||||
|
||||
def _context_clear(self, agent, session_id: str) -> str:
|
||||
if not agent:
|
||||
return "⚠️ Agent 未初始化"
|
||||
return _t("⚠️ Agent 未初始化", "⚠️ Agent not initialized")
|
||||
|
||||
with agent.messages_lock:
|
||||
count = len(agent.messages)
|
||||
agent.messages.clear()
|
||||
|
||||
return f"✅ 已清除当前对话上下文 ({count} 条消息)"
|
||||
return _t(f"✅ 已清除当前对话上下文 ({count} 条消息)", f"✅ Conversation context cleared ({count} messages)")
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# config
|
||||
@@ -578,21 +624,24 @@ class CowCliPlugin(Plugin):
|
||||
def _config_show_all(self) -> str:
|
||||
from config import conf
|
||||
cfg = conf()
|
||||
lines = ["⚙️ 当前配置", ""]
|
||||
lines = [_t("⚙️ 当前配置", "⚙️ Current Config"), ""]
|
||||
for key in sorted(self._CONFIG_READABLE):
|
||||
val = cfg.get(key, "")
|
||||
lines.append(f" {key}: {val}")
|
||||
lines.append("")
|
||||
lines.append("━━━━━━━━━━━━━━━━━━━━━━━━━━")
|
||||
lines.append("💡 /config <key> 查看配置")
|
||||
lines.append("💡 /config <key> <val> 修改配置")
|
||||
lines.append(_t("💡 /config <key>: 查看配置", "💡 /config <key>: Show a config item"))
|
||||
lines.append(_t("💡 /config <key> <val>: 修改配置", "💡 /config <key> <val>: Update a config item"))
|
||||
return "\n".join(lines)
|
||||
|
||||
def _config_get(self, key: str) -> str:
|
||||
from config import conf
|
||||
if key not in self._CONFIG_READABLE:
|
||||
available = ", ".join(sorted(self._CONFIG_READABLE))
|
||||
return f"不支持查看 '{key}'\n\n可查看的配置项: {available}"
|
||||
return _t(
|
||||
f"不支持查看 '{key}'\n\n可查看的配置项: {available}",
|
||||
f"Cannot show '{key}'\n\nReadable config items: {available}",
|
||||
)
|
||||
val = conf().get(key, "")
|
||||
return f"⚙️ {key}: {val}"
|
||||
|
||||
@@ -602,9 +651,12 @@ class CowCliPlugin(Plugin):
|
||||
|
||||
if key not in self._CONFIG_WRITABLE:
|
||||
if key in self._CONFIG_READABLE:
|
||||
return f"⚠️ '{key}' 为只读配置,不支持修改"
|
||||
return _t(f"⚠️ '{key}' 为只读配置,不支持修改", f"⚠️ '{key}' is read-only and cannot be modified")
|
||||
available = ", ".join(sorted(self._CONFIG_WRITABLE))
|
||||
return f"不支持修改 '{key}'\n\n可修改的配置项: {available}"
|
||||
return _t(
|
||||
f"不支持修改 '{key}'\n\n可修改的配置项: {available}",
|
||||
f"Cannot modify '{key}'\n\nWritable config items: {available}",
|
||||
)
|
||||
|
||||
old_val = conf().get(key, "")
|
||||
|
||||
@@ -637,7 +689,7 @@ class CowCliPlugin(Plugin):
|
||||
with open(config_path, "w", encoding="utf-8") as f:
|
||||
_json.dump(file_config, f, indent=4, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
return f"写入 config.json 失败: {e}"
|
||||
return _t(f"写入 config.json 失败: {e}", f"Failed to write config.json: {e}")
|
||||
|
||||
# Sync updated values to environment variables so that load_config()
|
||||
# won't overwrite the new value with a stale env var (common in Docker).
|
||||
@@ -660,7 +712,7 @@ class CowCliPlugin(Plugin):
|
||||
except Exception as e:
|
||||
logger.warning(f"[CowCli] config reload warning: {e}")
|
||||
|
||||
result = f"✅ 配置已更新\n\n {key}: {old_val} → {new_val}"
|
||||
result = _t(f"✅ 配置已更新\n\n {key}: {old_val} → {new_val}", f"✅ Config updated\n\n {key}: {old_val} → {new_val}")
|
||||
if "bot_type" in updates and updates["bot_type"] != old_bot_type:
|
||||
result += f"\n bot_type: {old_bot_type} → {updates['bot_type']}"
|
||||
return result
|
||||
@@ -725,10 +777,13 @@ class CowCliPlugin(Plugin):
|
||||
from cli.commands.install import run_install_browser
|
||||
|
||||
if args.strip():
|
||||
return (
|
||||
return _t(
|
||||
"用法: /install-browser\n\n"
|
||||
"无需参数,等同于终端执行 `cow install-browser`。\n"
|
||||
"安装过程可能持续数分钟;进度会以多条消息推送,pip 详细输出见服务日志。"
|
||||
"安装过程可能持续数分钟;进度会以多条消息推送,pip 详细输出见服务日志。",
|
||||
"Usage: /install-browser\n\n"
|
||||
"No arguments needed; equivalent to running `cow install-browser` in a terminal.\n"
|
||||
"Installation may take a few minutes; progress is pushed as multiple messages, and detailed pip output goes to the service log.",
|
||||
)
|
||||
|
||||
# Suppress detailed stream in chat; phases go through channel.send
|
||||
@@ -740,11 +795,16 @@ class CowCliPlugin(Plugin):
|
||||
on_phase=lambda m: self._send_install_progress(e_context, m),
|
||||
)
|
||||
if code != 0:
|
||||
return (
|
||||
return _t(
|
||||
"❌ 安装未成功结束,请查看上方分段提示或服务器日志;"
|
||||
"也可在终端执行 `cow install-browser`。"
|
||||
"也可在终端执行 `cow install-browser`。",
|
||||
"❌ Installation did not finish successfully. Check the messages above or the server log; "
|
||||
"you can also run `cow install-browser` in a terminal.",
|
||||
)
|
||||
return "✅ 安装流程已结束。请重启 CowAgent 后使用 browser 工具(进度见上方消息)。"
|
||||
return _t(
|
||||
"✅ 安装流程已结束。请重启 CowAgent 后使用 browser 工具(进度见上方消息)。",
|
||||
"✅ Installation finished. Restart CowAgent to use the browser tool (see messages above for progress).",
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# skill
|
||||
@@ -770,16 +830,25 @@ class CowCliPlugin(Plugin):
|
||||
elif sub == "disable":
|
||||
return self._skill_set_enabled(sub_args, False)
|
||||
else:
|
||||
return (
|
||||
return _t(
|
||||
"用法: /skill <子命令>\n\n"
|
||||
"子命令:\n"
|
||||
" list [--remote] 查看技能列表\n"
|
||||
" search <关键词> 搜索技能\n"
|
||||
" install <名称> 安装技能\n"
|
||||
" uninstall <名称> 卸载技能\n"
|
||||
" info <名称> 查看技能详情\n"
|
||||
" enable <名称> 启用技能\n"
|
||||
" disable <名称> 禁用技能"
|
||||
"list [--remote]: 查看技能列表\n"
|
||||
"search <关键词>: 搜索技能\n"
|
||||
"install <名称>: 安装技能\n"
|
||||
"uninstall <名称>: 卸载技能\n"
|
||||
"info <名称>: 查看技能详情\n"
|
||||
"enable <名称>: 启用技能\n"
|
||||
"disable <名称>: 禁用技能",
|
||||
"Usage: /skill <subcommand>\n\n"
|
||||
"Subcommands:\n"
|
||||
"list [--remote]: List skills\n"
|
||||
"search <keyword>: Search skills\n"
|
||||
"install <name>: Install a skill\n"
|
||||
"uninstall <name>: Uninstall a skill\n"
|
||||
"info <name>: Show skill details\n"
|
||||
"enable <name>: Enable a skill\n"
|
||||
"disable <name>: Disable a skill",
|
||||
)
|
||||
|
||||
def _refresh_skill_manager(self):
|
||||
@@ -813,13 +882,16 @@ class CowCliPlugin(Plugin):
|
||||
if os.path.exists(os.path.join(skill_path, "SKILL.md")):
|
||||
entries.append({"name": name, "source": source, "enabled": True})
|
||||
if not entries:
|
||||
return "暂无已安装的技能\n\n💡 /skill list --remote 浏览技能广场"
|
||||
return _t(
|
||||
"暂无已安装的技能\n\n💡 /skill list --remote: 浏览技能广场",
|
||||
"No skills installed yet\n\n💡 /skill list --remote: Browse Skill Hub",
|
||||
)
|
||||
config = {e["name"]: e for e in entries}
|
||||
|
||||
sorted_entries = sorted(config.values(), key=lambda e: e.get("name", ""))
|
||||
enabled_count = sum(1 for e in sorted_entries if e.get("enabled", True))
|
||||
|
||||
lines = [f"📦 已安装的技能 ({enabled_count}/{len(sorted_entries)})", ""]
|
||||
lines = [_t(f"📦 已安装的技能 ({enabled_count}/{len(sorted_entries)})", f"📦 Installed Skills ({enabled_count}/{len(sorted_entries)})"), ""]
|
||||
for entry in sorted_entries:
|
||||
name = entry.get("name", "")
|
||||
enabled = entry.get("enabled", True)
|
||||
@@ -835,13 +907,13 @@ class CowCliPlugin(Plugin):
|
||||
if desc:
|
||||
line += f"\n {desc}"
|
||||
if source:
|
||||
line += f"\n 来源: {source}"
|
||||
line += _t(f"\n 来源: {source}", f"\n Source: {source}")
|
||||
lines.append(line)
|
||||
lines.append("")
|
||||
|
||||
lines.append("━━━━━━━━━━━━━━━━━━━━━━━━━━")
|
||||
lines.append("💡 /skill list --remote 浏览技能广场")
|
||||
lines.append("💡 /skill info <名称> 查看详情")
|
||||
lines.append(_t("💡 /skill list --remote: 浏览技能广场", "💡 /skill list --remote: Browse Skill Hub"))
|
||||
lines.append(_t("💡 /skill info <名称>: 查看详情", "💡 /skill info <name>: Show details"))
|
||||
return "\n".join(lines)
|
||||
|
||||
def _skill_list(self, args: str) -> str:
|
||||
@@ -871,43 +943,43 @@ class CowCliPlugin(Plugin):
|
||||
skills = data.get("skills", [])
|
||||
total = data.get("total", len(skills))
|
||||
except Exception as e:
|
||||
return f"获取技能广场失败: {e}"
|
||||
return _t(f"获取技能广场失败: {e}", f"Failed to fetch Skill Hub: {e}")
|
||||
|
||||
if not skills and page == 1:
|
||||
return "技能广场暂无可用技能"
|
||||
return _t("技能广场暂无可用技能", "No skills available on Skill Hub")
|
||||
|
||||
total_pages = max(1, (total + page_size - 1) // page_size)
|
||||
page = min(page, total_pages)
|
||||
installed = set(load_skills_config().keys())
|
||||
|
||||
lines = ["🌐 技能广场", ""]
|
||||
lines = [_t("🌐 技能广场", "🌐 Skill Hub"), ""]
|
||||
for s in skills:
|
||||
name = s.get("name", "")
|
||||
display = s.get("display_name", "") or name
|
||||
desc = s.get("description", "")
|
||||
if len(desc) > 50:
|
||||
desc = desc[:47] + "…"
|
||||
badge = " [已安装]" if name in installed else ""
|
||||
badge = _t(" [已安装]", " [installed]") if name in installed else ""
|
||||
lines.append(f"📌 {display}{badge}")
|
||||
lines.append(f" 名称: {name}")
|
||||
lines.append(_t(f" 名称: {name}", f" Name: {name}"))
|
||||
if desc:
|
||||
lines.append(f" {desc}")
|
||||
lines.append("")
|
||||
|
||||
lines.append("━━━━━━━━━━━━━━━━━━━━━━━━━━")
|
||||
lines.append(f"📄 第 {page}/{total_pages} 页")
|
||||
lines.append(_t(f"📄 第 {page}/{total_pages} 页", f"📄 Page {page}/{total_pages}"))
|
||||
if page < total_pages:
|
||||
lines.append(f"💡 /skill list --remote --page {page + 1} 下一页")
|
||||
lines.append(_t(f"💡 /skill list --remote --page {page + 1}: 下一页", f"💡 /skill list --remote --page {page + 1}: Next page"))
|
||||
if page > 1:
|
||||
lines.append(f"💡 /skill list --remote --page {page - 1} 上一页")
|
||||
lines.append("💡 /skill install <名称> 安装技能")
|
||||
lines.append("💡 /skill search <关键词> 搜索技能")
|
||||
lines.append("🌐 https://skills.cowagent.ai 在线浏览全部技能")
|
||||
lines.append(_t(f"💡 /skill list --remote --page {page - 1}: 上一页", f"💡 /skill list --remote --page {page - 1}: Previous page"))
|
||||
lines.append(_t("💡 /skill install <名称>: 安装技能", "💡 /skill install <name>: Install a skill"))
|
||||
lines.append(_t("💡 /skill search <关键词>: 搜索技能", "💡 /skill search <keyword>: Search skills"))
|
||||
lines.append(_t("🌐 https://skills.cowagent.ai 在线浏览全部技能", "🌐 https://skills.cowagent.ai Browse all skills online"))
|
||||
return "\n".join(lines)
|
||||
|
||||
def _skill_search(self, query: str) -> str:
|
||||
if not query:
|
||||
return "请指定搜索关键词: /skill search <关键词>"
|
||||
return _t("请指定搜索关键词: /skill search <关键词>", "Please specify a search keyword: /skill search <keyword>")
|
||||
|
||||
import requests
|
||||
from cli.utils import SKILL_HUB_API, load_skills_config
|
||||
@@ -916,35 +988,35 @@ class CowCliPlugin(Plugin):
|
||||
resp.raise_for_status()
|
||||
skills = resp.json().get("skills", [])
|
||||
except Exception as e:
|
||||
return f"搜索失败: {e}"
|
||||
return _t(f"搜索失败: {e}", f"Search failed: {e}")
|
||||
|
||||
if not skills:
|
||||
return f"未找到与「{query}」相关的技能"
|
||||
return _t(f"未找到与「{query}」相关的技能", f"No skills found for \"{query}\"")
|
||||
|
||||
installed = set(load_skills_config().keys())
|
||||
lines = [f"🔍 搜索「{query}」({len(skills)} 个结果)", ""]
|
||||
lines = [_t(f"🔍 搜索「{query}」({len(skills)} 个结果)", f"🔍 Search \"{query}\" ({len(skills)} results)"), ""]
|
||||
for s in skills:
|
||||
name = s.get("name", "")
|
||||
display = s.get("display_name", "") or name
|
||||
desc = s.get("description", "")
|
||||
if len(desc) > 50:
|
||||
desc = desc[:47] + "…"
|
||||
badge = " [已安装]" if name in installed else ""
|
||||
badge = _t(" [已安装]", " [installed]") if name in installed else ""
|
||||
lines.append(f"📌 {display}{badge}")
|
||||
lines.append(f" 名称: {name}")
|
||||
lines.append(_t(f" 名称: {name}", f" Name: {name}"))
|
||||
if desc:
|
||||
lines.append(f" {desc}")
|
||||
lines.append("")
|
||||
|
||||
lines.append("━━━━━━━━━━━━━━━━━━━━━━━━━━")
|
||||
lines.append("💡 /skill install <名称> 安装技能")
|
||||
lines.append(_t("💡 /skill install <名称>: 安装技能", "💡 /skill install <name>: Install a skill"))
|
||||
return "\n".join(lines)
|
||||
|
||||
_INSTALL_TIMEOUT = 60
|
||||
|
||||
def _skill_install(self, name: str, e_context: EventContext) -> str:
|
||||
if not name:
|
||||
return "请指定要安装的技能: /skill install <名称>"
|
||||
return _t("请指定要安装的技能: /skill install <名称>", "Please specify a skill to install: /skill install <name>")
|
||||
|
||||
from concurrent.futures import ThreadPoolExecutor, TimeoutError as FuturesTimeout
|
||||
from cli.commands.skill import install_skill
|
||||
@@ -955,16 +1027,16 @@ class CowCliPlugin(Plugin):
|
||||
result = future.result(timeout=self._INSTALL_TIMEOUT)
|
||||
|
||||
if result.error:
|
||||
return f"安装失败: {result.error}"
|
||||
return _t(f"安装失败: {result.error}", f"Install failed: {result.error}")
|
||||
|
||||
if not result.installed:
|
||||
return "\n".join(result.messages) if result.messages else "未找到可安装的技能"
|
||||
return "\n".join(result.messages) if result.messages else _t("未找到可安装的技能", "No installable skill found")
|
||||
|
||||
return self._format_install_result(result)
|
||||
except FuturesTimeout:
|
||||
return "安装超时,请稍后重试或检查网络连接"
|
||||
return _t("安装超时,请稍后重试或检查网络连接", "Install timed out. Please retry later or check your network connection.")
|
||||
except Exception as e:
|
||||
return f"安装失败: {e}"
|
||||
return _t(f"安装失败: {e}", f"Install failed: {e}")
|
||||
|
||||
@staticmethod
|
||||
def _format_install_result(result) -> str:
|
||||
@@ -978,20 +1050,20 @@ class CowCliPlugin(Plugin):
|
||||
for skill_name in result.installed:
|
||||
desc = _read_skill_description(os.path.join(skills_dir, skill_name))
|
||||
display = config.get(skill_name, {}).get("display_name", "")
|
||||
lines.append(f"✅ 技能安装成功:{skill_name}")
|
||||
lines.append(_t(f"✅ 技能安装成功:{skill_name}", f"✅ Skill installed: {skill_name}"))
|
||||
if display and display != skill_name:
|
||||
lines.append(f" 名称:{display}")
|
||||
lines.append(_t(f" 名称:{display}", f" Name: {display}"))
|
||||
if desc:
|
||||
lines.append(f" 描述:{desc}")
|
||||
lines.append(_t(f" 描述:{desc}", f" Description: {desc}"))
|
||||
|
||||
if len(result.installed) > 1:
|
||||
lines.append(f"\n共安装 {len(result.installed)} 个技能")
|
||||
lines.append(_t(f"\n共安装 {len(result.installed)} 个技能", f"\nInstalled {len(result.installed)} skills"))
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
def _skill_uninstall(self, name: str) -> str:
|
||||
if not name:
|
||||
return "请指定要卸载的技能: /skill uninstall <名称>"
|
||||
return _t("请指定要卸载的技能: /skill uninstall <名称>", "Please specify a skill to uninstall: /skill uninstall <name>")
|
||||
|
||||
import shutil
|
||||
import json
|
||||
@@ -1004,7 +1076,7 @@ class CowCliPlugin(Plugin):
|
||||
skill_dir = self._resolve_skill_dir(name, skills_dir)
|
||||
|
||||
if not skill_dir:
|
||||
return f"技能 '{name}' 未安装"
|
||||
return _t(f"技能 '{name}' 未安装", f"Skill '{name}' is not installed")
|
||||
|
||||
shutil.rmtree(skill_dir)
|
||||
|
||||
@@ -1019,7 +1091,7 @@ class CowCliPlugin(Plugin):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return f"✅ 技能 '{name}' 已卸载"
|
||||
return _t(f"✅ 技能 '{name}' 已卸载", f"✅ Skill '{name}' uninstalled")
|
||||
|
||||
@staticmethod
|
||||
def _resolve_skill_dir(name: str, skills_dir: str):
|
||||
@@ -1055,7 +1127,7 @@ class CowCliPlugin(Plugin):
|
||||
|
||||
def _skill_info(self, name: str) -> str:
|
||||
if not name:
|
||||
return "请指定技能名称: /skill info <名称>"
|
||||
return _t("请指定技能名称: /skill info <名称>", "Please specify a skill name: /skill info <name>")
|
||||
|
||||
from cli.utils import get_skills_dir, get_builtin_skills_dir
|
||||
|
||||
@@ -1078,18 +1150,18 @@ class CowCliPlugin(Plugin):
|
||||
source = "custom"
|
||||
|
||||
if not skill_dir:
|
||||
return f"技能 '{name}' 未找到"
|
||||
return _t(f"技能 '{name}' 未找到", f"Skill '{name}' not found")
|
||||
|
||||
skill_md = os.path.join(skill_dir, "SKILL.md")
|
||||
if not os.path.exists(skill_md):
|
||||
return f"技能 '{name}' 没有 SKILL.md 文件"
|
||||
return _t(f"技能 '{name}' 没有 SKILL.md 文件", f"Skill '{name}' has no SKILL.md file")
|
||||
|
||||
with open(skill_md, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
|
||||
meta, body = self._strip_frontmatter(content)
|
||||
|
||||
header_lines = [f"📖 技能: {name} [{source}]", ""]
|
||||
header_lines = [_t(f"📖 技能: {name} [{source}]", f"📖 Skill: {name} [{source}]"), ""]
|
||||
desc = meta.get("description", "")
|
||||
if desc:
|
||||
header_lines.append(f" {desc}")
|
||||
@@ -1104,8 +1176,10 @@ class CowCliPlugin(Plugin):
|
||||
|
||||
def _skill_set_enabled(self, name: str, enabled: bool) -> str:
|
||||
if not name:
|
||||
action = "启用" if enabled else "禁用"
|
||||
return f"请指定技能名称: /skill {'enable' if enabled else 'disable'} <名称>"
|
||||
return _t(
|
||||
f"请指定技能名称: /skill {'enable' if enabled else 'disable'} <名称>",
|
||||
f"Please specify a skill name: /skill {'enable' if enabled else 'disable'} <name>",
|
||||
)
|
||||
|
||||
import json
|
||||
from cli.utils import get_skills_dir
|
||||
@@ -1114,24 +1188,25 @@ class CowCliPlugin(Plugin):
|
||||
config_path = os.path.join(skills_dir, "skills_config.json")
|
||||
|
||||
if not os.path.exists(config_path):
|
||||
return "技能配置文件不存在"
|
||||
return _t("技能配置文件不存在", "Skills config file not found")
|
||||
|
||||
try:
|
||||
with open(config_path, "r", encoding="utf-8") as f:
|
||||
config = json.load(f)
|
||||
except Exception as e:
|
||||
return f"读取配置失败: {e}"
|
||||
return _t(f"读取配置失败: {e}", f"Failed to read config: {e}")
|
||||
|
||||
if name not in config:
|
||||
return f"技能 '{name}' 未在配置中找到"
|
||||
return _t(f"技能 '{name}' 未在配置中找到", f"Skill '{name}' not found in config")
|
||||
|
||||
config[name]["enabled"] = enabled
|
||||
with open(config_path, "w", encoding="utf-8") as f:
|
||||
json.dump(config, f, indent=4, ensure_ascii=False)
|
||||
|
||||
action = "启用" if enabled else "禁用"
|
||||
icon = "✅" if enabled else "⬚"
|
||||
return f"{icon} 技能 '{name}' 已{action}"
|
||||
if enabled:
|
||||
return _t(f"{icon} 技能 '{name}' 已启用", f"{icon} Skill '{name}' enabled")
|
||||
return _t(f"{icon} 技能 '{name}' 已禁用", f"{icon} Skill '{name}' disabled")
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# memory
|
||||
@@ -1157,13 +1232,19 @@ class CowCliPlugin(Plugin):
|
||||
|
||||
@staticmethod
|
||||
def _memory_help() -> str:
|
||||
return (
|
||||
return _t(
|
||||
"🧠 记忆管理\n\n"
|
||||
"用法: /memory <子命令>\n\n"
|
||||
"子命令:\n"
|
||||
" status 查看索引状态 (provider / model / dim / chunks)\n"
|
||||
" rebuild-index 清空并重建向量索引 (切换 embedding 模型后必须执行)\n"
|
||||
" dream [N] 手动触发记忆蒸馏 (整理近N天, 默认3, 最多30)"
|
||||
"status: 查看索引状态 (provider / model / dim / chunks)\n"
|
||||
"rebuild-index: 清空并重建向量索引 (切换 embedding 模型后必须执行)\n"
|
||||
"dream [N]: 手动触发记忆蒸馏 (整理近N天, 默认3, 最多30)",
|
||||
"🧠 Memory Management\n\n"
|
||||
"Usage: /memory <subcommand>\n\n"
|
||||
"Subcommands:\n"
|
||||
"status: Show index status (provider / model / dim / chunks)\n"
|
||||
"rebuild-index: Rebuild the vector index (required after switching embedding model)\n"
|
||||
"dream [N]: Trigger memory distillation (last N days, default 3, max 30)",
|
||||
)
|
||||
|
||||
def _memory_dream(self, days: int, e_context, session_id: str) -> str:
|
||||
@@ -1178,10 +1259,10 @@ class CowCliPlugin(Plugin):
|
||||
try:
|
||||
flush_mgr = self._create_standalone_flush_manager()
|
||||
except Exception as e:
|
||||
return f"⚠️ 无法初始化记忆蒸馏: {e}"
|
||||
return _t(f"⚠️ 无法初始化记忆蒸馏: {e}", f"⚠️ Failed to initialize memory distillation: {e}")
|
||||
|
||||
if not flush_mgr.llm_model:
|
||||
return "⚠️ 未配置 LLM 模型,无法执行记忆蒸馏"
|
||||
return _t("⚠️ 未配置 LLM 模型,无法执行记忆蒸馏", "⚠️ No LLM model configured, cannot run memory distillation")
|
||||
|
||||
# SaaS (e_context is None): run synchronously, return full result
|
||||
if e_context is None:
|
||||
@@ -1196,13 +1277,16 @@ class CowCliPlugin(Plugin):
|
||||
if result:
|
||||
self._notify(e_context, self._build_dream_result(flush_mgr, is_web))
|
||||
else:
|
||||
self._notify(e_context, "💤 记忆蒸馏跳过 — 没有新的记忆内容需要整理")
|
||||
self._notify(e_context, _t("💤 记忆蒸馏跳过 — 没有新的记忆内容需要整理", "💤 Memory distillation skipped — no new memories to process"))
|
||||
except Exception as e:
|
||||
logger.warning(f"[CowCli] /memory dream failed: {e}")
|
||||
self._notify(e_context, f"❌ 记忆蒸馏失败: {e}")
|
||||
self._notify(e_context, _t(f"❌ 记忆蒸馏失败: {e}", f"❌ Memory distillation failed: {e}"))
|
||||
|
||||
threading.Thread(target=_run, daemon=True).start()
|
||||
return f"🌙 记忆蒸馏已启动 (整理近 {days} 天的记忆)\n\n整理在后台执行,完成后会通知你。"
|
||||
return _t(
|
||||
f"🌙 记忆蒸馏已启动 (整理近 {days} 天的记忆)\n\n整理在后台执行,完成后会通知你。",
|
||||
f"🌙 Memory distillation started (processing the last {days} days)\n\nRunning in the background; you'll be notified when it's done.",
|
||||
)
|
||||
|
||||
def _memory_dream_sync(self, flush_mgr, days: int) -> str:
|
||||
"""Run deep dream synchronously and return the full result."""
|
||||
@@ -1210,10 +1294,10 @@ class CowCliPlugin(Plugin):
|
||||
result = flush_mgr.deep_dream(lookback_days=days, force=True)
|
||||
if result:
|
||||
return self._build_dream_result(flush_mgr, is_web=True)
|
||||
return "💤 记忆蒸馏跳过 — 没有新的记忆内容需要整理"
|
||||
return _t("💤 记忆蒸馏跳过 — 没有新的记忆内容需要整理", "💤 Memory distillation skipped — no new memories to process")
|
||||
except Exception as e:
|
||||
logger.warning(f"[CowCli] /memory dream sync failed: {e}")
|
||||
return f"❌ 记忆蒸馏失败: {e}"
|
||||
return _t(f"❌ 记忆蒸馏失败: {e}", f"❌ Memory distillation failed: {e}")
|
||||
|
||||
@staticmethod
|
||||
def _resolve_active_embedding():
|
||||
@@ -1255,9 +1339,9 @@ class CowCliPlugin(Plugin):
|
||||
agent = self._get_agent("")
|
||||
memory_manager = agent.memory_manager if agent else None
|
||||
|
||||
lines = ["🧠 记忆索引状态", ""]
|
||||
lines = [_t("🧠 记忆索引状态", "🧠 Memory Index Status"), ""]
|
||||
if not memory_manager:
|
||||
lines.append(" ⚠️ Agent 尚未初始化,先发一条普通消息再试")
|
||||
lines.append(_t(" ⚠️ Agent 尚未初始化,先发一条普通消息再试", " ⚠️ Agent not initialized yet, send a normal message first"))
|
||||
return "\n".join(lines)
|
||||
|
||||
stats = memory_manager.storage.get_stats()
|
||||
@@ -1278,7 +1362,7 @@ class CowCliPlugin(Plugin):
|
||||
lines.append(f" Model : {cfg_model}")
|
||||
lines.append(f" Dim : {cfg_dim if cfg_dim else '?'}")
|
||||
else:
|
||||
lines.append(" Provider : (未初始化, keyword-only)")
|
||||
lines.append(_t(" Provider : (未初始化, keyword-only)", " Provider : (not initialized, keyword-only)"))
|
||||
|
||||
# Health hints — only shown when the user has explicitly opted into
|
||||
# vector search via `embedding_provider`. Legacy users (no explicit
|
||||
@@ -1289,17 +1373,17 @@ class CowCliPlugin(Plugin):
|
||||
if explicitly_opted_in and provider_obj is not None:
|
||||
if chunks > 0 and embedded < chunks:
|
||||
missing = chunks - embedded
|
||||
warnings.append(
|
||||
f" ⚠️ {missing}/{chunks} 个 chunk 没有向量;"
|
||||
f"运行 /memory rebuild-index 后所有记忆才会被向量化检索"
|
||||
)
|
||||
warnings.append(_t(
|
||||
f" ⚠️ {missing}/{chunks} 个 chunk 没有向量;运行 /memory rebuild-index 后所有记忆才会被向量化检索",
|
||||
f" ⚠️ {missing}/{chunks} chunks have no vectors; run /memory rebuild-index to enable vector search for all memories",
|
||||
))
|
||||
|
||||
index_dim = detect_index_dim(memory_manager.storage)
|
||||
if index_dim is not None and cfg_dim and index_dim != cfg_dim:
|
||||
warnings.append(
|
||||
f" ⚠️ 索引中存量向量为 {index_dim} 维,与当前配置 {cfg_dim} 维不一致;"
|
||||
f"运行 /memory rebuild-index 重建后向量检索才会生效"
|
||||
)
|
||||
warnings.append(_t(
|
||||
f" ⚠️ 索引中存量向量为 {index_dim} 维,与当前配置 {cfg_dim} 维不一致;运行 /memory rebuild-index 重建后向量检索才会生效",
|
||||
f" ⚠️ Existing vectors are {index_dim}-dim, mismatching the current {cfg_dim}-dim config; run /memory rebuild-index to make vector search work",
|
||||
))
|
||||
|
||||
if warnings:
|
||||
lines.append("")
|
||||
@@ -1312,9 +1396,11 @@ class CowCliPlugin(Plugin):
|
||||
session_id = self._get_session_id(e_context, fallback=session_id)
|
||||
agent = self._get_agent(session_id)
|
||||
if not agent or not agent.memory_manager:
|
||||
return (
|
||||
return _t(
|
||||
"⚠️ Agent 尚未初始化,无法重建索引。\n"
|
||||
"请先发送一条普通消息触发 Agent 启动后再试。"
|
||||
"请先发送一条普通消息触发 Agent 启动后再试。",
|
||||
"⚠️ Agent not initialized, cannot rebuild the index.\n"
|
||||
"Send a normal message first to start the Agent, then try again.",
|
||||
)
|
||||
|
||||
memory_manager = agent.memory_manager
|
||||
@@ -1328,12 +1414,14 @@ class CowCliPlugin(Plugin):
|
||||
._init_embedding_provider(memory_manager.config, session_id=session_id)
|
||||
except Exception as e:
|
||||
logger.exception("[CowCli] /memory rebuild-index: build provider failed")
|
||||
return f"⚠️ 无法根据当前配置构造 embedding provider: {e}"
|
||||
return _t(f"⚠️ 无法根据当前配置构造 embedding provider: {e}", f"⚠️ Failed to build embedding provider from current config: {e}")
|
||||
|
||||
if fresh_provider is None:
|
||||
return (
|
||||
return _t(
|
||||
"⚠️ 当前没有可用的 embedding provider。\n"
|
||||
"请检查 config.json 中的 embedding 相关配置 (provider / api key)。"
|
||||
"请检查 config.json 中的 embedding 相关配置 (provider / api key)。",
|
||||
"⚠️ No embedding provider available.\n"
|
||||
"Check the embedding settings in config.json (provider / api key).",
|
||||
)
|
||||
memory_manager.embedding_provider = fresh_provider
|
||||
|
||||
@@ -1353,23 +1441,29 @@ class CowCliPlugin(Plugin):
|
||||
if result.ok:
|
||||
self._notify(
|
||||
e_context,
|
||||
(
|
||||
_t(
|
||||
f"✅ 索引重建完成\n"
|
||||
f" cleared : {result.removed}\n"
|
||||
f" chunks : {result.chunks}\n"
|
||||
f" files : {result.files}"
|
||||
f" files : {result.files}",
|
||||
f"✅ Index rebuild complete\n"
|
||||
f" cleared : {result.removed}\n"
|
||||
f" chunks : {result.chunks}\n"
|
||||
f" files : {result.files}",
|
||||
),
|
||||
)
|
||||
else:
|
||||
self._notify(e_context, f"❌ 索引重建失败: {result.error}")
|
||||
self._notify(e_context, _t(f"❌ 索引重建失败: {result.error}", f"❌ Index rebuild failed: {result.error}"))
|
||||
except Exception as e:
|
||||
logger.exception("[CowCli] /memory rebuild-index failed")
|
||||
self._notify(e_context, f"❌ 索引重建失败: {e}")
|
||||
self._notify(e_context, _t(f"❌ 索引重建失败: {e}", f"❌ Index rebuild failed: {e}"))
|
||||
|
||||
threading.Thread(target=_run, daemon=True).start()
|
||||
return (
|
||||
return _t(
|
||||
f"🔧 索引重建已启动 (model={model_label}, dim={dim_label})\n\n"
|
||||
f"将重新向量化所有记忆和知识文件,完成后会通知你。"
|
||||
f"将重新向量化所有记忆和知识文件,完成后会通知你。",
|
||||
f"🔧 Index rebuild started (model={model_label}, dim={dim_label})\n\n"
|
||||
f"Re-vectorizing all memory and knowledge files; you'll be notified when done.",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@@ -1380,15 +1474,19 @@ class CowCliPlugin(Plugin):
|
||||
result = rebuild_in_process(memory_manager)
|
||||
except Exception as e:
|
||||
logger.exception("[CowCli] /memory rebuild-index sync failed")
|
||||
return f"❌ 索引重建失败: {e}"
|
||||
return _t(f"❌ 索引重建失败: {e}", f"❌ Index rebuild failed: {e}")
|
||||
|
||||
if not result.ok:
|
||||
return f"❌ 索引重建失败: {result.error}"
|
||||
return (
|
||||
return _t(f"❌ 索引重建失败: {result.error}", f"❌ Index rebuild failed: {result.error}")
|
||||
return _t(
|
||||
f"✅ 索引重建完成 (model={model_label}, dim={dim_label})\n"
|
||||
f" cleared : {result.removed}\n"
|
||||
f" chunks : {result.chunks}\n"
|
||||
f" files : {result.files}"
|
||||
f" files : {result.files}",
|
||||
f"✅ Index rebuild complete (model={model_label}, dim={dim_label})\n"
|
||||
f" cleared : {result.removed}\n"
|
||||
f" chunks : {result.chunks}\n"
|
||||
f" files : {result.files}",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@@ -1418,7 +1516,7 @@ class CowCliPlugin(Plugin):
|
||||
def _build_dream_result(flush_mgr, is_web: bool) -> str:
|
||||
"""Build dream completion message with diary content."""
|
||||
from datetime import datetime
|
||||
lines = ["✅ 记忆蒸馏完成"]
|
||||
lines = [_t("✅ 记忆蒸馏完成", "✅ Memory distillation complete")]
|
||||
|
||||
# Read today's dream diary
|
||||
today = datetime.now().strftime("%Y-%m-%d")
|
||||
@@ -1433,9 +1531,9 @@ class CowCliPlugin(Plugin):
|
||||
lines.append(f"\n{diary}")
|
||||
|
||||
if is_web:
|
||||
lines.append("\n[MEMORY.md](/memory/MEMORY.md) | [梦境日记](/memory/dreams)")
|
||||
lines.append(_t("\n[MEMORY.md](/memory/MEMORY.md) | [梦境日记](/memory/dreams)", "\n[MEMORY.md](/memory/MEMORY.md) | [Dream Diary](/memory/dreams)"))
|
||||
else:
|
||||
lines.append("\nMEMORY.md 已更新")
|
||||
lines.append(_t("\nMEMORY.md 已更新", "\nMEMORY.md updated"))
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
@@ -1485,11 +1583,17 @@ class CowCliPlugin(Plugin):
|
||||
with open(config_path, "w", encoding="utf-8") as f:
|
||||
_json.dump(file_config, f, indent=4, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
return f"⚠️ 内存中已切换,但写入 config.json 失败: {e}"
|
||||
return _t(f"⚠️ 内存中已切换,但写入 config.json 失败: {e}", f"⚠️ Switched in memory, but failed to write config.json: {e}")
|
||||
|
||||
status = "开启 ✅" if enabled else "关闭 ❌"
|
||||
note = "知识库将在下次对话中生效" if enabled else "知识库系统已停用,不再注入提示词和索引知识文件"
|
||||
return f"📚 知识库已{status}\n\n{note}"
|
||||
if enabled:
|
||||
return _t(
|
||||
"📚 知识库已开启 ✅\n\n知识库将在下次对话中生效",
|
||||
"📚 Knowledge base enabled ✅\n\nIt will take effect in the next conversation",
|
||||
)
|
||||
return _t(
|
||||
"📚 知识库已关闭 ❌\n\n知识库系统已停用,不再注入提示词和索引知识文件",
|
||||
"📚 Knowledge base disabled ❌\n\nThe knowledge system is off; no prompt injection or file indexing",
|
||||
)
|
||||
|
||||
def _knowledge_stats(self) -> str:
|
||||
from config import conf
|
||||
@@ -1499,7 +1603,7 @@ class CowCliPlugin(Plugin):
|
||||
"knowledge"
|
||||
)
|
||||
if not os.path.isdir(knowledge_dir):
|
||||
return "📚 知识库目录不存在\n\n💡 开启知识库: /knowledge on"
|
||||
return _t("📚 知识库目录不存在\n\n💡 开启知识库: /knowledge on", "📚 Knowledge base directory not found\n\n💡 Enable it: /knowledge on")
|
||||
|
||||
enabled = conf().get("knowledge", True)
|
||||
total_files = 0
|
||||
@@ -1516,13 +1620,13 @@ class CowCliPlugin(Plugin):
|
||||
total_bytes += os.path.getsize(os.path.join(root, f))
|
||||
cat_count[category] = cat_count.get(category, 0) + 1
|
||||
|
||||
status = "✅ 已开启" if enabled else "❌ 已关闭"
|
||||
status = _t("✅ 已开启", "✅ Enabled") if enabled else _t("❌ 已关闭", "❌ Disabled")
|
||||
lines = [
|
||||
"📚 知识库统计",
|
||||
_t("📚 知识库统计", "📚 Knowledge Base Stats"),
|
||||
"",
|
||||
f"状态: {status}",
|
||||
f"页面: {total_files} 篇",
|
||||
f"大小: {total_bytes / 1024:.1f} KB",
|
||||
_t(f"状态: {status}", f"Status: {status}"),
|
||||
_t(f"页面: {total_files} 篇", f"Pages: {total_files}"),
|
||||
_t(f"大小: {total_bytes / 1024:.1f} KB", f"Size: {total_bytes / 1024:.1f} KB"),
|
||||
"",
|
||||
]
|
||||
if cat_count:
|
||||
@@ -1530,12 +1634,12 @@ class CowCliPlugin(Plugin):
|
||||
lines.append(f"- {cat}/ ({cat_count[cat]} pages)")
|
||||
lines.append("")
|
||||
|
||||
lines.append(f"路径: {knowledge_dir}")
|
||||
lines.append(_t(f"路径: {knowledge_dir}", f"Path: {knowledge_dir}"))
|
||||
lines.extend([
|
||||
"",
|
||||
"━━━━━━━━━━━━━━━━━━━━━━━━━━",
|
||||
"💡 /knowledge list 查看文件树",
|
||||
"💡 /knowledge on|off 开关知识库",
|
||||
_t("💡 /knowledge list: 查看文件树", "💡 /knowledge list: Show file tree"),
|
||||
_t("💡 /knowledge on|off: 开关知识库", "💡 /knowledge on|off: Toggle knowledge base"),
|
||||
])
|
||||
return "\n".join(lines)
|
||||
|
||||
@@ -1547,7 +1651,7 @@ class CowCliPlugin(Plugin):
|
||||
"knowledge"
|
||||
)
|
||||
if not os.path.isdir(knowledge_dir):
|
||||
return "📚 知识库目录不存在\n\n💡 开启知识库: /knowledge on"
|
||||
return _t("📚 知识库目录不存在\n\n💡 开启知识库: /knowledge on", "📚 Knowledge base directory not found\n\n💡 Enable it: /knowledge on")
|
||||
|
||||
tree = ["knowledge/"]
|
||||
|
||||
@@ -1577,7 +1681,7 @@ class CowCliPlugin(Plugin):
|
||||
tree.append(f"{child_prefix}└── ... +{len(md_files) - max_show} more")
|
||||
|
||||
if not subdirs:
|
||||
tree.append("(空)")
|
||||
tree.append(_t("(空)", "(empty)"))
|
||||
|
||||
return "```\n" + "\n".join(tree) + "\n```"
|
||||
|
||||
@@ -1602,4 +1706,4 @@ class CowCliPlugin(Plugin):
|
||||
return None
|
||||
|
||||
def get_help_text(self, **kwargs):
|
||||
return "在对话中使用 /help 或 cow help 查看可用命令"
|
||||
return _t("在对话中使用 /help 或 cow help 查看可用命令", "Use /help or cow help in chat to see available commands")
|
||||
|
||||
Reference in New Issue
Block a user