diff --git a/headroom/proxy/server.py b/headroom/proxy/server.py index 40d70eb20..94a045870 100644 --- a/headroom/proxy/server.py +++ b/headroom/proxy/server.py @@ -1818,10 +1818,13 @@ def create_app(config: ProxyConfig | None = None) -> FastAPI: """Passthrough for /backend-api/responses/* sub-endpoints.""" return await openai_responses_sub(request, sub_path) - @app.api_route("/backend-api/codex/responses/{sub_path:path}", methods=["GET", "POST", "DELETE"]) + @app.api_route( + "/backend-api/codex/responses/{sub_path:path}", methods=["GET", "POST", "DELETE"] + ) async def openai_codex_nested_responses_sub(request: Request, sub_path: str): """Passthrough for /backend-api/codex/responses/* sub-endpoints.""" return await openai_responses_sub(request, sub_path) + # OpenAI Batch API endpoints (with compression!) @app.post("/v1/batches") async def create_batch(request: Request): diff --git a/tests/test_proxy_codex_route_aliases.py b/tests/test_proxy_codex_route_aliases.py index 67aacee3a..797361f61 100644 --- a/tests/test_proxy_codex_route_aliases.py +++ b/tests/test_proxy_codex_route_aliases.py @@ -1,3 +1,4 @@ +import httpx from fastapi import WebSocket from fastapi.responses import JSONResponse from fastapi.testclient import TestClient @@ -35,3 +36,38 @@ def test_codex_responses_websocket_aliases_delegate_to_openai_handler(monkeypatc assert websocket.receive_json() == {"ok": True, "path": path} assert seen_paths == ["/backend-api/responses", "/backend-api/codex/responses"] + + +def test_codex_responses_subpath_aliases_delegate_to_passthrough(): + class FakeAsyncClient: + def __init__(self) -> None: + self.calls: list[tuple[str, str]] = [] + + async def request(self, method, url, **_kwargs): # type: ignore[no-untyped-def] + self.calls.append((method, url)) + return httpx.Response(200, json={"method": method, "url": url}) + + async def aclose(self) -> None: + return None + + with TestClient(create_app(ProxyConfig())) as client: + fake_http_client = FakeAsyncClient() + client.app.state.proxy.http_client = fake_http_client + client.app.state.proxy.OPENAI_API_URL = "https://api.openai.test" + + api_key_response = client.post( + "/backend-api/responses/compact?trace=1", + json={"model": "gpt-5.3-codex"}, + ) + chatgpt_response = client.post( + "/backend-api/codex/responses/compact?trace=2", + headers={"chatgpt-account-id": "acct_123"}, + json={"model": "gpt-5.3-codex"}, + ) + + assert api_key_response.status_code == 200 + assert chatgpt_response.status_code == 200 + assert fake_http_client.calls == [ + ("POST", "https://api.openai.test/v1/responses/compact?trace=1"), + ("POST", "https://chatgpt.com/backend-api/codex/responses/compact?trace=2"), + ]