applyCaching gate keyed on model-name substring, not capability — non-Anthropic cacheable models (openrouter/openai-compatible/copilot) never get cache_control
维护者通常 1 天内回复
还没有人认领这个 Issue。
评估
- 难度
- 5/5
- 预计耗时
- 一周以上
- 新手友好度
- 45/100
- Issue 类型
- 缺陷
- 描述清晰度
- 基本清楚
- 活跃度
- 冷清
- 技术栈
- typescript
调研方向
从 packages/opencode/src/provider/transform.ts 开始,重点查看第 285-297 行的 ProviderTransform.message() 和第 196-210 行的 applyCaching(),然后检查 provider.ts:787 中的 capabilities schema。将该 gate 与受支持的 provider 选项和现有覆盖范围进行比较;完成的标准是,所选的 capability 或 provider 规则定义清晰,并且非 Anthropic 的可缓存模型能够接收预期的指令,同时不回归现有行为。
由索引模型根据 Issue 内容生成。
描述
Description
Found via static analysis of prompt-cache anti-patterns (CacheLint), then confirmed by hand on main @ f0fb1e1.
ProviderTransform.message() decides whether to call applyCaching() using a hard-coded model-name gate (packages/opencode/src/provider/transform.ts:285-297):
if (
(model.providerID === "anthropic" ||
model.providerID === "google-vertex-anthropic" ||
model.providerID === "altimate-backend" ||
model.api.id.includes("anthropic") ||
model.api.id.includes("claude") ||
model.id.includes("anthropic") ||
model.id.includes("claude") ||
model.api.npm === "@ai-sdk/anthropic") &&
model.api.npm !== "@ai-sdk/gateway"
) {
msgs = applyCaching(msgs, model)
}
But applyCaching() itself already defines cache directives for five providers, not just Anthropic (transform.ts:196-210):
const providerOptions = {
anthropic: { cacheControl: { type: "ephemeral" } },
openrouter: { cacheControl: { type: "ephemeral" } },
bedrock: { cachePoint: { type: "default" } },
openaiCompatible: { cache_control: { type: "ephemeral" } },
copilot: { copilot_cache_control:{ type: "ephemeral" } },
}
So a cacheable model served through openrouter / openai-compatible / copilot whose id contains neither claude nor anthropic (e.g. a GPT / Gemini / Qwen / Kimi routed through those providers) never enters applyCaching() and never gets a cache breakpoint — even though the function clearly intends to cache it.
This is an under-claim: caching silently fails to engage. It is not cache-busting, and Anthropic-named models are unaffected.
Impact
For an affected model, the system prefix (system prompt + tool schema + earlier turns) is re-sent at full input price on every turn instead of being read from cache. On long agentic loops that is roughly the usual cached-prefix discount forgone each turn, plus higher TTFB — for exactly the self-hosted / BYO-LLM users the project targets. The blast radius is bounded to non-Anthropic-named models that genuinely support explicit cache_control via openrouter/openai-compatible/copilot.
Steps to reproduce
- Configure a cacheable model through
openrouter(oropenai-compatible/copilot) whose id does not containclaude/anthropic(e.g. an OpenRouter-served model that honorscache_control). - Run a multi-turn session.
- Observe that no
cacheControl/cache_controlprovider option is stamped on the system/last-user blocks (theapplyCachingbranch is skipped), so the prefix is billed as fresh input every turn.
Suggested fix (for discussion)
Decouple the applyCaching gate from the model-name list and drive it off an explicit capability/provider-support signal, so the gate matches the set of providers applyCaching already knows how to cache:
- introduce a
capabilities.cachingflag on the model (thecapabilitiesschema inprovider.ts:787currently has no caching field), populated from the model registry; or - gate on a per-provider "supports prompt cache" set covering
anthropic / openrouter / bedrock / openaiCompatible / copilot(the same five keys already inproviderOptions).
I want to flag the design angle rather than send a drive-by PR: this is a hot path, and #891 was deliberately deferred for the same "needs design + careful testing, don't regress gateway cache-hit rates" reason. It also overlaps with #891's goal of having a single source of truth for the cache-control gate. I'm happy to open a PR if a maintainer confirms the preferred shape (capability flag vs provider set) and that emitting these directives for non-Anthropic providers is intended.
Caveat: confirmed present and unguarded on main @ f0fb1e1; line numbers may drift.
- 主要语言
- TypeScript
- 星标
- 810
- 派生
- 121
- 平均合并
- 1 天 13 小时
- 30 天内合并 PR
- 64
环境准备
在浏览器里用你自己的 GitHub 账号启动这个项目的开发容器。
- 没有 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
AltimateAI/altimate-code 的其他 Issue
-
难度 2/5 1-3 小时 新手友好度 68/100
AltimateAI/altimate-code#1454 ·
维护者通常 1 天内回复
-
test: MCP tests fail when the developer's ~/.claude.json has MCP servers (HOME is not sandboxed)未关闭
难度 2/5 1-3 小时 新手友好度 78/100
AltimateAI/altimate-code#1386 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 84/100
AltimateAI/altimate-code#1384 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 84/100
AltimateAI/altimate-code#1323 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 86/100
AltimateAI/altimate-code#1288 ·
维护者通常 1 天内回复
查看 AltimateAI/altimate-code 的全部 Issue
相似的 Issue
-
难度 2/5 1-3 小时 新手友好度 72/100
维护者通常 1 天内回复
-
needs triage
难度 2/5 1-3 小时 新手友好度 76/100
homarr-labs/homarr#7191 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 62/100
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 74/100
AvaProtocol/ava-sdk-js#276 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 75/100
apollographql/apollo-client#13493 ·
维护者通常 1 天内回复