Repository navigation
feat: add Azure Content Understanding with resumable lakehouse writes - #2698
Open
Rana Singh (ranadeepsingh) wants to merge 10 commits into
Open
Rana Singh (ranadeepsingh) wants to merge 10 commits into
Rana Singh (ranadeepsingh) wants to merge 10 commits into
Conversation
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.
SynapseML has no Azure Content Understanding stage, so users have to write service calls and job recovery themselves. This PR adds a direct REST transformer and a journal that saves accepted operation handles before polling. It supports explicit Foundry resources and uses the runtime endpoint and identity for Fabric-managed analysis.
Related Issues/PRs
Related implementation and review: #2815. This update applies the retry, API-option, cancellation, and example findings to the existing implementation here.
What changes are proposed in this pull request?
ContentUnderstandingcalls the REST API from Scala and exposes a generated Python API. It needs no Content Understanding SDK. It uses SynapseML's shared HTTP transformer for Spark concurrency, with a CU-specific protocol that distinguishes rejected submissions from work the service may already have accepted.allowInputTruncation. Service options have scalar and column setters.analyze,submit, andpollmodes. The service processes accepted jobs asynchronously. SynapseML can await their results or return a handle for later polling; it does not leave a detached polling daemon running.Retry-Afteris absent.setPollingIntervalSeconds(6)configures seconds in Python and Scala, including fractional values. The existing millisecond setter and savedpollingDelayparameter remain compatible.Retry-Afterwithout consuming the ordinary poll budget.setRetryOnThrottling(False)restores bounded handling.writeToTableandwriteToPathwith an append-only Delta or Parquet journal. Stable IDs and request hashes prevent accidental reuse with different inputs. Completed records are skipped; saved handles resume without a new POST.readTableandreadPathcontact storage only.The default API version is
2025-11-01. ExplicitallowInputTruncation=TrueorFalserequires2026-06-01-previewor newer. The check uses each row's effective API version before submission. A null truncation value omits the option.Limits that matter
setConcurrency(n)limits concurrent transform operations per active Spark partition. In analyze mode, a slot covers submission and polling until completion or the polling attempt limit. This uses the existing shared asynchronous scheduler, not a new completion-order scheduler. Durable writers are sequential and single-owner;batchSizecontrols row collection, not request concurrency. Their keyset batches can rescan and sort the input manifest. For large manifests, use controlled batches orforeachBatchwith stable IDs and the same journal.The Fabric-managed route supports analysis and result polling, not
createAnalyzerorgetAnalyzer. Availability depends on the Fabric service, capacity, and region. This client change does not enable service features or make Foundry LLM deployments available through Fabric. The managed notebook usesprebuilt-readfor OCR. Use BYO Foundry for the LLM-backed invoice example or custom analyzer administration.timeoutgoverns HTTP connection/read timeouts,maxPollAttemptsbounds ordinary polling attempts, andconcurrentTimeoutbounds waiting for concurrent results. A blocked HTTP read may need to reach its socket timeout before cancellation is observed. Driver-side writes stop on JVM interruption; stopping only Python's wait may not stop that JVM call.There is a crash window between service acceptance and the first journal commit. This is not an exactly-once billing guarantee. Service results also expire. Keep document IDs and source content immutable when resuming.
Examples and API references
The guide covers Python and Scala APIs, authentication, request options, recovery, and limits.
The Foundry invoice notebook generates a synthetic PDF, extracts invoice fields with
prebuilt-invoice, saves the handle, and resumes polling.The keyless Fabric notebook generates a synthetic PDF and uses managed
prebuilt-readwith a Delta journal. Repeated display reads saved results rather than submitting again.References: REST analyze, Python SDK.
How is this patch tested?
Polling interval follow-up
Validated the source committed as
13b12d4a855ed456057854822861772f5e3274fc, using JDK 11, Spark 3.5.0, and Python 3.11.15.Passed. All 76 tests in seven CU suites passed, with no failures or skips. Style checks, production/test compilation, wrapper generation, and both packages passed.
Passed. The session runner loaded the newly built jars and generated wrappers, ran all eight checked-in Python tests, and executed the invoice notebook twice against a loopback service with one POST and one GET total. Black 22.3.0 passed for 219 Python files and both notebooks. Notebook JSON and code cells are valid, with no saved outputs.
Coverage includes the six-second default, fractional seconds, zero, invalid and overflowing values, legacy setter overrides,
setParams, copy, clear, and save/load. All previously declared public JVM methods onContentUnderstandingandContentUnderstandingParamsremain present.The earlier real-service evidence below applies to
a154713186. Foundry and Fabric live tests were not rerun for this interval change.Azure build 245852861 has started for
13b12d4a855ed456057854822861772f5e3274fc. This is a new validation run, not a claim that the earlier CI failures are fixed.Earlier validation at a154713
Rebased onto master
37948d7d03ef0fa7f1aa24c54c2dd4d8a29c8dc0. The following local and live runs validateda154713186a296a9e9d8d82fbca1abb1f2baf2b0.Local validation used JDK 11, Spark 3.5.0, Python 3.11.15, and Black 22.3.0.
Passed. All 74 tests in seven CU suites ran, with no failures or skipped tests. Style, production/test compilation, wrapper generation, and both packages passed.
Passed for 219 Python files and both notebooks. Both notebooks also passed JSON and code-cell syntax checks and contain no saved outputs.
Two session-only runners used the exported classpath and current generated Python packages. Here
$repois the tested worktree and$filesis the session's validation directory.Both passed. The first ran all seven checked-in Python tests and the invoice notebook twice against a loopback service, with one POST and one GET total.
The second used local Spark against real Foundry, not a mock or Fabric runtime. All three analyses used GPT-6.1 Sol and extracted the expected vendor, invoice ID, and total. Running the notebook again left its saved result and two journal records unchanged. Two additional documents succeeded in one partition with concurrency 2. Per-request model mappings were applied after the notebook setup cell; shared resource defaults were unchanged.
The live run verified both JVM class sources and the generated wrapper. Artifact SHA-256 values were
cc4ebd4d00b6507fb9190503e5e04c276057529eb23667890f7ec5131dddd217for core and5570ffab9ebcfe09861c75374869b2c411861bd11d8ea7b1e34a3303e9556ee3for cognitive. Evidence is retained incu-fabric-byo-foundry/evidence.json.The session-only
cu-probescenario used the checked-in Fabric E2E runner.$workspaceidentifies the test workspace;$coreJarand$cognitiveJarare hash-qualified copies of the jars above.Passed on Fabric Spark
3.5.5.5.4.20260921.11, applicationapplication_1794291062900_0001. All code cells in the keyless notebook ran with no explicit endpoint or credentials. Repeating the submit/poll cell reused the saved Delta result. Two further OCR analyses succeeded with concurrency 2. The scenario verified both jar sources and the exact generated core/cognitive Python packages, including the CU wrapper hash. This proves managedprebuilt-read, not managed LLM extraction.Evidence, runtime markers, and JUnit results are retained under
cu-fabric-managed-a154. The runner deleted its scratch notebook and lakehouse successfully.The eight Fabric tests cover gateway paths, implicit authentication, bounded refresh, explicit credentials, BYO isolation, cross-workspace rejection, durable recovery, and unsupported analyzer management. Token-refresh failure coverage checks both transform output and writer recovery without another POST.
The shared-client changes also passed 2 cancellation tests, 14 shared-client/pipeline-selection tests, and 10 framework tests earlier in this PR. That framework run retained one pre-existing ignored getter/setter test. The Fabric follow-up does not change those shared-client implementations.
Website rendering has not been run locally. Azure build 245807451 for
a154713186failed Core unit tests and Databricks CPU jobs 1, 2, and 5. Their causes have not been diagnosed, and the polling interval follow-up does not claim to fix them.Does this PR change any dependencies?
No SDK dependency, dependency-pin change, workflow change, or release-tooling change.
Does this PR add a new feature? If so, have you added samples on website?
The website sidebar links to the guide, which links to both runnable notebooks. Neither notebook contains credentials or saved execution outputs.
website/docs/documentationfolder.Make sure you choose the correct class
estimators/transformersand namespace.DocTablepoints to correct API link.npm startto make sure the website renders correctly.<!--pytest-codeblocks:cont-->before each python code blocks to enable auto-tests for python samples.WebsiteSamplesTestsjob pass in the pipeline.