180 lines
6.1 KiB
Python
180 lines
6.1 KiB
Python
import re
|
|
import unittest
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
|
|
from qwen_api_models import (
|
|
QWEN_API_MODEL_IDS,
|
|
QWEN_API_MODELS,
|
|
QWEN_API_MULTIMODAL_ENDPOINT_MODEL_IDS,
|
|
QWEN_API_RETIRED_MODEL_IDS,
|
|
QWEN_API_VISION_MODEL_IDS,
|
|
normalize_api_model_name,
|
|
)
|
|
from qwen_api_utils import call_qwen_api, extract_qwen_api_response_text
|
|
|
|
|
|
class FakeEndpoint:
|
|
def __init__(self):
|
|
self.calls = []
|
|
|
|
def call(self, **kwargs):
|
|
self.calls.append(kwargs)
|
|
return SimpleNamespace(status_code=200)
|
|
|
|
|
|
class QwenAPIModelTests(unittest.TestCase):
|
|
def test_new_models_are_registered_with_expected_capabilities(self):
|
|
self.assertIn("qwen3.8-max", QWEN_API_MODEL_IDS)
|
|
self.assertIn("qwen3.8-2.4t-a95b", QWEN_API_MODEL_IDS)
|
|
self.assertIn("qwen3.7-flash", QWEN_API_MODEL_IDS)
|
|
self.assertIn("qwen3.7-flash-2026-07-15", QWEN_API_MODEL_IDS)
|
|
self.assertIn("qwen3.7-max-2026-06-08", QWEN_API_MODEL_IDS)
|
|
|
|
self.assertIn("qwen3.8-max", QWEN_API_VISION_MODEL_IDS)
|
|
self.assertNotIn("qwen3.8-2.4t-a95b", QWEN_API_VISION_MODEL_IDS)
|
|
self.assertIn("qwen3.7-max-2026-06-08", QWEN_API_VISION_MODEL_IDS)
|
|
self.assertNotIn("qwen3.7-max", QWEN_API_VISION_MODEL_IDS)
|
|
self.assertNotIn("qwen3.7-max-2026-05-20", QWEN_API_VISION_MODEL_IDS)
|
|
|
|
def test_retired_models_are_not_selectable(self):
|
|
self.assertTrue(QWEN_API_RETIRED_MODEL_IDS.isdisjoint(QWEN_API_MODEL_IDS))
|
|
|
|
def test_current_retirement_labels_are_normalized(self):
|
|
labels = [
|
|
label for label in QWEN_API_MODELS if label.startswith("qwen-vl-max ")
|
|
]
|
|
self.assertEqual(len(labels), 1)
|
|
self.assertIn("offline scheduled 2026-10-10", labels[0])
|
|
self.assertIn("use qwen3.7-plus", labels[0])
|
|
self.assertEqual(normalize_api_model_name(labels[0]), "qwen-vl-max")
|
|
|
|
self.assertIn("qwen-plus", QWEN_API_MODELS)
|
|
self.assertIn("qwen-max", QWEN_API_MODELS)
|
|
|
|
def test_qwen38_uses_multimodal_endpoint_for_text(self):
|
|
generation = FakeEndpoint()
|
|
multimodal = FakeEndpoint()
|
|
dashscope = SimpleNamespace(
|
|
Generation=generation,
|
|
MultiModalConversation=multimodal,
|
|
)
|
|
|
|
call_qwen_api(
|
|
dashscope,
|
|
api_key="test-key",
|
|
model="qwen3.8-max",
|
|
messages=[{"role": "user", "content": "hello"}],
|
|
)
|
|
|
|
self.assertEqual(generation.calls, [])
|
|
self.assertEqual(len(multimodal.calls), 1)
|
|
self.assertEqual(
|
|
multimodal.calls[0]["messages"][0]["content"],
|
|
[{"text": "hello"}],
|
|
)
|
|
self.assertFalse(multimodal.calls[0]["enable_thinking"])
|
|
|
|
def test_qwen38_multimodal_forwards_json_response_format(self):
|
|
generation = FakeEndpoint()
|
|
multimodal = FakeEndpoint()
|
|
dashscope = SimpleNamespace(
|
|
Generation=generation,
|
|
MultiModalConversation=multimodal,
|
|
)
|
|
response_format = {"type": "json_object"}
|
|
|
|
call_qwen_api(
|
|
dashscope,
|
|
api_key="test-key",
|
|
model="qwen3.8-max",
|
|
messages=[
|
|
{"role": "user", "content": "Return the result as JSON."}
|
|
],
|
|
response_format=response_format,
|
|
force_multimodal=True,
|
|
)
|
|
|
|
self.assertEqual(generation.calls, [])
|
|
self.assertEqual(
|
|
multimodal.calls[0]["response_format"], response_format
|
|
)
|
|
self.assertFalse(multimodal.calls[0]["enable_thinking"])
|
|
|
|
def test_qwen38_open_model_uses_generation_endpoint(self):
|
|
generation = FakeEndpoint()
|
|
multimodal = FakeEndpoint()
|
|
dashscope = SimpleNamespace(
|
|
Generation=generation,
|
|
MultiModalConversation=multimodal,
|
|
)
|
|
|
|
call_qwen_api(
|
|
dashscope,
|
|
api_key="test-key",
|
|
model="qwen3.8-2.4t-a95b",
|
|
messages=[{"role": "user", "content": "hello"}],
|
|
)
|
|
|
|
self.assertEqual(len(generation.calls), 1)
|
|
self.assertEqual(multimodal.calls, [])
|
|
self.assertFalse(generation.calls[0]["enable_thinking"])
|
|
|
|
def test_vision_models_derive_multimodal_endpoint_membership(self):
|
|
self.assertTrue(
|
|
QWEN_API_VISION_MODEL_IDS.issubset(
|
|
QWEN_API_MULTIMODAL_ENDPOINT_MODEL_IDS
|
|
)
|
|
)
|
|
|
|
def test_frontend_vision_filter_matches_api_model_registry(self):
|
|
script_path = (
|
|
Path(__file__).resolve().parents[1] / "web" / "mmproj_filter.js"
|
|
)
|
|
script = script_path.read_text(encoding="utf-8")
|
|
match = re.search(
|
|
r"const VISION_API_MODEL_IDS = new Set\(\[(.*?)\]\);",
|
|
script,
|
|
flags=re.DOTALL,
|
|
)
|
|
|
|
self.assertIsNotNone(match)
|
|
frontend_model_ids = set(re.findall(r'"([^"]+)"', match.group(1)))
|
|
self.assertEqual(frontend_model_ids, QWEN_API_VISION_MODEL_IDS)
|
|
|
|
def test_extracts_text_from_supported_response_shapes(self):
|
|
responses = [
|
|
SimpleNamespace(
|
|
output=SimpleNamespace(
|
|
choices=[
|
|
SimpleNamespace(
|
|
message=SimpleNamespace(content="plain text")
|
|
)
|
|
]
|
|
)
|
|
),
|
|
{
|
|
"output": {
|
|
"choices": [
|
|
{
|
|
"message": {
|
|
"content": [
|
|
{"text": "first "},
|
|
{"text": "second"},
|
|
]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
},
|
|
SimpleNamespace(output=SimpleNamespace(text="output text")),
|
|
]
|
|
|
|
self.assertEqual(extract_qwen_api_response_text(responses[0]), "plain text")
|
|
self.assertEqual(extract_qwen_api_response_text(responses[1]), "first second")
|
|
self.assertEqual(extract_qwen_api_response_text(responses[2]), "output text")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|