diff --git a/README.md b/README.md index 75c0acd..6c27fb6 100644 --- a/README.md +++ b/README.md @@ -129,6 +129,8 @@ Playwright 不下载独立 Chromium。首次 `setup` 只缓存 MCP 的 Node.js MCP 服务器可通过 Web UI 左下角的 MCP 按钮可视化配置(写入 `data/agent/mcp.json`):支持 stdio(命令 + 参数)与 HTTP(URL + 请求头 + OAuth/Bearer)两种传输、环境变量键值编辑、工作目录、生命周期与超时设置,另保留原始 JSON 编辑兜底。保存后重启 pi(或 /reload)生效。 +Web UI 右上角的会话信息栏会汇总 Token 使用情况;当模型返回缓存读写数据时,还会显示按 Token 加权计算的缓存命中率:`cacheRead / (input + cacheRead + cacheWrite)`,不计输出 Token。 + #### 记忆模式 默认设置 `PI_MEMORY_NO_SEARCH=1` 和 `PI_MEMORY_QMD_UPDATE=off`。这不会削弱 Markdown 记忆、每日日志、scratchpad 或恢复记录,但会跳过 qmd 探测、安装提示和 `memory_search` 工具,因此全新安装不会自动下载 qmd 的本地模型。集成补丁会在 `setup` 以及每次启动前自动检查并重放,受管插件重装后无需手工修改。 @@ -324,6 +326,8 @@ Playwright never downloads a standalone Chromium in this project. Setup caches o MCP servers can be configured visually from the MCP button in the lower-left Web UI (writes `data/agent/mcp.json`): stdio (command + args) or HTTP (URL + headers + OAuth/Bearer) transport, environment-variable row editing, working directory, lifecycle and timeout options, plus raw JSON editing as a fallback. Changes take effect after restarting pi (or /reload). +The Web UI session-info panel in the upper-right summarizes Token usage. When a model reports cache read/write data, it also shows the token-weighted cache hit rate: `cacheRead / (input + cacheRead + cacheWrite)`, excluding output Tokens. + #### Memory modes The managed launcher defaults to `PI_MEMORY_NO_SEARCH=1` and `PI_MEMORY_QMD_UPDATE=off`. Markdown memory, daily logs, scratchpad, and recovery stay available, while qmd detection, its installation notice, and the `memory_search` tool are skipped. A deterministic integration patch is checked during setup and before every launch, so reinstalling managed packages needs no manual repair. diff --git a/pi-web/components/AppShell.tsx b/pi-web/components/AppShell.tsx index 3aa6ef4..d45cdde 100644 --- a/pi-web/components/AppShell.tsx +++ b/pi-web/components/AppShell.tsx @@ -1256,11 +1256,16 @@ export function AppShell() { [translate("session.toolResults"), sessionStats.toolResults.toLocaleString(locale)], [translate("session.total"), sessionStats.totalMessages.toLocaleString(locale)], ]; + const promptTokens = sessionStats.tokens.input + sessionStats.tokens.cacheRead + sessionStats.tokens.cacheWrite; + const cacheHitRate = promptTokens > 0 && (sessionStats.tokens.cacheRead > 0 || sessionStats.tokens.cacheWrite > 0) + ? `${((sessionStats.tokens.cacheRead / promptTokens) * 100).toFixed(1)}%` + : null; const tokenRows = [ [translate("session.input"), sessionStats.tokens.input.toLocaleString(locale)], [translate("session.output"), sessionStats.tokens.output.toLocaleString(locale)], ...(sessionStats.tokens.cacheRead > 0 ? [[translate("session.cacheRead"), sessionStats.tokens.cacheRead.toLocaleString(locale)]] : []), ...(sessionStats.tokens.cacheWrite > 0 ? [[translate("session.cacheWrite"), sessionStats.tokens.cacheWrite.toLocaleString(locale)]] : []), + ...(cacheHitRate ? [[translate("session.cacheHitRate"), cacheHitRate]] : []), [translate("session.total"), sessionStats.tokens.total.toLocaleString(locale)], ]; const ctx = contextUsage ?? sessionStats.contextUsage; diff --git a/pi-web/lib/i18n/messages/en.ts b/pi-web/lib/i18n/messages/en.ts index 556520a..0df965b 100644 --- a/pi-web/lib/i18n/messages/en.ts +++ b/pi-web/lib/i18n/messages/en.ts @@ -43,6 +43,7 @@ export const enLocale: LocalePlugin = { "session.output": "Output", "session.cacheRead": "Cache Read", "session.cacheWrite": "Cache Write", + "session.cacheHitRate": "Cache Hit Rate", "session.cost": "Cost", "session.context": "Context", "session.copy": "Copy {value}", diff --git a/pi-web/lib/i18n/messages/zh-CN.ts b/pi-web/lib/i18n/messages/zh-CN.ts index d6e2026..61d1fe8 100644 --- a/pi-web/lib/i18n/messages/zh-CN.ts +++ b/pi-web/lib/i18n/messages/zh-CN.ts @@ -43,6 +43,7 @@ export const zhCNLocale: LocalePlugin = { "session.output": "输出", "session.cacheRead": "缓存读取", "session.cacheWrite": "缓存写入", + "session.cacheHitRate": "缓存命中率", "session.cost": "费用", "session.context": "上下文", "session.copy": "复制{value}",