The PR added APIConnectionError handling to the main request and iteration try blocks but missed the finalization drain loop (line ~1492). That site catches httpx transport errors to preserve an already-completed, already-billed response when the drain iterator fails. Without the APIConnectionError handler, an SDK-wrapped transport error during drain would propagate uncaught and discard the completed response. Also strengthens the test's no-payload-leak assertion to check the full request body and URL are absent from the log message, not just the literal string 'payload'.
64 lines
2.0 KiB
Python
64 lines
2.0 KiB
Python
"""Diagnostics for Codex Responses transport failures."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from types import SimpleNamespace
|
|
|
|
import httpx
|
|
import pytest
|
|
from openai import APIConnectionError
|
|
|
|
from agent.codex_runtime import _codex_request_failure_details, run_codex_stream
|
|
|
|
|
|
def test_transport_failure_without_attached_request_reports_unknown_size():
|
|
error = httpx.RemoteProtocolError("connection closed")
|
|
|
|
request_body_bytes, exception_chain = _codex_request_failure_details(error)
|
|
|
|
assert request_body_bytes is None
|
|
assert exception_chain == "RemoteProtocolError"
|
|
|
|
|
|
def test_transport_failure_logs_exact_request_bytes_and_class_chain(caplog):
|
|
request_content = b'{"input":"payload"}'
|
|
request = httpx.Request(
|
|
"POST",
|
|
"https://example.invalid/responses",
|
|
content=request_content,
|
|
)
|
|
transport_error = httpx.RemoteProtocolError(
|
|
"server disconnected without sending a response",
|
|
request=request,
|
|
)
|
|
connection_error = APIConnectionError(request=request)
|
|
connection_error.__cause__ = transport_error
|
|
|
|
class FailingResponses:
|
|
def create(self, **_kwargs):
|
|
raise connection_error
|
|
|
|
client = SimpleNamespace(responses=FailingResponses())
|
|
agent = SimpleNamespace(
|
|
_interrupt_requested=False,
|
|
_current_api_request_id="request-id",
|
|
_fallback_index=0,
|
|
is_subagent=False,
|
|
model="gpt-5.6-sol",
|
|
provider="openai-codex",
|
|
session_id="",
|
|
)
|
|
|
|
with caplog.at_level(logging.WARNING, logger="agent.codex_runtime"):
|
|
with pytest.raises(APIConnectionError):
|
|
run_codex_stream(agent, {"model": "gpt-5.6-sol"}, client=client)
|
|
|
|
message = caplog.messages[-1]
|
|
assert f"serialized_request_body_bytes={len(request_content)}" in message
|
|
assert "stream_opened=false" in message
|
|
assert "exception_chain=APIConnectionError <- RemoteProtocolError" in message
|
|
assert "payload" not in message
|
|
assert request_content.decode() not in message
|
|
assert "example.invalid" not in message
|