diff --git a/src/skillspector/llm_analyzer_base.py b/src/skillspector/llm_analyzer_base.py index c5ab9dce..4835c97b 100644 --- a/src/skillspector/llm_analyzer_base.py +++ b/src/skillspector/llm_analyzer_base.py @@ -46,6 +46,38 @@ CHARS_PER_TOKEN = 4 CHUNK_OVERLAP_LINES = 50 +_MEDIA_FILE_EXTENSIONS = ( + ".png", + ".jpg", + ".jpeg", + ".gif", + ".bmp", + ".ico", + ".webp", + ".avif", + ".heic", + ".heif", + ".tif", + ".tiff", + ".mp3", + ".aac", + ".flac", + ".m4a", + ".ogg", + ".opus", + ".wav", + ".mp4", + ".m4v", + ".avi", + ".mov", + ".webm", + ".mkv", + ".mpeg", + ".mpg", + ".ogv", + ".3gp", +) + # --------------------------------------------------------------------------- # Default structured-output schemas (discovery mode) @@ -303,6 +335,9 @@ def get_batches( batches: list[Batch] = [] for path in file_paths: + if path.lower().endswith(_MEDIA_FILE_EXTENSIONS): + logger.info("Skipping media file from LLM analysis: %s", path) + continue content = file_cache.get(path) or "No content available for this file." file_findings = findings_by_file.get(path, []) diff --git a/tests/nodes/test_llm_analyzer_base.py b/tests/nodes/test_llm_analyzer_base.py index e344e654..6b928c64 100644 --- a/tests/nodes/test_llm_analyzer_base.py +++ b/tests/nodes/test_llm_analyzer_base.py @@ -203,6 +203,44 @@ def test_zero_padding(self) -> None: assert "L11: line10" in result +# --------------------------------------------------------------------------- +# LLMAnalyzerBase.get_batches +# --------------------------------------------------------------------------- + + +class TestLLMAnalyzerBaseGetBatches: + MODEL = "nvidia/openai/gpt-oss-120b" + + @pytest.mark.parametrize( + "path", + [ + "assets/demo.gif", + "assets/screenshot.PNG", + "assets/tutorial.mp4", + "assets/voice.mp3", + "assets/photo.webp", + "assets/movie.mkv", + ], + ) + @patch(MOCK_PATCH_TARGET, _mock_get_chat_model) + def test_media_files_are_skipped(self, path: str) -> None: + analyzer = LLMAnalyzerBase(base_prompt="test", model=self.MODEL) + + assert analyzer.get_batches([path], {path: "decoded media data"}) == [] + + @patch(MOCK_PATCH_TARGET, _mock_get_chat_model) + def test_text_files_and_svg_are_preserved(self) -> None: + analyzer = LLMAnalyzerBase(base_prompt="test", model=self.MODEL) + file_cache = { + "src/main.py": "print('hello')\n", + "assets/icon.svg": '', + } + + batches = analyzer.get_batches(list(file_cache), file_cache) + + assert {batch.file_path for batch in batches} == set(file_cache) + + # --------------------------------------------------------------------------- # LLMAnalyzerBase.build_prompt (default implementation) # ---------------------------------------------------------------------------