fix(provider): stabilize Codex prompt cache routing (#5540)

This commit is contained in:
chengyongru
2026-08-26 01:36:00 +08:00
committed by GitHub
parent 3ee3791626
commit 42f37dc4c0
10 changed files with 91 additions and 26 deletions
@@ -9,6 +9,7 @@ import pytest
from nanobot.providers.base import (
LLMProvider,
LLMResponse,
ProviderCallContext,
ProviderConversationState,
ToolCallRequest,
)
@@ -289,3 +290,23 @@ def test_independent_request_exposes_context_without_capability_check() -> None:
assert provider_context.conversation_state is None
assert provider_context.context_window_tokens == 200_000
provider.supports_native_compaction.assert_not_called()
def test_independent_request_exposes_session_id_without_token_budget() -> None:
provider = _provider(compact=False)
messages = [{"role": "user", "content": "hello"}]
controller = ProviderConversationStateController(
provider=provider,
model="gpt-5.6",
messages=messages,
session_id="webui:cache-test",
)
provider_context = controller.prepare_request(
messages,
context_window_tokens=None,
)
assert provider_context == ProviderCallContext(
session_id="webui:cache-test",
)
+33 -18
View File
@@ -260,8 +260,8 @@ async def test_codex_request_uses_configured_proxy(monkeypatch) -> None:
@pytest.mark.asyncio
async def test_codex_prompt_cache_key_uses_stable_conversation_prefix(monkeypatch) -> None:
bodies: list[dict] = []
async def test_codex_omits_prompt_cache_key_without_session_id(monkeypatch) -> None:
bodies: list[dict[str, Any]] = []
_mock_codex_token(monkeypatch)
@@ -289,25 +289,40 @@ async def test_codex_prompt_cache_key_uses_stable_conversation_prefix(monkeypatc
{"role": "assistant", "content": "first answer"},
],
)
await provider.chat(
[
{"role": "system", "content": "You are nanobot."},
{"role": "user", "content": "first request"},
{"role": "assistant", "content": "first answer"},
{"role": "user", "content": "follow up"},
],
)
await provider.chat(
[
{"role": "system", "content": "You are nanobot."},
{"role": "user", "content": "different request"},
{"role": "assistant", "content": "first answer"},
],
)
assert "prompt_cache_key" not in bodies[0]
assert "service_tier" not in bodies[0]
@pytest.mark.asyncio
async def test_codex_prompt_cache_key_prefers_stable_session_id(monkeypatch) -> None:
bodies: list[dict[str, Any]] = []
_mock_codex_token(monkeypatch)
async def fake_request(_url, _headers, body, **_kwargs):
bodies.append(body)
return provider_base.LLMResponse(content="ok")
monkeypatch.setattr("nanobot.providers.openai_codex_provider._request_codex", fake_request)
provider = OpenAICodexProvider()
for session_id, first_request in (
("session-a", "first request"),
("session-a", "different visible prefix"),
("session-b", "first request"),
):
await provider.chat(
[
{"role": "system", "content": "You are nanobot."},
{"role": "user", "content": first_request},
],
provider_context=provider_base.ProviderCallContext(
session_id=session_id,
),
)
assert bodies[0]["prompt_cache_key"] == bodies[1]["prompt_cache_key"]
assert bodies[0]["prompt_cache_key"] != bodies[2]["prompt_cache_key"]
assert all("service_tier" not in body for body in bodies)
@pytest.mark.asyncio
+5 -1
View File
@@ -431,13 +431,17 @@ async def test_image_retry_discards_provider_state_with_images(
response = await provider.chat_with_retry(
messages=messages,
provider_context=ProviderCallContext(conversation_state=state),
provider_context=ProviderCallContext(
conversation_state=state,
session_id="webui:cache-test",
),
)
assert response.content == "ok, no image"
retry_context = provider.contexts[-1]
assert isinstance(retry_context, ProviderCallContext)
assert retry_context.conversation_state is None
assert retry_context.session_id == "webui:cache-test"
public_content = messages[0]["content"]
if isinstance(public_content, list):
assert all(block.get("type") != "image_url" for block in public_content)