Initial project import
This commit is contained in:
@@ -0,0 +1,294 @@
|
||||
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):
|
||||
@staticmethod
|
||||
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()
|
||||
Reference in New Issue
Block a user