diff --git a/providers/common/ai/docs/operators/llm_file_analysis.rst b/providers/common/ai/docs/operators/llm_file_analysis.rst index 8b2733ab05acb..c60435aa90619 100644 --- a/providers/common/ai/docs/operators/llm_file_analysis.rst +++ b/providers/common/ai/docs/operators/llm_file_analysis.rst @@ -153,10 +153,10 @@ This operator also inherits ``LLMOperator``'s HITL review parameters -- Supported Formats ----------------- -- Text-like: ``.log``, ``.json``, ``.csv``, ``.parquet``, ``.avro`` +- Text-like: ``.log``, ``.txt``, ``.md``, ``.json``, ``.csv``, ``.parquet``, ``.avro`` - Multimodal: ``.png``, ``.jpg``, ``.jpeg``, ``.pdf`` when ``multi_modal=True`` - Gzip-compressed text inputs are supported for ``.log.gz``, ``.json.gz``, and - ``.csv.gz``. + ``.csv.gz``, ``.txt.gz``, and ``.md.gz``. - Gzip is not supported for ``.parquet``, ``.avro``, image, or PDF inputs. Parquet and Avro readers require their corresponding optional extras: diff --git a/providers/common/ai/src/airflow/providers/common/ai/utils/file_analysis.py b/providers/common/ai/src/airflow/providers/common/ai/utils/file_analysis.py index 38c3d6f149293..6f6366b515e68 100644 --- a/providers/common/ai/src/airflow/providers/common/ai/utils/file_analysis.py +++ b/providers/common/ai/src/airflow/providers/common/ai/utils/file_analysis.py @@ -49,13 +49,14 @@ "jpg", "json", "log", + "md", "parquet", "pdf", "png", "txt", ) -_TEXT_LIKE_FORMATS = frozenset({"csv", "json", "log", "avro", "parquet", "txt"}) +_TEXT_LIKE_FORMATS = frozenset({"csv", "json", "log", "avro", "parquet", "txt", "md"}) _MULTI_MODAL_FORMATS = frozenset({"jpeg", "jpg", "pdf", "png"}) _COMPRESSION_SUFFIXES = { "bz2": "bzip2", @@ -64,7 +65,7 @@ "xz": "xz", "zst": "zstd", } -_GZIP_SUPPORTED_FORMATS = frozenset({"csv", "json", "log", "txt"}) +_GZIP_SUPPORTED_FORMATS = frozenset({"csv", "json", "log", "txt", "md"}) _TEXT_SAMPLE_HEAD_CHARS = 8_000 _TEXT_SAMPLE_TAIL_CHARS = 2_000 _MEDIA_TYPES = { diff --git a/providers/common/ai/tests/unit/common/ai/utils/test_file_analysis.py b/providers/common/ai/tests/unit/common/ai/utils/test_file_analysis.py index 2b524998b60e1..bba7f56111a73 100644 --- a/providers/common/ai/tests/unit/common/ai/utils/test_file_analysis.py +++ b/providers/common/ai/tests/unit/common/ai/utils/test_file_analysis.py @@ -90,6 +90,28 @@ def test_txt_file_analysis(self, tmp_path): assert "first line" in request.user_content assert "format=txt" in request.user_content + def test_markdown_file_analysis(self, tmp_path): + path = tmp_path / "notes.md" + path.write_text("# Notes\n\nFirst finding.\n", encoding="utf-8") + + request = build_file_analysis_request( + file_path=str(path), + file_conn_id=None, + prompt="Summarize the notes", + multi_modal=False, + max_files=20, + max_file_size_bytes=1024, + max_total_size_bytes=2048, + max_text_chars=500, + sample_rows=10, + ) + + assert isinstance(request, FileAnalysisRequest) + assert request.resolved_paths == [str(path)] + assert "Summarize the notes" in request.user_content + assert "First finding" in request.user_content + assert "format=md" in request.user_content + def test_file_conn_id_overrides_embedded_connection(self, tmp_path): path = tmp_path / "data.json" path.write_text('{"status": "ok"}', encoding="utf-8") @@ -360,6 +382,8 @@ class TestFileAnalysisHelpers: ("app", "log", None), ("notes.txt", "txt", None), ("notes.txt.gz", "txt", "gzip"), + ("notes.md", "md", None), + ("notes.md.gz", "md", "gzip"), ], ) def test_detect_file_format(self, tmp_path, filename, expected_format, expected_compression):