Repository navigation
Use Azure AI Content Understanding for Enhanced document extraction - #1278
Merged
Paul Lizer (paullizer) merged 4 commits intoAug 18, 2026
Merged
Paul Lizer (paullizer) merged 4 commits into
Paul Lizer (paullizer) merged 4 commits into
Conversation
Enhanced extraction for PDFs and images now uses Azure AI Content Understanding (prebuilt-documentSearch) instead of Document Intelligence Layout, because it also returns AI-generated descriptions of figures, charts, and diagrams. Standard extraction is unchanged and still uses Document Intelligence prebuilt-read. Enhanced never becomes a hard dependency. Content Understanding is not offered in Azure Government, so resolve_enhanced_extraction_engine falls back to Document Intelligence Layout when the cloud is unsupported, when Content Understanding is unconfigured, or when a request fails. The reason is recorded on the document and surfaced in workspace tooltips. A new enable_enhanced_extraction toggle gates the feature and defaults the mode selector to Auto. The admin UI is environment-aware: Azure Public shows the Content Understanding configuration with a test button and setup guide, while Government and custom clouds show that Enhanced uses Document Intelligence Layout and needs no configuration. Existing deployments already set to Enhanced or Auto are migrated on first settings read so they do not silently downgrade to Standard. Auto still samples first pages with Document Intelligence Layout as the cheaper detector, now also upgrading when it finds figures or images. Neither engine describes figures inside Office files, so embedded images are extracted from DOCX and PPTX packages and analyzed with whichever engine backs the selected mode, then indexed as their own citable chunks. Cost is bounded by a minimum size filter, content-hash de-duplication, and a per-document cap. Uploaded Office files are treated as untrusted: output names are generated rather than reused from archive entries, entry sizes are checked before decompression, and slide relationship parts are parsed with defusedxml. The Change Extraction action now covers images as well as PDFs. Existing setting and document field names are unchanged, so no Cosmos or search index migration is required. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Follow-up to 6d9c04b, addressing findings from a re-review of the fixes that the original code review never saw, plus the one regression the full-suite sweep turned up. Bounded zip reads. The declared-size check added earlier is not a sound zip-bomb defense: ZipInfo.file_size comes from the archive header and can be forged, and CPython decompresses a chunk before truncating it to that size. A 64 KB entry declaring 4096 bytes peaked at 141 MiB through archive.read(); streaming the same entry in bounded chunks peaks at 0.29 MiB. Entries are now read through archive.open() with a hard byte ceiling, the declared size is kept only as a cheap pre-filter for honest archives, compression methods are restricted to those real OOXML packages use, and the number of archive entries and relationship parts inspected is capped. Figure rendering. The figure label and its content shared a line, so a diagram kept after its description was de-duplicated produced an opening code fence mid-line, which markdown renderers treat as paragraph text. The label now sits on its own line. Figures are also no longer dropped when they carry a caption but no description, which the API schema allows, Chart.js content is emitted as a JSON block, and the no-pages fallback renders figures instead of returning early. Legacy Word test. test_legacy_doc_ole_extraction.py loads functions_content.py through importlib with a hand-curated set of module stubs, so the new functions_office_media import broke it. Since that module only needs stdlib, PIL, and defusedxml, the test now puts the app directory on sys.path and imports the real module rather than stubbing it. Verified the full 857-file functional suite against a baseline worktree at the pre-change commit, diffing per test so pre-existing failures are excluded. The legacy Word test was the only regression and is fixed here. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Refs #1277 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Conflicts were limited to the two files every concurrent change touches: - config.py: Development advanced VERSION to 0.250.220, so this branch renumbers from 0.250.218 to 0.250.221. Another change had already claimed 0.250.218, which is why the renumber is required rather than cosmetic. - release_notes.md: kept both sides. This branch's section moves to the top as v0.250.221, above Development's 0.250.220, 0.250.219, and 0.250.218 sections. Version references in the feature doc and the two new functional test files were renumbered to match. The tests assert a version floor via assert_app_version_at_least, so the renumber keeps them meaningful rather than pinning an exact value. No source files conflicted. Verified the Content Understanding, Office embedded image, legacy Word, Document Intelligence, and route policy suites still pass, along with the three test files Development added or changed in this range. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
11 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1277
Summary
Enhanced extraction for PDFs and images now uses Azure AI Content Understanding (
prebuilt-documentSearch) instead of Document Intelligenceprebuilt-layout. Content Understanding returns layout-aware markdown plus AI-generated descriptions of figures, charts, and diagrams — the structure Document Intelligence never produced.Standard extraction is unchanged and still uses Document Intelligence
prebuilt-read, which remains required for workspaces and chat file uploads.functions_content_understanding.pyreturns the exact same[{page_number, content}]shape asextract_content_with_azure_di(), so the pipeline swap is a contained branch rather than a rewrite. It uses REST overrequests— no new dependency.Enhanced never becomes a hard dependency
Content Understanding is only offered in Azure commercial regions and is not available in Azure Government.
resolve_enhanced_extraction_engine()falls back to Document Intelligence Layout when the cloud is unsupported, when Content Understanding is unconfigured, or when a request fails at runtime. The reason is recorded on the document and surfaced in workspace tooltips.The admin UI is environment-aware: Azure Public shows the Content Understanding configuration with a Test Connection button and a setup guide; Azure Government and custom clouds show that Enhanced uses Document Intelligence Layout and there is nothing to configure.
Other behavior
Compatibility
Existing setting and per-document field names are preserved, so there is no Cosmos or search index migration.
document_intelligence_pdf_image_extraction_modekeeps itsread/layout/autovalues;layoutnow means "Enhanced", which routes to Content Understanding when available. New per-document fields (extraction_engine,extraction_engine_reason) are additive.Upgrade path: the new
enable_enhanced_extractiontoggle defaults toFalse, so deployments already set to Enhanced or Auto are migrated on first settings read — the toggle is switched on and persisted. Without this they would have silently downgraded to Standard and then had the stored mode overwritten on the next settings save.Bugs found in review
Two review rounds surfaced six issues, all fixed here:
ZipInfo.file_sizeis forgeable and CPython decompresses before truncatingOn #2, measured directly: a 65 KB entry declaring 4096 bytes peaked at 141 MiB through
archive.read(); bounded streaming peaks at 0.29 MiB. Uploaded Office files are now read througharchive.open()with a hard byte ceiling, a compression-method whitelist, and caps on entries and relationship parts inspected. Slide relationship parts are parsed withdefusedxml(already a pinned dependency).Testing
Two new test files with 32 tests, including real DOCX/PPTX zip round-trips and behavioral tests of page reconstruction against the documented response shape. Every security fix has a regression test that was confirmed to fail against the old code rather than assumed correct.
The full 857-file functional suite was run against a baseline worktree at the pre-change commit, diffing per test so the repo's many pre-existing failures (stale pinned versions, tests needing live Azure) were excluded:
The single regression was
test_legacy_doc_ole_extraction.py, which loadsfunctions_content.pythroughimportlibwith a hand-curated stub set and broke on the newfunctions_office_mediaimport. Fixed by putting the app directory onsys.pathso the test imports the real module rather than stubbing it. Route policy contract tests also pass.Reviewer notes
prebuilt-documentSearchinvokes a language model per document, so Enhanced is meaningfully more expensive than Document Intelligence Layout. Auto is the recommended default, which is why enabling the toggle lands there.Version bumped
0.250.217→0.250.218. Feature doc added atdocs/explanation/features/CONTENT_UNDERSTANDING_ENHANCED_EXTRACTION.md, release notes updated with an upgrade-notes section.