{"repo":"NousResearch/hermes-agent","public_id":"22705","display_label":"OpenRouter Grok prompt caching header investigation and fix","summary":"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.","subsystem":null,"counts":{"members":3,"open_issues":0,"open_issues_distinct":0,"open_prs":0,"closed_issues":1,"merged_prs":1,"attempt_prs":1},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":22809,"merged_at":"2026-05-09T20:38:52+00:00"},"resolution":{"state":"resolved","by":"pr-22809"},"freshness":{"state":"verified","last_verified_at":"2026-05-09T20:38:52+00:00","is_stale":false},"grouping":{"backbone":"STRUCTURAL_SPANNING","link_density":0.6666666666666666},"fix_edges":[{"issue_number":22705,"pr_number":22809,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true,"pr_merged_at":"2026-05-09T20:38:52+00:00"}],"members":[{"kind":"issue","number":22705,"title":"OpenRouter Grok prompt caching likely misses xAI server-affinity header","url":null,"state":"closed"},{"kind":"pr","number":22708,"title":"fix(openrouter): add x-grok-conv-id header for Grok models to improve prompt cache hit rates","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#22809","number":22809},{"kind":"pr","value":"#22802","number":22802}],"category":"merged_via","implication":"superseded"},"merged":false},{"kind":"pr","number":22809,"title":"fix(openrouter): add x-grok-conv-id header for Grok models (carve-out of #22708)","url":null,"state":"merged","close_reason":{"category":"merged"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["agent/transports/chat_completions.py","plugins/model-providers/openrouter/__init__.py","scripts/release.py","tests/providers/test_provider_profiles.py"],"fix_hunks":[{"file":"agent/transports/chat_completions.py","hunks":["@@ -448,6 +448,7 @@ def _build_kwargs_from_profile(self, profile, model, sanitized, tools, params):\n                 qwen_session_metadata=params.get(\"qwen_session_metadata\"),\n                 model=model,\n                 ollama_num_ctx=params.get(\"ollama_num_ctx\"),\n+                session_id=params.get(\"session_id\"),\n             )\n         )\n         api_kwargs.update(top_level_from_profile)"]},{"file":"plugins/model-providers/openrouter/__init__.py","hunks":["@@ -53,16 +53,28 @@ def build_api_kwargs_extras(\n         *,\n         reasoning_config: dict | None = None,\n         supports_reasoning: bool = False,\n+        model: str | None = None,\n+        session_id: str | None = None,\n         **context: Any,\n     ) -> tuple[dict[str, Any], dict[str, Any]]:\n-        \"\"\"OpenRouter passes the full reasoning_config dict as extra_body.reasoning.\"\"\"\n+        \"\"\"OpenRouter passes the full reasoning_config dict as extra_body.reasoning.\n+\n+        For xAI Grok models routed through OpenRouter, attach the\n+        ``x-grok-conv-id`` header so that xAI's prompt cache stays pinned to\n+        the same backend server across turns.\n+        \"\"\"\n         extra_body: dict[str, Any] = {}\n         if supports_reasoning:\n             if reasoning_config is not None:\n                 extra_body[\"reasoning\"] = dict(reasoning_config)\n             else:\n                 extra_body[\"reasoning\"] = {\"enabled\": True, \"effort\": \"medium\"}\n-        return extra_body, {}\n+\n+        extra_headers: dict[str, Any] = {}\n+        if session_id and model and model.startswith((\"x-ai/grok-\", \"xai/grok-\")):\n+            extra_headers[\"x-grok-conv-id\"] = session_id\n+\n+        return extra_body, {\"extra_headers\": extra_headers} if extra_headers else {}\n \n \n openrouter = OpenRouterProfile("]},{"file":"scripts/release.py","hunks":["@@ -67,6 +67,7 @@\n     \"[redacted:email]\": \"Qwinty\",\n     \"[redacted:email]\": \"CalmProton\",\n     \"308068+mbac@users.noreply.github.com\": \"mbac\",\n+    \"[redacted:email]\": \"Ninso112\",\n     \"[redacted:email]\": \"wesleysimplicio\",\n     \"[redacted:email]\": \"SiliconID\",\n     \"[redacted:email]\": \"xieNniu\","]},{"file":"tests/providers/test_provider_profiles.py","hunks":["@@ -121,6 +121,52 @@ def test_default_reasoning(self):\n         eb, _ = p.build_api_kwargs_extras(supports_reasoning=True)\n         assert eb[\"reasoning\"] == {\"enabled\": True, \"effort\": \"medium\"}\n \n+    def test_grok_session_id_sets_cache_affinity_header(self):\n+        \"\"\"OpenRouter + Grok model + session_id => x-grok-conv-id header.\"\"\"\n+        p = get_provider_profile(\"openrouter\")\n+        _, tl = p.build_api_kwargs_extras(\n+            model=\"x-ai/grok-4\",\n+            session_id=\"sess-abc123\",\n+        )\n+        assert tl[\"extra_headers\"][\"x-grok-conv-id\"] == \"sess-abc123\"\n+\n+    def test_grok_xai_prefix_also_supported(self):\n+        \"\"\"xai/ prefix (without dash) should also get the header.\"\"\"\n+        p = get_provider_profile(\"openrouter\")\n+        _, tl = p.build_api_kwargs_extras(\n+            model=\"xai/grok-3\",\n+            session_id=\"sess-xyz\",\n+        )\n+        assert tl[\"extra_headers\"][\"x-grok-conv-id\"] == \"sess-xyz\"\n+\n+    def test_non_grok_model_no_affinity_header(self):\n+        \"\"\"OpenRouter + non-Grok model => no x-grok-conv-id header.\"\"\"\n+        p = get_provider_profile(\"openrouter\")\n+        _, tl = p.build_api_kwargs_extras(\n+            model=\"anthropic/claude-sonnet-4.6\",\n+            session_id=\"sess-abc123\",\n+        )\n+        assert \"extra_headers\" not in tl\n+        assert \"x-grok-conv-id\" not in tl\n+\n+    def test_grok_without_session_id_no_header(self):\n+        \"\"\"Grok model but no session_id => no header (nothing to pin).\"\"\"\n+        p = get_provider_profile(\"openrouter\")\n+        _, tl = p.build_api_kwargs_extras(model=\"x-ai/grok-4\")\n+        assert \"extra_headers\" not in tl\n+\n+    def test_grok_reasoning_and_header_together(self):\n+        \"\"\"Reasoning extra_body and Grok header should coexist.\"\"\"\n+        p = get_provider_profile(\"openrouter\")\n+        eb, tl = p.build_api_kwargs_extras(\n+            model=\"x-ai/grok-4\",\n+            session_id=\"sess-123\",\n+            supports_reasoning=True,\n+            reasoning_config={\"enabled\": True, \"effort\": \"high\"},\n+        )\n+        assert eb[\"reasoning\"] == {\"enabled\": True, \"effort\": \"high\"}\n+        assert tl[\"extra_headers\"][\"x-grok-conv-id\"] == \"sess-123\"\n+\n \n class TestNousProfile:\n     def test_tags(self):\n"]}]}],"trust":{"default_tier":"verified","includes_suggested":false}}