Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions packages/markitdown/src/markitdown/_markitdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,14 @@ def enable_builtins(self, **kwargs) -> None:
if docintel_version is not None:
docintel_args["api_version"] = docintel_version

docintel_model_id = kwargs.get("docintel_model_id")
if docintel_model_id is not None:
docintel_args["model_id"] = docintel_model_id

docintel_features = kwargs.get("docintel_features")
if docintel_features is not None:
docintel_args["features"] = docintel_features

self.register_converter(
DocumentIntelligenceConverter(**docintel_args),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ def __init__(
DocumentIntelligenceFileType.BMP,
DocumentIntelligenceFileType.TIFF,
],
model_id: str = "prebuilt-layout",
features: List[str] | None = None,
):
"""
Initialize the DocumentIntelligenceConverter.
Expand All @@ -155,10 +157,18 @@ def __init__(
api_version (str): The API version to use. Defaults to "2024-07-31-preview".
credential (AzureKeyCredential | TokenCredential | None): The credential to use for authentication.
file_types (List[DocumentIntelligenceFileType]): The file types to accept. Defaults to all supported file types.
model_id (str): The Document Intelligence model to analyze with. Defaults to "prebuilt-layout".
Pass e.g. "prebuilt-read" for cheaper plain-text extraction.
features (List[str] | None): The analysis add-on features to request for OCR-eligible
file types. Defaults to None, which keeps the built-in set (formulas, high
resolution OCR, and font style). Pass an empty list to disable all add-ons.
Add-ons are never sent for office file types, which do not support them.
"""

super().__init__()
self._file_types = file_types
self._features = features
self.model_id = model_id

# Raise an error if the dependencies are not available.
# This is different than other converters since this one isn't even instantiated
Expand Down Expand Up @@ -228,6 +238,9 @@ def _analysis_features(self, stream_info: StreamInfo) -> List[str]:
if mimetype.startswith(prefix):
return []

if self._features is not None:
return list(self._features)

return [
DocumentAnalysisFeature.FORMULAS, # enable formula extraction
DocumentAnalysisFeature.OCR_HIGH_RESOLUTION, # enable high resolution OCR
Expand All @@ -242,7 +255,7 @@ def convert(
) -> DocumentConverterResult:
# Extract the text using Azure Document Intelligence
poller = self.doc_intel_client.begin_analyze_document(
model_id="prebuilt-layout",
model_id=self.model_id,
body=AnalyzeDocumentRequest(bytes_source=file_stream.read()),
features=self._analysis_features(stream_info),
output_content_format=CONTENT_FORMAT, # TODO: replace with "ContentFormat.MARKDOWN" when the bug is fixed
Expand Down
74 changes: 74 additions & 0 deletions packages/markitdown/tests/test_docintel_options.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import io

from markitdown._stream_info import StreamInfo
from markitdown.converters._doc_intel_converter import (
DocumentIntelligenceConverter,
DocumentIntelligenceFileType,
)

ALL_TYPES = list(DocumentIntelligenceFileType)


def _make_converter(features=None, model_id="prebuilt-layout"):
"""Build a converter without touching Azure (mirrors test_docintel_html.py)."""
conv = DocumentIntelligenceConverter.__new__(DocumentIntelligenceConverter)
conv._file_types = ALL_TYPES
conv._features = features
conv.model_id = model_id
return conv


PDF = StreamInfo(mimetype="application/pdf", extension=".pdf")
DOCX = StreamInfo(mimetype=None, extension=".docx")


def _values(features):
"""Azure's feature enum is a str-enum; compare on the wire values."""
return [getattr(f, "value", f) for f in features]


def test_default_features_unchanged():
conv = _make_converter()
assert _values(conv._analysis_features(PDF)) == [
"formulas",
"ocrHighResolution",
"styleFont",
]


def test_default_model_id_unchanged():
assert _make_converter().model_id == "prebuilt-layout"


def test_empty_features_disables_addons():
conv = _make_converter(features=[])
assert conv._analysis_features(PDF) == []


def test_explicit_features_are_used():
conv = _make_converter(features=["ocrHighResolution"])
assert _values(conv._analysis_features(PDF)) == ["ocrHighResolution"]


def test_explicit_features_not_sent_for_office_types():
"""Office file types do not support add-ons, so they stay empty."""
conv = _make_converter(features=["ocrHighResolution"])
assert conv._analysis_features(DOCX) == []


def test_explicit_features_list_is_copied():
"""The caller's list must not be aliased into the request."""
requested = ["ocrHighResolution"]
conv = _make_converter(features=requested)
returned = conv._analysis_features(PDF)
returned.append("styleFont")
assert requested == ["ocrHighResolution"]


def test_custom_model_id_is_stored():
assert _make_converter(model_id="prebuilt-read").model_id == "prebuilt-read"


def test_accepts_still_works():
"""Sanity: the new attributes do not disturb accepts()."""
assert _make_converter().accepts(io.BytesIO(b""), PDF)