mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-09-04 02:01:48 +03:00
fix(provider): stabilize Codex prompt cache routing (#5540)
This commit is contained in:
@@ -384,12 +384,16 @@ class TestFallbackOnPrimaryError:
|
||||
messages=[{"role": "user", "content": "hi"}],
|
||||
model="gpt-5.6",
|
||||
max_tokens=10_000,
|
||||
provider_context=ProviderCallContext(context_window_tokens=50_000),
|
||||
provider_context=ProviderCallContext(
|
||||
context_window_tokens=50_000,
|
||||
session_id="webui:cache-test",
|
||||
),
|
||||
)
|
||||
|
||||
primary_context = primary.context_calls[0]
|
||||
assert primary_context is not None
|
||||
assert primary_context.context_window_tokens == 200_000
|
||||
assert primary_context.session_id == "webui:cache-test"
|
||||
assert resolve_compact_threshold(
|
||||
primary_context.context_window_tokens,
|
||||
10_000,
|
||||
|
||||
@@ -8,6 +8,7 @@ from nanobot.providers.base import (
|
||||
GenerationSettings,
|
||||
LLMProvider,
|
||||
LLMResponse,
|
||||
ProviderCallContext,
|
||||
ToolCallRequest,
|
||||
)
|
||||
from nanobot.utils.llm_runtime import LLMRuntime
|
||||
@@ -22,6 +23,7 @@ async def test_active_run_keeps_provider_captured_at_admission() -> None:
|
||||
first_calls = 0
|
||||
second_calls = 0
|
||||
request_temperatures: list[float] = []
|
||||
request_session_ids: list[str | None] = []
|
||||
selected_runtime = LLMRuntime.capture(
|
||||
first_provider,
|
||||
"captured-model",
|
||||
@@ -33,6 +35,9 @@ async def test_active_run_keeps_provider_captured_at_admission() -> None:
|
||||
nonlocal first_calls, selected_runtime
|
||||
first_calls += 1
|
||||
request_temperatures.append(kwargs["temperature"])
|
||||
provider_context = kwargs["provider_context"]
|
||||
assert isinstance(provider_context, ProviderCallContext)
|
||||
request_session_ids.append(provider_context.session_id)
|
||||
selected_runtime = LLMRuntime.capture(
|
||||
second_provider,
|
||||
"future-model",
|
||||
@@ -63,9 +68,11 @@ async def test_active_run_keeps_provider_captured_at_admission() -> None:
|
||||
runtime=selected_runtime,
|
||||
max_iterations=2,
|
||||
max_tool_result_chars=AgentDefaults().max_tool_result_chars,
|
||||
session_key="webui:cache-test",
|
||||
))
|
||||
|
||||
assert first_calls == 2
|
||||
assert second_calls == 0
|
||||
assert request_temperatures == [0.2, 0.2]
|
||||
assert request_session_ids == ["webui:cache-test", "webui:cache-test"]
|
||||
assert selected_runtime.provider is second_provider
|
||||
|
||||
@@ -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",
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user