diff --git a/apps/api/app/services/smart_asset_selector.py b/apps/api/app/services/smart_asset_selector.py index 8e781f508..7878b1bd5 100755 --- a/apps/api/app/services/smart_asset_selector.py +++ b/apps/api/app/services/smart_asset_selector.py @@ -18,7 +18,6 @@ from __future__ import annotations import logging -import math from dataclasses import dataclass logger = logging.getLogger(__name__) @@ -278,9 +277,7 @@ class SmartAssetSelector: # 分桶 short_bucket = [d for d in scored if d.duration is not None and d.duration < _SHORT_BUCKET_MAX] medium_bucket = [ - d - for d in scored - if d.duration is not None and _SHORT_BUCKET_MAX <= d.duration < _MEDIUM_BUCKET_MAX + d for d in scored if d.duration is not None and _SHORT_BUCKET_MAX <= d.duration < _MEDIUM_BUCKET_MAX ] long_bucket = [d for d in scored if d.duration is not None and d.duration >= _MEDIUM_BUCKET_MAX] unknown_bucket = [d for d in scored if d.duration is None] @@ -295,7 +292,7 @@ class SmartAssetSelector: selected_ids: set[str] = set() # 先按配额从每个桶取 - for bucket, name in zip(buckets, bucket_names): + for bucket, _name in zip(buckets, bucket_names, strict=False): quota = min(base_quota, len(bucket)) if quota <= 0: continue diff --git a/tests/unit/test_ai_service.py b/tests/unit/test_ai_service.py index f83e408ae..4049f9687 100755 --- a/tests/unit/test_ai_service.py +++ b/tests/unit/test_ai_service.py @@ -20,8 +20,8 @@ sys.path.insert(0, "apps/api") from app.services.ai_service import ( # noqa: E402 TITLE_STYLES, _generate_titles_fallback, - _parse_titles_from_response, _parse_semantic_match_response, + _parse_titles_from_response, _semantic_match_fallback, generate_smart_titles, semantic_match_assets, @@ -76,7 +76,7 @@ class TestTitleParsing(unittest.TestCase): def test_parse_markdown_code_block_json(self): """解析 markdown 代码块包裹的 JSON.""" - content = "```json\n[\"标题1\", \"标题2\"]\n```" + content = '```json\n["标题1", "标题2"]\n```' result = _parse_titles_from_response(content) self.assertEqual(len(result), 2) @@ -209,9 +209,7 @@ class TestGenerateSmartTitles(unittest.TestCase): mock_client = MagicMock() mock_client.is_available = True # 返回无法解析的内容(只有一个标题且格式异常) - mock_client.chat_completion = MagicMock( - return_value="一段文字说明,不是标题列表" - ) + mock_client.chat_completion = MagicMock(return_value="一段文字说明,不是标题列表") with patch("app.services.ai_service.get_doubao_client", return_value=mock_client): result = generate_smart_titles("测试视频", "viral", 5) # 只有1个有效标题,不足2个触发降级 @@ -223,7 +221,7 @@ class TestTitleStyles(unittest.TestCase): def test_all_styles_have_required_fields(self): """所有风格都有必要字段.""" - for key, info in TITLE_STYLES.items(): + for _key, info in TITLE_STYLES.items(): self.assertIn("name", info) self.assertIn("description", info) self.assertIn("examples", info) @@ -310,12 +308,14 @@ class TestSemanticMatchParsing(unittest.TestCase): def test_parse_matches_list_format(self): """解析 {matches: [...]} 格式.""" - content = json.dumps({ - "matches": [ - {"asset_id": "a1", "score": 0.9}, - {"asset_id": "a2", "score": 0.7}, - ] - }) + content = json.dumps( + { + "matches": [ + {"asset_id": "a1", "score": 0.9}, + {"asset_id": "a2", "score": 0.7}, + ] + } + ) result = _parse_semantic_match_response(content, ["a1", "a2"]) self.assertIsNotNone(result) self.assertAlmostEqual(result["a1"], 0.9) @@ -323,10 +323,12 @@ class TestSemanticMatchParsing(unittest.TestCase): def test_parse_array_format(self): """解析数组格式.""" - content = json.dumps([ - {"id": "x1", "score": 0.5}, - {"id": "x2", "score": 0.88}, - ]) + content = json.dumps( + [ + {"id": "x1", "score": 0.5}, + {"id": "x2", "score": 0.88}, + ] + ) result = _parse_semantic_match_response(content, ["x1", "x2"]) self.assertIsNotNone(result) self.assertAlmostEqual(result["x1"], 0.5) @@ -341,7 +343,7 @@ class TestSemanticMatchParsing(unittest.TestCase): def test_parse_markdown_code_block(self): """解析markdown代码块.""" - content = "```json\n{\"a1\": 0.7}\n```" + content = '```json\n{"a1": 0.7}\n```' result = _parse_semantic_match_response(content, ["a1", "a2"]) # 只有1个素材评分,少于一半(需要至少1个,max(1, 2//2)=1) self.assertIsNotNone(result) @@ -396,9 +398,7 @@ class TestSemanticMatchAssets(unittest.TestCase): """豆包调用成功路径.""" mock_client = MagicMock() mock_client.is_available = True - mock_client.chat_completion = MagicMock( - return_value=json.dumps({"a1": 0.9, "a2": 0.5, "a3": 0.2}) - ) + mock_client.chat_completion = MagicMock(return_value=json.dumps({"a1": 0.9, "a2": 0.5, "a3": 0.2})) with patch("app.services.ai_service.get_doubao_client", return_value=mock_client): result = semantic_match_assets("风景视频", self._make_assets()) self.assertEqual(result["source"], "doubao")