feat: dual LLM mode (default via .env, custom per-request) for AI analysis

This commit is contained in:
hyh
2026-09-04 12:06:05 +08:00
parent 1a935cae6e
commit 4a4c6f2ec2
10 changed files with 154 additions and 63 deletions
+36 -11
View File
@@ -49,17 +49,15 @@ class EndToEndTests(APITestCase):
def test_create_requirement_analyzes_spec(self):
self._login()
with mock.patch("requirements.llm.requests.post") as post:
post.return_value = mock.Mock(
json=lambda: {"choices": [{"message": {"content": (
'{"ai_inspection": true, '
'"stability": {"enabled": true, "requirements": "基准并发20错误率≤1%"}, '
'"functional": {"enabled": true, "points": ["首页可访问", "返回 ok"]}, '
'"performance": {"enabled": false, "requirements": ""}, '
'"security": {"enabled": false, "requirements": ""}}'
)}}]},
raise_for_status=lambda: None,
)
llm_json = (
'{"ai_inspection": true, '
'"stability": {"enabled": true, "requirements": "基准并发20错误率≤1%"}, '
'"functional": {"enabled": true, "points": ["首页可访问", "返回 ok"]}, '
'"performance": {"enabled": false, "requirements": ""}, '
'"security": {"enabled": false, "requirements": ""}}'
)
with mock.patch("requirements.llm.resolve_llm", return_value=("http://llm.test/v1", "k", "m")), \
mock.patch("requirements.llm._chat", return_value=llm_json):
resp = self.client.post("/api/requirements/create/", {
"title": "作业1-分析",
"content": "实现一个 web 服务, 首页返回 ok, 要求稳定度达到 99%",
@@ -69,6 +67,33 @@ class EndToEndTests(APITestCase):
self.assertTrue(requirement.spec["stability"]["enabled"])
self.assertIn("首页可访问", requirement.spec["functional"]["points"])
def test_create_requirement_custom_llm(self):
self._login()
override = {"mode": "custom", "base_url": "http://custom.test/v1",
"api_key": "ck", "model": "custom-model"}
llm_json = '{"ai_inspection": true, "functional": {"enabled": true, "points": ["自定义功能点"]}}'
captured = {}
with mock.patch("requirements.llm.resolve_llm", side_effect=lambda ov: captured.update(ov) or ("http://custom.test/v1", "ck", "custom-model")), \
mock.patch("requirements.llm._chat", return_value=llm_json):
resp = self.client.post("/api/requirements/create/", {
"title": "custom-llm-作业",
"content": "任意",
"llm": override,
}, format="json")
self.assertEqual(resp.status_code, 201, resp.content)
self.assertEqual(captured["mode"], "custom")
requirement = Requirement.objects.get(title="custom-llm-作业")
self.assertEqual(requirement.llm_config["mode"], "custom")
self.assertIn("自定义功能点", requirement.spec["functional"]["points"])
def test_custom_llm_requires_fields(self):
self._login()
resp = self.client.post("/api/requirements/create/", {
"title": "缺字段", "content": "任意",
"llm": {"mode": "custom", "base_url": "", "model": ""},
}, format="json")
self.assertEqual(resp.status_code, 400)
def test_submission_full_pipeline(self):
self._login()
requirement = Requirement.objects.create(