feat: /acp compact — dedicated compression model (session shared-prefix + models.json) - #241
ranxianglei wants to merge 2 commits into
Conversation
Add an optional dedicated model that writes compress summaries instead of the main model, reusing credentials from ~/.pi/agent/models.json. - /acp compact [list | <id> | reset] command - compressionModelId persisted to ~/.pi/acp.json - CompressionModelClient (list/resolve/summarize via ModelRuntime) - handleCompress routes to the model with guaranteed fallback to the main model on any failure (never blocks the session) - 18 new tests (models.json read, mock SSE API, fallback) - README.md + CONFIGURATION.md docs
…n model Add a 'session' mode to the dedicated compression model: instead of only supporting a cheaper models.json model, /acp compact session now uses the session's own model in a separate call that reuses the session prompt prefix (system prompt + active tools + the exact transformed messages Pi just sent), so the provider prompt cache keeps the input cheap and the compression reasoning stays out of the main model's context. - runtime: per-session capture of the transformed messages (setLastSentMessages) - index: capture the rebuilt messages in the context transform; pass pi to the tool - compress-model: SESSION_MODEL_REF + summarizeContext (takes a built Context) - compress-tool: branch on session vs models.json; build the shared-prefix Context; per-range instruction appended to the captured prefix - commands: /acp compact session + status display - tests: 4 new session tests (set/status/shared-prefix success/fallback) - docs: README + CONFIGURATION for the session mode
📦 Built Extension ArtifactBranch: Option A — Install from npm PR tag (recommended)pi install npm:billion-context-pi@pr-241Each push to this PR publishes a new version under the Option B — Download artifact
tar xzf billion-context-pi-pr241.tgz
pi install ./packageThis comment is automatically updated on each push. |
[bot] Reviewing this PR now. Branch |
[bot] Review complete — LGTM, all CI checks green. Merging is left to you (human-only per AGENTS.md). CI (run on branch
|
| Check | Result |
|---|---|
npm run typecheck |
✅ pass |
npm test |
✅ 450/450 pass (22 new: 12 in tests/compress-model.test.ts, 10 in tests/acp-compact.test.ts) |
npm run build |
✅ dist/index.js 535.59 KB, self-contained — zero runtime refs to pi-ai (type-only imports erased) |
Verified integration points
- Pins intact:
acp-kernel0.0.46,pi-coding-agent0.83.0 unchanged; new devDep@earendil-works/pi-aipinned exactly to0.83.0(type-only, so devDep placement is correct). The 1.3K-line lockfile diff is just pi-ai's transitive deps (AWS/Anthropic SDKs) — expected. - Shared-prefix capture (
src/index.ts:367):runtime.setLastSentMessages(sid, rebuilt)stores exactly the array returned as{ messages: rebuilt }— the final ACP-transformed messages (tags + injected nudge). Correct source. - Prefix fidelity:
getSystemPromptText(ctx)usesctx.getSystemPrompt()(same prompt Pi sends),convertToLlm(AgentMessage[]): Message[]exists in pi-coding-agent 0.83.0, andcollectActiveTools's{name, description, parameters}mapping matches pi-ai'sTool/Contextshapes exactly. So the session-mode request should be byte-identical to the main model's prefix → cache hit. - Fallbacks: per-range
try/catch→ main-model summary retained on any failure; unresolvable ref, missing session model, and empty response all degrade gracefully with a⚠️ note in the panel. Session never blocked. - Tests: the 4 session tests use a real mock SSE server and assert the shared prefix (
"lorem ipsum") is actually present in the outbound request body, plus the unreachable-model fallback path. Good coverage of the load-bearing behavior. - Docs: README + CONFIGURATION cover both modes, the fallback guarantee, resolution rules, and the global-file scope.
Non-blocking observations
- Fallback quality cliff: when a compression model is set, the tool description tells the main model to pass a minimal placeholder ("compressed"). If the compression call fails for that range, the block's stored summary is that placeholder — much weaker than the old "main model writes full summaries" behavior, and the only recovery is decompress + re-compress. The session is never interrupted (as designed), but consider either (a) stating this tradeoff explicitly in the tool description/docs, or (b) asking the main model for a 1–2 sentence real summary instead of a bare placeholder so the fallback is still useful.
- Project-level override edge case:
/acp compactwrites the global~/.pi/acp.json, butloadUserConfiglets a project.pi/acp.jsonoverride per-field. If a project file setscompressionModelId, the nextreloadConfig(forced bylastUserConfigKey = undefined) re-reads and the project value wins over the command's in-memory update. CONFIGURATION documents the scope, but the command's "applies across sessions" message could mislead in that corner. - Memory:
lastSentMessagesholds a full copy of the transformed messages per session untilclearSessionTracking— fine, just noting it's a duplicate of data already in memory. - Minor:
commandArgString(args: string)is typedstringbut handles arrays (test convenience) — the type is a slight lie; harmless. truncateContent(content, 120000)caps range content only in models.json mode; session mode sends the full (cached) context with no cap. Inconsistent but each is reasonable for its mode.
None of these block the merge. Nice work — the session-mode design (isolation without a cheaper model, via cache) is the interesting part.
|
解决冲突 更新最新代码 |
上游同步完成,冲突已解决,已更新到最新代码。 现状: 冲突(12 个文件,均保留双方特性后解决):
移植工作:被 master 移除的 调试发现:验证时发现 master 新增的 host gate( 验证:typecheck 干净;全量测试绿 ×3(823 tests / 817 pass / 0 fail / 6 skip);构建成功(dist 554 KB 自包含)。注意:本机磁盘 97% 且外部进程会间歇性删除 /tmp 下的文件(观察到 TAP 文件与测试状态目录在命令之间被清掉),本地全量跑偶发的瞬时失败与此无关——以 CI 为准。 未改版本号(release-branch-only 规则);PR #243 等你 review 和合并。 一句话总结:把 PR #243 同步到最新 master(v0.1.70 / acp-kernel 0.0.69),解决了 12 个文件的冲突并保留双方特性,本地全量测试通过,待人工合并。 |
Summary
Implements #240: a
/acp compactcommand that offloadscompresssummary-writing to a separate call, in two modes:session— the session's own model, in a separate call that reuses the session prompt prefix (system prompt + active tools + the exact transformed messages Pi just sent). Because the prefix matches what the main model already sent, the provider prompt cache keeps the input cheap, and the compression reasoning stays out of the main model's context.<id>— a cheapermodels.jsonmodel (reuses itsbaseUrl/apiKey; a different cache namespace, so no prefix sharing, but a lower per-token price).Unset → the main model writes summaries (unchanged default). Any call failure (network/timeout/API/empty) → fallback to the main model's summary for that range — the session is never interrupted.
Changes
src/compress-model.ts:CompressionModelClient(listModels/resolveModel/summarize/summarizeContext),SESSION_MODEL_REF.src/compress-tool.ts: branch onsessionvsmodels.json; build the shared-prefixContext; per-range instruction appended to the captured prefix.src/index.ts: capture the ACP-transformed messages in the context transform; passpito the tool.src/runtime.ts: per-sessionsetLastSentMessages/getLastSentMessages.src/commands.ts:/acp compact [session|<id>|reset]+ status display.src/user-config.ts/src/config.ts:compressionModelId(+saveCompressionModelIdwriter).compress-model+acp-compact, incl. 4 session tests). 450/450 pass.Related