Spaces:
Paused
Paused
| from __future__ import annotations | |
| import json | |
| import time | |
| import unittest | |
| import requests | |
| from test.utils import save_image | |
| AUTH_KEY = "chatgpt2api" | |
| BASE_URL = "http://localhost:8000" | |
| TEXT_MODEL = "auto" | |
| IMAGE_MODEL = "gpt-image-2" | |
| CODEX_IMAGE_MODEL = "codex-gpt-image-2" | |
| class ResponsesTests(unittest.TestCase): | |
| def _iter_sse_payloads(response: requests.Response): | |
| for line in response.iter_lines(): | |
| if not line: | |
| continue | |
| text = line.decode("utf-8", errors="replace") | |
| yield text | |
| def test_text_response_http(self): | |
| """测试 Responses 文本的非流式 HTTP 调用。""" | |
| response = requests.post( | |
| f"{BASE_URL}/v1/responses", | |
| headers={"Authorization": f"Bearer {AUTH_KEY}"}, | |
| json={ | |
| "model": TEXT_MODEL, | |
| "input": [ | |
| { | |
| "role": "user", | |
| "content": [ | |
| {"type": "input_text", "text": "你好,请简单介绍一下你自己。"}, | |
| ], | |
| } | |
| ], | |
| }, | |
| timeout=300, | |
| ) | |
| self.assertEqual(response.status_code, 200, response.text) | |
| print("responses text non-stream status:") | |
| print(response.status_code) | |
| print("responses text non-stream result:") | |
| try: | |
| payload = response.json() | |
| print(json.dumps(payload, ensure_ascii=False, indent=2)) | |
| self.assertEqual(payload.get("object"), "response") | |
| self.assertEqual(payload.get("status"), "completed") | |
| self.assertTrue(isinstance(payload.get("output"), list) and payload.get("output")) | |
| except Exception: | |
| print(response.text) | |
| raise | |
| def test_text_response_stream_http(self): | |
| """测试 Responses 文本的流式 HTTP 调用。""" | |
| response = requests.post( | |
| f"{BASE_URL}/v1/responses", | |
| headers={"Authorization": f"Bearer {AUTH_KEY}"}, | |
| json={ | |
| "model": TEXT_MODEL, | |
| "input": [ | |
| { | |
| "role": "user", | |
| "content": [ | |
| {"type": "input_text", "text": "你好,请简单介绍一下你自己。"}, | |
| ], | |
| } | |
| ], | |
| "stream": True, | |
| }, | |
| stream=True, | |
| timeout=300, | |
| ) | |
| self.assertEqual(response.status_code, 200, response.text) | |
| self.assertTrue( | |
| response.headers.get("content-type", "").startswith("text/event-stream"), | |
| response.headers.get("content-type", ""), | |
| ) | |
| started_at = time.time() | |
| print("responses text stream status:") | |
| print(response.status_code) | |
| print("responses text stream chunks:") | |
| event_types = [] | |
| for text in self._iter_sse_payloads(response): | |
| print(f"{time.time() - started_at:6.2f}s {text}") | |
| if not text.startswith("data:"): | |
| continue | |
| payload_text = text[5:].strip() | |
| if payload_text == "[DONE]": | |
| break | |
| try: | |
| payload = json.loads(payload_text) | |
| except Exception: | |
| continue | |
| event_type = str(payload.get("type") or "") | |
| if event_type: | |
| event_types.append(event_type) | |
| self.assertIn("response.created", event_types) | |
| self.assertIn("response.completed", event_types) | |
| def test_image_response_http(self): | |
| """测试 Responses 画图的非流式 HTTP 调用。""" | |
| response = requests.post( | |
| f"{BASE_URL}/v1/responses", | |
| headers={"Authorization": f"Bearer {AUTH_KEY}"}, | |
| json={ | |
| "model": IMAGE_MODEL, | |
| "input": [ | |
| { | |
| "role": "user", | |
| "content": [ | |
| {"type": "input_text", "text": "我想做一张南京城市宣传海报图。"}, | |
| ], | |
| } | |
| ], | |
| "tools": [{"type": "image_generation"}], | |
| }, | |
| timeout=300, | |
| ) | |
| self.assertEqual(response.status_code, 200, response.text) | |
| saved_paths = [] | |
| try: | |
| payload = response.json() | |
| except Exception: | |
| payload = {} | |
| for index, item in enumerate(payload.get("output") or [], start=1): | |
| if not isinstance(item, dict): | |
| continue | |
| image_b64 = str(item.get("result") or "") | |
| if image_b64: | |
| saved_paths.append(save_image(image_b64, f"responses_image_non_stream_{index}")) | |
| print("responses image non-stream status:") | |
| print(response.status_code) | |
| print("responses image non-stream result:") | |
| print(json.dumps(payload, ensure_ascii=False, indent=2)) | |
| print("responses image non-stream saved files:") | |
| for path in saved_paths: | |
| print(path) | |
| def test_image_response_stream_http(self): | |
| """测试 Responses 画图的流式 HTTP 调用。""" | |
| response = requests.post( | |
| f"{BASE_URL}/v1/responses", | |
| headers={"Authorization": f"Bearer {AUTH_KEY}"}, | |
| json={ | |
| "model": IMAGE_MODEL, | |
| "input": [ | |
| { | |
| "role": "user", | |
| "content": [ | |
| {"type": "input_text", "text": "我想做一张南京城市宣传海报图。"}, | |
| ], | |
| } | |
| ], | |
| "tools": [{"type": "image_generation"}], | |
| "stream": True, | |
| }, | |
| stream=True, | |
| timeout=300, | |
| ) | |
| self.assertEqual(response.status_code, 200, response.text) | |
| self.assertTrue( | |
| response.headers.get("content-type", "").startswith("text/event-stream"), | |
| response.headers.get("content-type", ""), | |
| ) | |
| started_at = time.time() | |
| saved_paths = [] | |
| print("responses image stream status:") | |
| print(response.status_code) | |
| print("responses image stream chunks:") | |
| for text in self._iter_sse_payloads(response): | |
| print(f"{time.time() - started_at:6.2f}s {text}") | |
| if not text.startswith("data:"): | |
| continue | |
| payload_text = text[5:].strip() | |
| if payload_text == "[DONE]": | |
| break | |
| try: | |
| payload = json.loads(payload_text) | |
| except Exception: | |
| continue | |
| if payload.get("type") != "response.output_item.done": | |
| continue | |
| item = payload.get("item") or {} | |
| if str(item.get("type") or "") != "image_generation_call": | |
| continue | |
| image_b64 = str(item.get("result") or "") | |
| if image_b64: | |
| saved_paths.append(save_image(image_b64, f"responses_image_stream_{len(saved_paths) + 1}")) | |
| print("responses image stream saved files:") | |
| for path in saved_paths: | |
| print(path) | |
| def test_codex_image_response_http(self): | |
| """测试 Responses 的 codex 画图非流式 HTTP 调用。""" | |
| response = requests.post( | |
| f"{BASE_URL}/v1/responses", | |
| headers={"Authorization": f"Bearer {AUTH_KEY}"}, | |
| json={ | |
| "model": CODEX_IMAGE_MODEL, | |
| "input": [ | |
| { | |
| "role": "user", | |
| "content": [ | |
| {"type": "input_text", "text": "我想做一张南京城市宣传海报图。"}, | |
| ], | |
| } | |
| ], | |
| "tools": [{"type": "image_generation"}], | |
| }, | |
| timeout=300, | |
| ) | |
| self.assertEqual(response.status_code, 200, response.text) | |
| saved_paths = [] | |
| try: | |
| payload = response.json() | |
| except Exception: | |
| payload = {} | |
| for index, item in enumerate(payload.get("output") or [], start=1): | |
| if not isinstance(item, dict): | |
| continue | |
| image_b64 = str(item.get("result") or "") | |
| if image_b64: | |
| saved_paths.append(save_image(image_b64, f"responses_codex_image_non_stream_{index}")) | |
| print("responses codex image non-stream status:") | |
| print(response.status_code) | |
| print("responses codex image non-stream result:") | |
| print(json.dumps(payload, ensure_ascii=False, indent=2)) | |
| print("responses codex image non-stream saved files:") | |
| for path in saved_paths: | |
| print(path) | |
| def test_codex_image_response_stream_http(self): | |
| """测试 Responses 的 codex 画图流式 HTTP 调用。""" | |
| response = requests.post( | |
| f"{BASE_URL}/v1/responses", | |
| headers={"Authorization": f"Bearer {AUTH_KEY}"}, | |
| json={ | |
| "model": CODEX_IMAGE_MODEL, | |
| "input": [ | |
| { | |
| "role": "user", | |
| "content": [ | |
| {"type": "input_text", "text": "我想做一张南京城市宣传海报图。"}, | |
| ], | |
| } | |
| ], | |
| "tools": [{"type": "image_generation"}], | |
| "stream": True, | |
| }, | |
| stream=True, | |
| timeout=300, | |
| ) | |
| self.assertEqual(response.status_code, 200, response.text) | |
| self.assertTrue( | |
| response.headers.get("content-type", "").startswith("text/event-stream"), | |
| response.headers.get("content-type", ""), | |
| ) | |
| started_at = time.time() | |
| saved_paths = [] | |
| print("responses codex image stream status:") | |
| print(response.status_code) | |
| print("responses codex image stream chunks:") | |
| for text in self._iter_sse_payloads(response): | |
| print(f"{time.time() - started_at:6.2f}s {text}") | |
| if not text.startswith("data:"): | |
| continue | |
| payload_text = text[5:].strip() | |
| if payload_text == "[DONE]": | |
| break | |
| try: | |
| payload = json.loads(payload_text) | |
| except Exception: | |
| continue | |
| if payload.get("type") != "response.output_item.done": | |
| continue | |
| item = payload.get("item") or {} | |
| if str(item.get("type") or "") != "image_generation_call": | |
| continue | |
| image_b64 = str(item.get("result") or "") | |
| if image_b64: | |
| saved_paths.append(save_image(image_b64, f"responses_codex_image_stream_{len(saved_paths) + 1}")) | |
| print("responses codex image stream saved files:") | |
| for path in saved_paths: | |
| print(path) | |
| if __name__ == "__main__": | |
| unittest.main() | |