Skip to content

fix: align official Grok prompt cache with CPA for Sub2API#708

Merged
chenyme merged 4 commits into
chenyme:mainfrom
YuJunZhiXue:fix/official-prompt-cache-cpa
Jul 20, 2026
Merged

fix: align official Grok prompt cache with CPA for Sub2API#708
chenyme merged 4 commits into
chenyme:mainfrom
YuJunZhiXue:fix/official-prompt-cache-cpa

Conversation

@YuJunZhiXue

Copy link
Copy Markdown
Contributor

摘要

修复 Grok Build 官方 Prompt Cache(cached_tokens)长期 0% 的问题,对齐 CLIProxyAPI (CPA) 的会话亲和行为,并兼容 Sub2API 会话信号透传。

看板「缓存命中率」读的是上游 usage.input_tokens_details.cached_tokens,不是 reasoning-replay。
本次修的是 官方缓存亲和链路,不是回放缓存。

问题根因(对照 CPA)

修复前 Grok2API CPA 后果
无 session 每请求 uuid.NewV7()x-grok-conv-id 不写 打散 xAI 服务器亲和 → cached_tokens=0
无 session 粘滞 无兜底 消息 hash 兜底 多账号乱跳
Sub2API 信号 提取不全 session / conversation / prompt_cache_key seed 常空

官方机制说明:xAI Prompt Caching

修改内容

1. 禁止随机 conv-id(P0)

  • 文件:backend/internal/infra/provider/cli/adapter.go
  • grokSessionID("") 返回空,不再每请求生成 UUID
  • 仅有稳定 session 时才设置 x-grok-conv-id / x-grok-session-id

2. 稳定会话身份(对齐 CPA)

  • 文件:backend/internal/application/gateway/prompt_cache.go
  • prompt_cache_key / session seed → 跨轮稳定 upstreamID + 按模型隔离的 affinityKey
  • 无 session 时 → soft session(system + 首条 user 锚点,多轮不漂移)
  • 完全无信号 → 空身份(不伪造)

3. Sub2API 会话信号提取

  • 文件:backend/internal/transport/http/inference/prompt_cache.go
  • 新增识别:session_id / conversation_id / body prompt_cache_key / X-Grok-Conv-Id

4. 网关接线

  • 文件:backend/internal/application/gateway/service.go
  • 传入 body 做 soft 身份;debug 日志:prompt_cache_session_empty / prompt_cache_session_soft

测试

cd backend
go test ./internal/application/gateway/ ./internal/transport/http/inference/ ./internal/infra/provider/cli/ \
  -count=1 -run 'BuildSession|Soft|PromptCache|GrokSession|OfficialCache|ConversationIdentity'

已通过:

  • 显式 key / Claude seed 跨轮稳定 + 租户隔离
  • soft session 多轮不漂移
  • 空信号不造 ID
  • Sub2API prompt_cache_key / session_id 提取
  • adapter 空 key 不写 conv-id

Sub2API 对接要求(部署后必配)

每轮固定传同一会话(任选其一):

{ "prompt_cache_key": "稳定-UUID" }

或 header:

session_id: 稳定-UUID
X-Session-Id: 稳定-UUID
X-Claude-Code-Session-Id: 稳定-UUID

Nginx(若有):

underscores_in_headers on;

验收

  1. 使用 Grok Build 账号(Web 路径写死 cached_tokens: 0
  2. 同一 prompt_cache_key 连续多轮,只追加消息
  3. 第 2 轮起应出现:
"input_tokens_details": { "cached_tokens": > 0 }
  1. 看板「缓存」列 > 0,命中率上升

说明

官方 cached_tokens 长期为 0 的根因不是 reasoning-replay,而是会话亲和被打散:
空 session 时每请求随机 x-grok-conv-id,且无消息 hash 粘滞兜底。

对齐 CLIProxyAPI 行为:
- 无稳定 session 时不再伪造随机 conv-id / session-id
- 显式 prompt_cache_key / Claude session 生成跨轮稳定 upstreamID
- 无 session 时用 system+首条 user 消息锚点 soft session(多轮不漂移)
- 扩展 Sub2API 会话信号提取:session_id / conversation_id / prompt_cache_key / x-grok-conv-id
- 租户隔离与按模型 affinity 保持不变

测试覆盖:gateway soft session、Sub2API e2e 身份、adapter 空 key、inference seed 提取。
…edentials

1) Usage 统计:
- Anthropic Messages 的 cache_read_input_tokens 写入统一 CachedInputTokens
- OpenAI Chat Completions 的 prompt_tokens_details.cached_tokens 同样写入
- Responses input_tokens_details.cached_tokens 行为保持
- Chat completion_tokens_details.reasoning_tokens 一并兼容
- 增加 Messages / Chat / 优先级回归测试

2) 凭据刷新:
- credential_decrypt_failed 不再标 Permanent(本地加密密钥问题可恢复)
- 已标记 permanent 的 decrypt_failed 允许 force/批量调度重试
- invalid_grant 等真正 OAuth 永久失败仍阻断
- 增加密钥恢复后可重试 + invalid_grant 仍永久 的回归测试
@YuJunZhiXue

Copy link
Copy Markdown
Contributor Author

追加提交(本 PR 第二 commit)

1. Anthropic / OpenAI 缓存统计修复(看板 cached_input_tokens 恒 0)

根因:
协议转换后:

  • Anthropic Messages 使用 usage.cache_read_input_tokens
  • OpenAI Chat Completions 使用 usage.prompt_tokens_details.cached_tokens
  • 统一解析器原来只读 Responses 的 input_tokens_details.cached_tokens

修改: handler.gotoGatewayUsage 同时识别三种字段,优先 Responses,再 Chat,再 Anthropic。
测试: Messages / Chat / 优先级回归已加且通过。

2. credential_decrypt_failed 可恢复刷新

根因:
解密失败被标 Permanent: true,密钥恢复后手动/批量刷新仍被 resolvePermanentRefreshFailure 拦截。

修改:

  • credential_decrypt_failedPermanent: false
  • 已落库的 decrypt_failed permanent 允许 force / 调度重试
  • invalid_grant 等真正 OAuth 永久失败仍阻断

测试: TestCredentialDecryptFailedAllowsRetryAfterKeyRecovery 通过。

3. 与官方缓存修复的关系

本 PR 现包含:

  1. CPA 对齐的官方 Prompt Cache 亲和(conv-id / soft session / Sub2API 信号)
  2. 三协议 usage 缓存字段统一写入审计
  3. 凭据解密失败自愈

Codex(OpenAI)/Anthropic 两条协议的缓存命中都应能进入 request_audits.cached_input_tokens 并在看板显示。

@chenyme
chenyme self-requested a review July 19, 2026 10:05
补齐复核发现的遗漏,确保 Codex(OpenAI)/Anthropic 缓存统计与亲和更稳:

1) soft session 纳入顶层 instructions/system(Responses/Chat 常见)
2) 流式 usage 不再要求 TotalTokens>0 才采纳;合并多帧避免半截帧抹掉 cached
3) conversation streamConverter 在仅有 cached_tokens 时也更新 usage

回归:stream merge Anthropic/Chat、instructions soft session、既有三协议解析。
@YuJunZhiXue

Copy link
Copy Markdown
Contributor Author

全面复核补齐(第 3 commit)

对照 CPA + 全链路审计后,本提交补上遗漏点:

已覆盖清单

能力 状态 说明
官方 Prompt Cache 亲和(CPA 对齐) 禁止随机 conv-id;显式 key/session 稳定;soft message hash 兜底
Sub2API 会话信号 prompt_cache_key / session_id / conversation_id / X-Grok-Conv-Id
Responses cached_tokens 原有路径
Anthropic cache_read_input_tokens 写入统一 CachedInputTokens
OpenAI/Codex prompt_tokens_details.cached_tokens 同上
Chat completion_tokens_details.reasoning_tokens 一并兼容
流式多帧 usage 合并 本提交 不再要求 TotalTokens>0;cached 晚到也能记入审计
soft session 含 instructions/system 本提交 Responses/Chat 顶层 system 锚点
streamConverter 仅缓存字段也更新 本提交 避免转换层丢 cached
credential_decrypt_failed 可恢复 invalid_grant 仍永久
Reasoning replay 独立能力,不写 cached_tokens

仍需部署侧配合(代码无法单独保证)

  1. 必须用 Grok Build 账号:Web Provider 写死 cached_tokens: 0
  2. Sub2API 每轮透传同一 session
    { "prompt_cache_key": "稳定-UUID" }
    或 header session_id / X-Session-Id;Nginx 开 underscores_in_headers on
  3. 多轮只追加消息,不要改历史/重排(xAI 前缀缓存规则)
  4. 首轮 cached_tokens=0 正常;从第 2 轮起应 >0

自测

go test ./internal/transport/http/inference/ ./internal/application/gateway/ \
  ./internal/infra/provider/cli/ ./internal/application/account/ \
  ./internal/infra/provider/conversation/ \
  -run 'ExtractUsage|StreamInspector|BuildSession|Soft|OfficialCache|PromptCache|GrokSession|CredentialDecrypt|ConvertResponses'
# all ok

@chenyme

chenyme commented Jul 19, 2026

Copy link
Copy Markdown
Owner

暂时没能复现 PR 内所说的缓存提升,能否提供相关可复现的流程?

@YuJunZhiXue

Copy link
Copy Markdown
Contributor Author

暂时没能复现 PR 内所说的缓存提升,能否提供相关可复现的流程?

不是缓存提升,打错字了,是完善了由于Anthropic / OpenAI 转换协议有无法缓存的问题,sorry打错字了

@codehz

codehz commented Jul 20, 2026

Copy link
Copy Markdown

确实,之前用聊天补全接口,缓存率都是0()

@chenyme
chenyme marked this pull request as draft July 20, 2026 02:39
@chenyme
chenyme marked this pull request as ready for review July 20, 2026 02:40
@chenyme
chenyme merged commit 3f6ea87 into chenyme:main Jul 20, 2026
9 of 19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants