chatgpt2api / test /test_multi_image_results.py
huanx's picture
Deploy chatgpt2api to Hugging Face Space
bf53440 verified
Raw
History Blame Contribute Delete
7.25 kB
from __future__ import annotations
import base64
import unittest
from unittest import mock
from services.config import config
from services.openai_backend_api import OpenAIBackendAPI
from services.protocol.conversation import ImageOutput, extract_conversation_ids
from services.protocol.openai_v1_response import stream_image_response
def _conversation(file_ids: list[str], sediment_ids: list[str] | None = None) -> dict:
parts: list[object] = [
{"content_type": "image_asset_pointer", "asset_pointer": f"file-service://{file_id}"}
for file_id in file_ids
]
parts.extend(f"sediment://{sediment_id}" for sediment_id in (sediment_ids or []))
return {
"mapping": {
"tool": {
"message": {
"author": {"role": "tool"},
"create_time": 1,
"metadata": {"async_task_type": "image_gen"},
"content": {"content_type": "multimodal_text", "parts": parts},
}
}
}
}
class FakeBackend(OpenAIBackendAPI):
def __init__(self, conversations: list[dict] | None = None) -> None:
self.conversations = conversations or []
self.calls = 0
self.file_urls: dict[str, str] = {}
self.sediment_urls: dict[str, str] = {}
def _get_conversation(self, conversation_id: str) -> dict:
self.calls += 1
index = min(self.calls - 1, len(self.conversations) - 1)
return self.conversations[index]
def _get_file_download_url(self, file_id: str) -> str:
return self.file_urls.get(file_id, "")
def _get_attachment_download_url(self, conversation_id: str, attachment_id: str) -> str:
return self.sediment_urls.get(attachment_id, "")
class MultiImageResultTests(unittest.TestCase):
def test_stream_id_extractor_keeps_full_file_ids(self) -> None:
payload = (
'{"conversation_id":"conv-1"} '
'file-service://file-first_123-extra sediment://sed-second_456-extra'
)
conversation_id, file_ids, sediment_ids = extract_conversation_ids(payload)
self.assertEqual(conversation_id, "conv-1")
self.assertEqual(file_ids, ["file-first_123-extra"])
self.assertEqual(sediment_ids, ["sed-second_456-extra"])
def test_conversation_record_extractor_finds_all_generated_assets(self) -> None:
backend = FakeBackend()
conversation = {
"mapping": {
"user": {
"message": {
"author": {"role": "user"},
"content": {"parts": ["file-service://file-user-input"]},
}
},
"tool": {
"message": {
"author": {"role": "tool"},
"create_time": 1,
"metadata": {
"async_task_type": "image_gen",
"nested": {"asset": "file-service://file-second"},
},
"content": {
"content_type": "text",
"parts": [
{"content_type": "image_asset_pointer", "asset_pointer": "file-service://file-first"},
"sediment://sed-first",
],
},
}
},
"assistant": {
"message": {
"author": {"role": "assistant"},
"create_time": 2,
"metadata": {},
"content": {
"parts": [
{"content_type": "image_asset_pointer", "asset_pointer": "file-service://file-third"}
]
},
}
},
}
}
records = backend._extract_image_tool_records(conversation)
file_ids = [file_id for record in records for file_id in record["file_ids"]]
sediment_ids = [sediment_id for record in records for sediment_id in record["sediment_ids"]]
self.assertEqual(file_ids, ["file-first", "file-second", "file-third"])
self.assertEqual(sediment_ids, ["sed-first"])
def test_poll_waits_for_generated_asset_ids_to_settle(self) -> None:
backend = FakeBackend([
_conversation(["file-one"]),
_conversation(["file-one", "file-two"], ["sed-one"]),
_conversation(["file-one", "file-two"], ["sed-one"]),
])
with (
mock.patch.dict(config.data, {"image_poll_initial_wait_secs": 0, "image_poll_interval_secs": 0.5}),
mock.patch("services.openai_backend_api.time.sleep", lambda _seconds: None),
):
file_ids, sediment_ids = backend._poll_image_results("conv-1", timeout_secs=10)
self.assertEqual(file_ids, ["file-one", "file-two"])
self.assertEqual(sediment_ids, ["sed-one"])
self.assertEqual(backend.calls, 3)
def test_resolver_uses_file_and_sediment_urls(self) -> None:
backend = FakeBackend()
backend.file_urls = {"file-one": "https://files.test/one.png"}
backend.sediment_urls = {
"sed-one": "https://attachments.test/one.png",
"sed-two": "https://attachments.test/two.png",
}
urls = backend._resolve_image_urls("conv-1", ["file-one"], ["sed-one", "sed-two"])
self.assertEqual(urls, [
"https://files.test/one.png",
"https://attachments.test/one.png",
"https://attachments.test/two.png",
])
def test_resolver_keeps_stream_ids_when_poll_extension_fails(self) -> None:
backend = FakeBackend()
backend.file_urls = {"file-one": "https://files.test/one.png"}
backend._get_conversation = mock.Mock(side_effect=RuntimeError("poll failed"))
with mock.patch("services.openai_backend_api.time.sleep", lambda _seconds: None):
urls = backend.resolve_conversation_image_urls("conv-1", ["file-one"], [], poll=True)
self.assertEqual(urls, ["https://files.test/one.png"])
def test_responses_stream_emits_all_image_output_items(self) -> None:
first = base64.b64encode(b"first").decode("ascii")
second = base64.b64encode(b"second").decode("ascii")
events = list(stream_image_response(
[ImageOutput(
kind="result",
model="gpt-image-2",
index=1,
total=1,
data=[{"b64_json": first}, {"b64_json": second}],
)],
"draw two options",
"gpt-image-2",
))
done_events = [event for event in events if event.get("type") == "response.output_item.done"]
completed = next(event["response"] for event in events if event.get("type") == "response.completed")
self.assertEqual([event["output_index"] for event in done_events], [0, 1])
self.assertEqual([item["result"] for item in completed["output"]], [first, second])
if __name__ == "__main__":
unittest.main()