Files
cmhub/apps/ai/tests.py
T
2026-07-02 10:33:15 +08:00

178 lines
6.1 KiB
Python

import base64
from django.test import SimpleTestCase
from apps.ai.providers import AiCapabilityError, ResolvedModel, get_provider, resolve_api_type
from apps.ai.providers.openai_compatible import ChatCompletionsProvider, ImagesEditsProvider
class FakeResponse:
def __init__(self, payload, content=b""):
self.payload = payload
self.content = content
def json(self):
return self.payload
def raise_for_status(self):
return None
class FakeSession:
def __init__(self, *responses):
self.responses = list(responses)
self.posts = []
self.gets = []
self.trust_env = True
def post(self, url, **kwargs):
self.posts.append({"url": url, **kwargs})
return self.responses.pop(0)
def get(self, url, **kwargs):
self.gets.append({"url": url, **kwargs})
return self.responses.pop(0)
class ProviderRegistryTests(SimpleTestCase):
def test_auto_api_type_resolves_chat_provider_from_url(self):
url = "https://api.vectorengine.ai/v1/chat/completions"
self.assertEqual(resolve_api_type("auto", url), "chat")
self.assertIsInstance(get_provider("auto", url), ChatCompletionsProvider)
class ChatCompletionsProviderTests(SimpleTestCase):
def test_generate_text_builds_chat_payload_and_cleans_titles(self):
session = FakeSession(
FakeResponse(
{
"choices": [
{"message": {"content": "1. Red Dress\n2. Blue Coat"}}
]
}
)
)
provider = ChatCompletionsProvider(session=session)
model = ResolvedModel(
name="GPT-5.5 text",
url="https://api.vectorengine.ai/v1",
model="gpt-5.5",
api_key="test-key",
api_type="chat",
)
result = provider.generate_text(
"Generate titles",
model,
parameters={"temperature": 0.2},
)
self.assertEqual(result.text, "Red Dress")
self.assertEqual(result.titles, ("Red Dress", "Blue Coat"))
request = session.posts[0]
self.assertEqual(
request["url"],
"https://api.vectorengine.ai/v1/chat/completions",
)
self.assertEqual(request["headers"]["Authorization"], "Bearer test-key")
self.assertEqual(request["json"]["model"], "gpt-5.5")
self.assertFalse(request["json"]["stream"])
self.assertEqual(request["json"]["temperature"], 0.2)
def test_generate_image_parses_chat_multimodal_data_url(self):
generated = b"generated-image"
encoded = base64.b64encode(generated).decode("ascii")
session = FakeSession(
FakeResponse(
{
"choices": [
{
"message": {
"content": [
{"type": "text", "text": "done"},
{
"type": "image_url",
"image_url": {
"url": f"data:image/png;base64,{encoded}"
},
},
]
}
}
]
}
)
)
provider = ChatCompletionsProvider(session=session)
model = ResolvedModel(
name="Nano Banana 2",
url="https://api.vectorengine.ai/v1/chat/completions",
model="gemini-3.1-flash-image-preview",
api_key="test-key",
api_type="auto",
)
result = provider.generate_image(
"Generate product image",
model,
image=b"input-image",
image_mime_type="image/jpeg",
)
self.assertEqual(result.image, generated)
content = session.posts[0]["json"]["messages"][0]["content"]
self.assertEqual(content[0], {"type": "text", "text": "Generate product image"})
self.assertTrue(content[1]["image_url"]["url"].startswith("data:image/jpeg;base64,"))
class ImagesEditsProviderTests(SimpleTestCase):
def test_generate_image_builds_multipart_request_and_parses_base64(self):
generated = b"edited-image"
encoded = base64.b64encode(generated).decode("ascii")
session = FakeSession(FakeResponse({"data": [{"b64_json": encoded}]}))
provider = ImagesEditsProvider(session=session)
model = ResolvedModel(
name="GPT Image 2",
url="https://api.vectorengine.ai/v1/images/edits",
model="gpt-image-2",
api_key="test-key",
api_type="images_edits",
)
result = provider.generate_image(
"Replace background",
model,
image=b"source-image",
image_mime_type="image/png",
image_filename="source.png",
resolution="1K",
)
self.assertEqual(result.image, generated)
request = session.posts[0]
self.assertEqual(request["url"], "https://api.vectorengine.ai/v1/images/edits")
self.assertEqual(request["headers"]["Authorization"], "Bearer test-key")
self.assertEqual(request["data"]["model"], "gpt-image-2")
self.assertEqual(request["data"]["size"], "1024x1024")
self.assertEqual(
request["files"]["image"],
("source.png", b"source-image", "image/png"),
)
def test_images_edits_requires_input_image(self):
provider = ImagesEditsProvider(session=FakeSession())
model = ResolvedModel(
name="GPT Image 2",
url="https://api.vectorengine.ai/v1/images/edits",
model="gpt-image-2",
api_key="test-key",
api_type="images_edits",
)
with self.assertRaises(AiCapabilityError):
provider.generate_image("Replace background", model)
with self.assertRaises(AiCapabilityError):
provider.generate_text("Generate title", model)