test(codex): pin the retention drop to real endpoints
The salvaged compatibility test stubs `_is_codex_backend=lambda: False` on a SimpleNamespace, so it proves the helper honors its own boolean but not that the boolean is right for any real endpoint. A predicate change that widened the drop onto retention-supporting hosts would keep it green. Adds a parametrized test that builds a real AIAgent per base URL and asserts the drop only fires for chatgpt.com/backend-api/codex, while api.meta.ai, bedrock-mantle.*.api.aws, api.openai.com and a same-host/different-path backend keep their supported 24h value. Also asserts prompt_cache_key survives untouched on every endpoint, since retention and cache-key routing are independent and the guard must not disturb caching. Verified non-vacuous: relaxing the guard's condition to drop on every endpoint fails 4 of the 6 cases (Meta, Bedrock, OpenAI, non-codex path). Drive-by on the guard itself: drop the dead `None` default on the `pop` that is already gated by an `in` check, and record why the predicate is resolved via getattr -- run_codex_stream is driven with lightweight stand-in agents that lack `_is_codex_backend`, so a bare call would raise AttributeError.
This commit is contained in:
@@ -1322,12 +1322,16 @@ def _sanitize_consumer_codex_request(
|
||||
rejected by the provider.
|
||||
"""
|
||||
sanitized = dict(request)
|
||||
# Resolved defensively on purpose: run_codex_stream is also driven with
|
||||
# lightweight stand-in agents that carry only the attributes a given path
|
||||
# needs (see tests/agent/test_codex_request_transport_diagnostics.py), so a
|
||||
# bare agent._is_codex_backend() here would raise AttributeError on them.
|
||||
backend_predicate = getattr(agent, "_is_codex_backend", None)
|
||||
is_consumer_codex = (
|
||||
bool(backend_predicate()) if callable(backend_predicate) else False
|
||||
)
|
||||
if is_consumer_codex and "prompt_cache_retention" in sanitized:
|
||||
sanitized.pop("prompt_cache_retention", None)
|
||||
sanitized.pop("prompt_cache_retention")
|
||||
logger.warning(
|
||||
"Dropped unsupported prompt_cache_retention at consumer Codex "
|
||||
"wire boundary (model=%s).",
|
||||
|
||||
@@ -602,6 +602,63 @@ def test_consumer_codex_wire_guard_preserves_compatible_endpoint_retention():
|
||||
assert sanitized is not request
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"base_url,model,expect_dropped",
|
||||
[
|
||||
# Consumer ChatGPT Codex: the endpoint rejects retention outright.
|
||||
("https://chatgpt.com/backend-api/codex", "gpt-5.6-sol", True),
|
||||
("https://chatgpt.com/backend-api/codex", "gpt-5-codex", True),
|
||||
# Hosts that support 24h retention must keep it (#70083, #88601).
|
||||
("https://api.meta.ai/v1", "gpt-5.6-sol", False),
|
||||
("https://bedrock-mantle.us-east-1.api.aws/v1", "openai.gpt-5.5", False),
|
||||
# Non-Codex OpenAI and a same-host/different-path backend are both
|
||||
# outside the consumer-Codex contract the guard enforces.
|
||||
("https://api.openai.com/v1", "gpt-5.6-sol", False),
|
||||
("https://chatgpt.com/backend-api/other", "gpt-5.6-sol", False),
|
||||
],
|
||||
)
|
||||
def test_wire_guard_scopes_retention_drop_by_real_endpoint(
|
||||
monkeypatch,
|
||||
base_url,
|
||||
model,
|
||||
expect_dropped,
|
||||
):
|
||||
"""The drop is scoped by the real endpoint, not by a stubbed predicate.
|
||||
|
||||
The sibling test above pins the helper's contract against an explicit
|
||||
boolean. This one drives ``_is_codex_backend()`` off a real ``AIAgent``
|
||||
built on each base URL, so a change to the hostname/path predicate that
|
||||
widened the drop onto retention-supporting hosts would fail here.
|
||||
"""
|
||||
from agent.codex_runtime import _sanitize_consumer_codex_request
|
||||
|
||||
_patch_agent_bootstrap(monkeypatch)
|
||||
agent = run_agent.AIAgent(
|
||||
model=model,
|
||||
api_mode="codex_responses",
|
||||
base_url=base_url,
|
||||
api_key="codex-token",
|
||||
quiet_mode=True,
|
||||
max_iterations=4,
|
||||
skip_context_files=True,
|
||||
skip_memory=True,
|
||||
)
|
||||
|
||||
request = {
|
||||
"model": model,
|
||||
"prompt_cache_key": "cache-key-sentinel",
|
||||
"prompt_cache_retention": "24h",
|
||||
"input": [{"role": "user", "content": "hi"}],
|
||||
}
|
||||
sanitized = _sanitize_consumer_codex_request(agent, request)
|
||||
|
||||
assert ("prompt_cache_retention" not in sanitized) is expect_dropped
|
||||
# Cache-key routing is independent of retention: the guard must never
|
||||
# disturb the prompt-cache key, on any endpoint.
|
||||
assert sanitized["prompt_cache_key"] == "cache-key-sentinel"
|
||||
assert request["prompt_cache_retention"] == "24h"
|
||||
|
||||
|
||||
def test_run_codex_stream_returns_collected_items_when_stream_ends_without_terminal(monkeypatch):
|
||||
"""The event-driven path tolerates streams that end without a terminal frame.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user