OpenRouter Grok prompt caching header investigation and fix
Cluster 22705. Data as of
Nothing open on GitHub. Fix merged in #22809 on 9 May 2026. Check it covers your case.
The short answer
- Known?
- Yes. 3 related items grouped here, 1 issue already closed.
- Fix event
- A fix was merged to main in PR #22809. Check it covers your case. No release data is tracked.
- Tracker
- The maintainers' tracker marks this
resolved, deciding PR #22809. That is separate from the fix event. - Still open
- Nothing open on GitHub.
These items concern a missing xAI server-affinity header (x-grok-conv-id) when routing Grok models through OpenRouter, which may reduce prompt cache hit rates. They include an issue identifying the gap and at least two related fix attempts or variants.
Open issues0
Open PRs0
Fixes on record
-
#22809 fix(openrouter): add x-grok-conv-id header for Grok models (carve-out of #22708)
files:
agent/transports/chat_completions.py,plugins/model-providers/openrouter/__init__.py,scripts/release.py,tests/providers/test_provider_profiles.pyShow the diff
-
agent/transports/chat_completions.py@@ -448,6 +448,7 @@ def _build_kwargs_from_profile(self, profile, model, sanitized, tools, params): qwen_session_metadata=params.get("qwen_session_metadata"), model=model, ollama_num_ctx=params.get("ollama_num_ctx"), + session_id=params.get("session_id"), ) ) api_kwargs.update(top_level_from_profile) -
plugins/model-providers/openrouter/__init__.py@@ -53,16 +53,28 @@ def build_api_kwargs_extras( *, reasoning_config: dict | None = None, supports_reasoning: bool = False, + model: str | None = None, + session_id: str | None = None, **context: Any, ) -> tuple[dict[str, Any], dict[str, Any]]: - """OpenRouter passes the full reasoning_config dict as extra_body.reasoning.""" + """OpenRouter passes the full reasoning_config dict as extra_body.reasoning. + + For xAI Grok models routed through OpenRouter, attach the + ``x-grok-conv-id`` header so that xAI's prompt cache stays pinned to + the same backend server across turns. + """ extra_body: dict[str, Any] = {} if supports_reasoning: if reasoning_config is not None: extra_body["reasoning"] = dict(reasoning_config) else: extra_body["reasoning"] = {"enabled": True, "effort": "medium"} - return extra_body, {} + + extra_headers: dict[str, Any] = {} + if session_id and model and model.startswith(("x-ai/grok-", "xai/grok-")): + extra_headers["x-grok-conv-id"] = session_id + + return extra_body, {"extra_headers": extra_headers} if extra_headers else {} openrouter = OpenRouterProfile( -
scripts/release.py@@ -67,6 +67,7 @@ "[redacted:email]": "Qwinty", "[redacted:email]": "CalmProton", "[email protected]": "mbac", + "[redacted:email]": "Ninso112", "[redacted:email]": "wesleysimplicio", "[redacted:email]": "SiliconID", "[redacted:email]": "xieNniu", -
tests/providers/test_provider_profiles.py@@ -121,6 +121,52 @@ def test_default_reasoning(self): eb, _ = p.build_api_kwargs_extras(supports_reasoning=True) assert eb["reasoning"] == {"enabled": True, "effort": "medium"} + def test_grok_session_id_sets_cache_affinity_header(self): + """OpenRouter + Grok model + session_id => x-grok-conv-id header.""" + p = get_provider_profile("openrouter") + _, tl = p.build_api_kwargs_extras( + model="x-ai/grok-4", + session_id="sess-abc123", + ) + assert tl["extra_headers"]["x-grok-conv-id"] == "sess-abc123" + + def test_grok_xai_prefix_also_supported(self): + """xai/ prefix (without dash) should also get the header.""" + p = get_provider_profile("openrouter") + _, tl = p.build_api_kwargs_extras( + model="xai/grok-3", + session_id="sess-xyz", + ) + assert tl["extra_headers"]["x-grok-conv-id"] == "sess-xyz" + + def test_non_grok_model_no_affinity_header(self): + """OpenRouter + non-Grok model => no x-grok-conv-id header.""" + p = get_provider_profile("openrouter") + _, tl = p.build_api_kwargs_extras( + model="anthropic/claude-sonnet-4.6", + session_id="sess-abc123", + ) + assert "extra_headers" not in tl + assert "x-grok-conv-id" not in tl + + def test_grok_without_session_id_no_header(self): + """Grok model but no session_id => no header (nothing to pin).""" + p = get_provider_profile("openrouter") + _, tl = p.build_api_kwargs_extras(model="x-ai/grok-4") + assert "extra_headers" not in tl + + def test_grok_reasoning_and_header_together(self): + """Reasoning extra_body and Grok header should coexist.""" + p = get_provider_profile("openrouter") + eb, tl = p.build_api_kwargs_extras( + model="x-ai/grok-4", + session_id="sess-123", + supports_reasoning=True, + reasoning_config={"enabled": True, "effort": "high"}, + ) + assert eb["reasoning"] == {"enabled": True, "effort": "high"} + assert tl["extra_headers"]["x-grok-conv-id"] == "sess-123" + class TestNousProfile: def test_tags(self):
-
1 PR closed without merging
From the close comments: merged via another change 1
-
#22708 fix(openrouter): add x-grok-conv-id header for Grok models to improve prompt cache hit rates · merged via another change →
#22809,#22802
Closed issues · 1
- Closed#22705OpenRouter Grok prompt caching likely misses xAI server-affinity header
How this cluster was grouped
-
Link evidence (
structural links throughout): every member is connected by structural evidence: shared changed files, or a closing/superseded reference. -
Pairwise link density
0.67: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36.1.00means every pair is linked.
Pick up a Lab task
Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.
Open the LabAt a glance
- Merged PRs
- #22809
- Closed issues
- 1
- Attempts
- 1 PR closed without merging
- Fix link checked
- 9 May 2026