mirror of
https://github.com/openai/codex.git
synced 2026-10-25 18:28:38 +00:00
Materialize remote images before model requests
This commit is contained in:
@@ -106,6 +106,7 @@ class MockResponsesServer:
|
||||
self._responses: queue.Queue[MockSseResponse] = queue.Queue()
|
||||
self._requests: list[CapturedResponsesRequest] = []
|
||||
self._requests_lock = threading.Lock()
|
||||
self._get_responses: dict[str, tuple[bytes, str]] = {}
|
||||
self._server = _ResponsesHttpServer(("127.0.0.1", 0), _ResponsesHandler, self)
|
||||
self._thread = threading.Thread(
|
||||
target=self._server.serve_forever,
|
||||
@@ -158,6 +159,13 @@ class MockResponsesServer:
|
||||
)
|
||||
)
|
||||
|
||||
def serve_bytes(self, path: str, body: bytes, *, content_type: str) -> str:
|
||||
"""Serve bytes from a GET path and return its absolute URL."""
|
||||
if not path.startswith("/"):
|
||||
raise ValueError("GET response path must start with '/'")
|
||||
self._get_responses[path] = (body, content_type)
|
||||
return f"{self.url}{path}"
|
||||
|
||||
def requests(self) -> list[CapturedResponsesRequest]:
|
||||
"""Return all recorded Responses API requests."""
|
||||
with self._requests_lock:
|
||||
@@ -202,6 +210,9 @@ class MockResponsesServer:
|
||||
"""Return the next queued SSE response or fail the HTTP request."""
|
||||
return self._responses.get_nowait()
|
||||
|
||||
def _get_response(self, path: str) -> tuple[bytes, str] | None:
|
||||
return self._get_responses.get(path)
|
||||
|
||||
|
||||
class AppServerHarness:
|
||||
"""Test fixture that points a pinned runtime app-server at MockResponsesServer."""
|
||||
@@ -283,6 +294,14 @@ class _ResponsesHandler(BaseHTTPRequestHandler):
|
||||
|
||||
def do_GET(self) -> None:
|
||||
"""Serve a minimal `/v1/models` response if app-server asks for models."""
|
||||
if response := self.server.mock._get_response(self.path):
|
||||
body, content_type = response
|
||||
self.send_response(200)
|
||||
self.send_header("content-type", content_type)
|
||||
self.send_header("content-length", str(len(body)))
|
||||
self.end_headers()
|
||||
self.wfile.write(body)
|
||||
return
|
||||
if self.path.endswith("/v1/models") or self.path.endswith("/models"):
|
||||
self._send_json(
|
||||
{
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
|
||||
from app_server_harness import AppServerHarness
|
||||
from app_server_helpers import TINY_PNG_BYTES
|
||||
|
||||
from openai_codex import Codex, ImageInput, LocalImageInput, SkillInput, TextInput
|
||||
|
||||
|
||||
def test_remote_image_input_reaches_responses_api(
|
||||
def test_remote_image_input_is_materialized_before_responses_api(
|
||||
tmp_path,
|
||||
) -> None:
|
||||
"""Remote image inputs should survive the SDK and app-server boundary."""
|
||||
remote_image_url = "https://example.com/codex.png"
|
||||
"""Remote image inputs should be inlined by the app-server."""
|
||||
|
||||
with AppServerHarness(tmp_path) as harness:
|
||||
remote_image_url = harness.responses.serve_bytes(
|
||||
"/codex.png",
|
||||
TINY_PNG_BYTES,
|
||||
content_type="image/png",
|
||||
)
|
||||
harness.responses.enqueue_assistant_message(
|
||||
"remote image received",
|
||||
response_id="remote-image",
|
||||
@@ -34,7 +40,7 @@ def test_remote_image_input_reaches_responses_api(
|
||||
} == {
|
||||
"final_response": "remote image received",
|
||||
"contains_user_prompt": True,
|
||||
"image_urls": [remote_image_url],
|
||||
"image_urls": [f"data:image/png;base64,{base64.b64encode(TINY_PNG_BYTES).decode('ascii')}"],
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user