Skip to content

Align Codex cache affinity header - #57012

Draft
ildunari wants to merge 1 commit into
NousResearch:mainfrom
ildunari:fix/codex-cache-affinity
Draft

Align Codex cache affinity header#57012
ildunari wants to merge 1 commit into
NousResearch:mainfrom
ildunari:fix/codex-cache-affinity

Conversation

@ildunari

@ildunari ildunari commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Fix Codex prompt-cache affinity by aligning the cache-routing x-client-request-id header with the final prompt_cache_key for the ChatGPT/Codex backend.

Hermes already builds a stable, content-addressed prompt_cache_key from the static prefix (instructions + tools). That keeps recurring jobs and fresh Hermes sessions from going cache-cold when the visible Hermes session_id changes. But the Codex backend path still sent x-client-request-id as the raw Hermes session id, so the body cache key and request-affinity header could point at different cache namespaces.

This keeps the raw session_id header unchanged for transcript/session identity, and only changes x-client-request-id to follow the final prompt_cache_key.

Fixes #47126.

What changed

  • Preserve content-addressed prompt_cache_key behavior.
  • Keep session_id header as the real Hermes session id.
  • Set Codex x-client-request-id to the final prompt_cache_key value.
  • Respect manual request_overrides["prompt_cache_key"] by making the header follow that final overridden key.
  • Add regression coverage for Codex cache-routing headers and override behavior.

Validation

Ran against a clean branch from origin/main:

pytest -q tests/agent/transports/test_codex_transport.py \
  tests/run_agent/test_run_agent_codex_responses.py \
  tests/agent/test_codex_responses_adapter.py \
  tests/agent/test_codex_cloudflare_headers.py

Result: 169 passed.

Also verified locally in an isolated disposable HERMES_HOME sandbox that resumed openai-codex / gpt-5.5 turns still reach high per-call cache reuse after warmup, while this patch keeps the existing content-addressed key behavior needed for timestamped recurring jobs.

@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint provider/openai OpenAI / Codex Responses API P2 Medium — degraded but workaround exists sweeper:risk-caching Sweeper risk: may break/degrade prompt caching or cache-key stability (invariant) labels Jul 2, 2026
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the focused cache-affinity fix. Current main still derives prompt_cache_key from the static prefix at agent/transports/codex.py:267, then routes x-client-request-id with the raw session_id at agent/transports/codex.py:353; the PR aligns those two values while preserving the transcript session_id header. request_overrides are merged before header construction at agent/transports/codex.py:303-305, so the new regression test for an overridden prompt_cache_key exercises the intended precedence.

Automated hermes-sweeper review.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 15, 2026
JoaoMarcos44 added a commit to JoaoMarcos44/hermes-agent that referenced this pull request Aug 5, 2026
…des, pin Grok cron affinity

Fixes NousResearch#79013, NousResearch#79014, NousResearch#79015.

- session_id header now carries the raw physical session id (NousResearch#57012
  contract); x-client-request-id mirrors the body's effective
  prompt_cache_key instead of both diverging to a bare scope string.
- extra_body.prompt_cache_key for xAI Responses now reads back a
  caller's top-level request_overrides={"prompt_cache_key": ...}
  instead of always using the auto-derived hash, so an explicit
  override actually governs the field xAI reads.
- x-grok-conv-id (native xAI Responses transport and Grok-via-OpenRouter
  profile) is now scoped through _cache_scope_from_session_id(), so
  cron re-fires of the same job pin to the same backend instead of a
  new one every fire.
- Fallback cache_key (when instructions/tools are empty) now falls back
  to the normalized scope instead of the raw session_id, same class of
  fix.
kshitijk4poor added a commit that referenced this pull request Aug 15, 2026
…ation

Legacy compaction mode (compression.in_place: false) rotates the physical
session_id mid-conversation. The prompt-cache scope introduced in #79161 was
derived from that physical id, so every rotation moved the same conversation
into a fresh cache bucket - the prompt cache went cold at every rotation
boundary (#79017).

Fix: resolve a rotation-stable logical scope - the compression-lineage ROOT
of the current session (SessionDB.get_compression_lineage, fork-aware
post-#79193) - once per turn, memoized per transcript segment, and prefer it
over the physical session_id at every prompt_cache_key derivation site:

- agent/prompt_cache_scope.py (new): resolve_prompt_cache_scope(agent) -
  lineage-root walk with per-segment memo; falls back to the physical id
  when no DB is attached or the walk fails, degrading to pre-fix behavior.
- transports/codex.py: build_kwargs accepts cache_scope_id and prefers it
  for the body prompt_cache_key, the xAI x-grok-conv-id header, and the
  Codex x-client-request-id routing header. The Codex session_id header
  keeps the raw physical id (transcript identity, #57012 contract).
- transports/chat_completions.py: _add_prompt_cache_key accepts
  cache_scope_id with the same precedence.
- chat_completion_helpers.py: build_api_kwargs threads the resolved scope
  into all three build_kwargs call sites (codex, profile, legacy).
- auxiliary_client.py: set_runtime_main carries cache_scope; the aux
  Responses cache-key site prefers it over the physical session_id.
- turn_context.py: resolves the scope once per turn and threads it through
  set_runtime_main (no DB walk on the per-API-call hot path).

Scope semantics preserved from #79161: /new starts a fresh scope (new
lineage), /branch children, delegate subagents, and tool children stay
isolated (explicit-fork exclusion in get_compression_lineage), unrelated
sessions keep distinct buckets, and cron per-fire timestamps still
normalize via _cache_scope_from_session_id.

Default installs compact in place (session_id never rotates), so they hit
the memo and produce byte-identical keys to before.

Fixes #79017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists provider/openai OpenAI / Codex Responses API sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-caching Sweeper risk: may break/degrade prompt caching or cache-key stability (invariant) sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Codex prompt cache regression after stripping extra_headers

3 participants