From b852ebb6eee35f62fcdcaf516e0419f49c0d3ee2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=99=B4=E5=A4=A9?= Date: Thu, 14 May 2026 04:18:33 +0800 Subject: [PATCH] =?UTF-8?q?feat(hermes):=20P1-3=20lazy=5Fdeps=20=E9=A2=84?= =?UTF-8?q?=E5=A4=84=E7=90=86=20-=20=E5=8A=A0=20IM=20=E6=B8=A0=E9=81=93?= =?UTF-8?q?=E4=B8=8D=E5=86=8D=E3=80=8C=E9=A6=96=E5=90=AF=20Gateway=20?= =?UTF-8?q?=E5=8D=A1=2030=20=E7=A7=92=E5=90=8E=E5=B4=A9=E3=80=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hermes 内核 tools/lazy_deps.py 维护了一个 allowlist:每个 feature(如 platform.telegram / tts.elevenlabs / search.exa)对应一组 PyPI 包。原本只有 Gateway 启动 platform 模块时 才会调 ensure() 装包,导致首次启动卡 30 秒甚至超时崩溃。 本 PR 把 lazy_deps 暴露给 ClawPanel UI,让用户能主动预装。 ## 后端三个新 Tauri 命令 - hermes_lazy_deps_features() — 列所有 LAZY_DEPS allowlist feature(17 个) - hermes_lazy_deps_status(features) — 批量查每个 feature 是否已装好 - hermes_lazy_deps_ensure(feature) — 主动调内核 tools.lazy_deps.ensure 装 实现方式: - 找到 ~/.hermes-venv 的 python 路径(unix: bin/python,windows: Scripts/python.exe) - 用 tokio::process::Command spawn `python -c ""` 跑临时 Python 脚本 - 脚本走 from tools.lazy_deps import ensure / feature_missing / LAZY_DEPS - 把结果以 JSON dump 给 stdout,Rust 端解析最后一行 - enhanced_path() 注入 PATH 兼容 macOS Tauri 启动后 PATH 不全 - serde_json::to_string 把字符串和列表序列化为 Python 合法字面量(防注入) 已注册到 lib.rs,前端 wrapper 在 tauri-api.js(features 走 10min 缓存)。 ## 前端 - 新页面 src/engines/hermes/pages/lazy-deps.js - 分类 grid(消息渠道 / TTS / STT / 搜索 / 模型商 / 记忆 / 图像 / 其他),每类有 emoji - 卡片式:feature 名(友好显示)+ specs 元信息 + 状态徽章(已装✓ / 未装warn / 未知)+ 装/重装按钮 - 装/重装按钮 await ensure,期间「安装中…」disabled,完成后刷新整张表 - 失败走 humanizeError 统一提示 - 17 个 feature 都有友好显示名 i18n(platform.telegram → Telegram,platform.dingtalk → 钉钉 等) - 完整 11 语言 i18n(hermesLazyDeps 模块),其它语言 fallback 到 en ## sidebar - Hermes 引擎「管理」section 新增「可选依赖管理」入口 - 路由 /h/lazy-deps ## CSS - 加 .lazy-deps-grid / .lazy-deps-card / .lazy-deps-badge.{ok,warn,unknown} - 复用现有 .empty-state / .empty-compact 风格 ## Web 模式 - dev-api.js 加三个同名 handler: - features 返回内置常见 platform 列表 - status 全标 unknown(无法 spawn python) - ensure 直接 reject,提示走桌面端 ## 累计变动 - 2 新文件(lazy-deps.js page + hermesLazyDeps.js i18n) - 7 修改(dev-api / hermes.rs / lib.rs / hermes/index.js / tauri-api.js / locales/index.js / components.css) - 11 语言 × ~17 个新 i18n 键 - cargo check ✓ + npm build ✓ --- scripts/dev-api.js | 35 +++++ src-tauri/src/commands/hermes.rs | 135 +++++++++++++++++ src-tauri/src/lib.rs | 3 + src/engines/hermes/index.js | 2 + src/engines/hermes/pages/lazy-deps.js | 200 ++++++++++++++++++++++++++ src/lib/tauri-api.js | 3 + src/locales/index.js | 3 +- src/locales/modules/hermesLazyDeps.js | 99 +++++++++++++ src/style/components.css | 85 +++++++++++ 9 files changed, 564 insertions(+), 1 deletion(-) create mode 100644 src/engines/hermes/pages/lazy-deps.js create mode 100644 src/locales/modules/hermesLazyDeps.js diff --git a/scripts/dev-api.js b/scripts/dev-api.js index ef58a57..b91856a 100644 --- a/scripts/dev-api.js +++ b/scripts/dev-api.js @@ -7159,6 +7159,41 @@ const handlers = { return { model: displayModel, model_raw: modelName, base_url: baseUrl, provider, api_key: apiKey, config_exists: fs.existsSync(configPath) } }, + // P1-3 lazy_deps: Web 模式下不能调 venv python,但仍提供 feature 列表 + 提示用户走桌面端装 + hermes_lazy_deps_features() { + const features = [ + { feature: 'platform.telegram', specs: ['python-telegram-bot[webhooks]==22.6'] }, + { feature: 'platform.discord', specs: ['discord.py[voice]==2.7.1'] }, + { feature: 'platform.slack', specs: ['slack-bolt==1.27.0', 'slack-sdk==3.40.1', 'aiohttp==3.13.3'] }, + { feature: 'platform.matrix', specs: ['matrix-nio[e2e]'] }, + { feature: 'platform.dingtalk', specs: ['dingtalk-stream'] }, + { feature: 'platform.feishu', specs: ['lark-oapi'] }, + { feature: 'tts.edge', specs: ['edge-tts==7.2.7'] }, + { feature: 'tts.elevenlabs', specs: ['elevenlabs==1.59.0'] }, + { feature: 'stt.faster_whisper', specs: ['faster-whisper==1.2.1', 'sounddevice==0.5.5', 'numpy==2.4.3'] }, + { feature: 'search.exa', specs: ['exa-py==2.10.2'] }, + { feature: 'search.firecrawl', specs: ['firecrawl-py==4.17.0'] }, + { feature: 'search.parallel', specs: ['parallel-web==0.4.2'] }, + { feature: 'provider.anthropic', specs: ['anthropic==0.86.0'] }, + { feature: 'provider.bedrock', specs: ['boto3==1.42.89'] }, + { feature: 'memory.honcho', specs: ['honcho-ai==2.0.1'] }, + { feature: 'memory.hindsight', specs: ['hindsight-client==0.6.1'] }, + { feature: 'image.fal', specs: ['fal-client==0.13.1'] }, + ] + return { ok: true, features } + }, + + hermes_lazy_deps_status({ features }) { + // Web 模式无法实际查询 venv,全部标 unknown + const status = {} + for (const f of features || []) status[f] = { known: true, satisfied: false, missing: [] } + return { ok: true, status } + }, + + hermes_lazy_deps_ensure({ feature }) { + return { ok: false, error: `Web 模式下无法预装依赖。请在桌面端 ClawPanel 完成 ${feature} 安装。` } + }, + // P1-4:完整解析 config.yaml,让前端能读 14+ 高价值字段 // Web 模式不引入 yaml 依赖,简单返回 raw + null highlights(前端按需渲染) hermes_read_config_full() { diff --git a/src-tauri/src/commands/hermes.rs b/src-tauri/src/commands/hermes.rs index 8bda68b..3ca1e28 100644 --- a/src-tauri/src/commands/hermes.rs +++ b/src-tauri/src/commands/hermes.rs @@ -2150,6 +2150,141 @@ pub async fn hermes_read_config_full() -> Result { })) } +// --------------------------------------------------------------------------- +// P1-3: lazy_deps 预处理命令 — 让用户启用渠道时不再「首启 Gateway 卡 30 秒后崩」 +// +// Hermes 内核 tools/lazy_deps.py 维护了一个 allowlist:每个 feature(如 +// `platform.telegram` / `tts.elevenlabs`)对应一组 PyPI 包。原本只有 Gateway +// 启动 platform 模块时才会调 ensure() 装包,导致首次启动卡住甚至超时崩。 +// +// 这里把 lazy_deps 暴露给 ClawPanel UI: +// - hermes_lazy_deps_features() — 列所有可装的 feature(小白选) +// - hermes_lazy_deps_status(features) — 批量查每个 feature 是否已安装 +// - hermes_lazy_deps_ensure(feature) — 主动预装 +// --------------------------------------------------------------------------- + +/// 找到 Hermes venv 的 Python 解释器路径 +fn hermes_venv_python() -> Option { + let venv_dir = dirs::home_dir()?.join(".hermes-venv"); + #[cfg(target_os = "windows")] + let py = venv_dir.join("Scripts").join("python.exe"); + #[cfg(not(target_os = "windows"))] + let py = venv_dir.join("bin").join("python"); + if py.exists() { + Some(py) + } else { + None + } +} + +/// 统一跑 venv python -c "