Skip to content

fix(ocr): isolate Azure Document Intelligence credential resolution - #40499

Closed
yujonglee-berri wants to merge 3 commits into
litellm_internal_stagingfrom
litellm_fix_azure_di_ocr_env_key
Closed

yujonglee-berri wants to merge 3 commits into
litellm_internal_stagingfrom
litellm_fix_azure_di_ocr_env_key

Conversation

@yujonglee-berri

@yujonglee-berri yujonglee-berri commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

TLDR

Problem this solves:

  • Generic Azure AI keys override Document Intelligence credentials
  • Environment-only sync and async OCR calls return 401

How it solves it:

  • Let each OCR adapter resolve connection defaults
  • Document Intelligence rejects inferred generic Azure defaults
  • Keep explicit keys and Azure Foundry behavior unchanged

User Flow

The bug occurs when an application configures both Azure AI and Azure Document Intelligence credentials, then calls Document Intelligence OCR without an explicit api_key. LiteLLM sends the generic Azure AI key to the Document Intelligence endpoint, causing 401 even though the correct dedicated key is configured

Both services use litellm.ocr(); the model string selects the service. The keys and resource URLs below are placeholders to replace with your own values

import os
import litellm

os.environ["AZURE_DOCUMENT_INTELLIGENCE_API_KEY"] = "di-key"
os.environ["AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT"] = "https://my-di.cognitiveservices.azure.com"

os.environ["AZURE_AI_API_KEY"] = "foundry-key"
os.environ["AZURE_AI_API_BASE"] = "https://my-foundry-endpoint"

document = {
    "type": "document_url",
    "document_url": (
        "https://cdn.jsdelivr.net/gh/BerriAI/litellm"
        "@d769e81c90d453240c61fc572cdb27fae06a89d0"
        "/tests/llm_translation/fixtures/dummy.pdf"
    ),
}

response = litellm.ocr(
    model="azure_ai/doc-intelligence/prebuilt-layout",
    document=document,
)
print(len(response.pages))

Before: the Document Intelligence call fails despite valid dedicated credentials

  1. Set both credential pairs as shown above, using distinct valid keys for the two services
  2. Run the Document Intelligence SDK call above without passing api_key or api_base
  3. Observe AuthenticationError with status 401: the call uses foundry-key against the Document Intelligence endpoint instead of di-key

After: the same Document Intelligence call uses its dedicated credentials and succeeds

  1. Set both credential pairs as shown above, using distinct valid keys for the two services
  2. Run the Document Intelligence SDK call above without passing api_key or api_base
  3. Observe 1 parsed page: the call uses di-key and https://my-di.cognitiveservices.azure.com

The same fix applies to the async SDK call, inside an async function:

response = await litellm.aocr(
    model="azure_ai/doc-intelligence/prebuilt-layout",
    document=document,
)

Other Azure OCR services retain their existing behavior. For example, this call uses AZURE_AI_API_KEY and AZURE_AI_API_BASE before and after the fix, with the model available on the configured endpoint:

response = litellm.ocr(
    model="azure_ai/mistral-document-ai-2505",
    document=document,
)

Explicit Document Intelligence credentials also continue to take precedence:

response = litellm.ocr(
    model="azure_ai/doc-intelligence/prebuilt-layout",
    document=document,
    api_key="explicit-di-key",
    api_base="https://another-di.cognitiveservices.azure.com",
)

Relevant issues

The proxy already rejects request-supplied api_base unless the administrator enables client-side overrides. A regression test covers the reported Document Intelligence payload without a key. SDK callers can still combine an explicit endpoint with environment credentials

Validation: 125 OCR and provider tests, 317 auth tests, and both live SDK calls pass. The request tests check the final endpoint and subscription-key header for environment and explicit credentials in sync and async modes

Linear ticket

Pre-Submission checklist

  • I have added meaningful tests
  • The handful of test files covering my change pass locally, e.g. uv run pytest tests/test_litellm/<your_test_file>.py -v
  • My PR passes all required CI/CD checks (e.g., lint, schema.d.ts sync check, etc.)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5 before requesting a maintainer review

Delays in PR merge?

If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review)

Screenshots / Proof of Fix

The live check uses separate Azure AI and Document Intelligence keys, the documented environment-only SDK setup, and a public one-page PDF. LiteLLM documents these provider-specific variables, while Microsoft documents that Document Intelligence calls require the resource key and endpoint

Before (a8bb2f9)

  1. Run python /tmp/ocr_auth_matrix.py --case azure-di-env-key --modes both --jobs 1 --delay 1
  2. Observe both real SDK calls reject the environment-selected credential:
BACKEND {"mode": "python"}
RUN     azure-di-env-key  sync  deadline=180s
FAIL    azure-di-env-key  sync  AuthenticationError: Azure_aiException - {"error":{"code":"401","message":"Access denied due to invalid subscription key or wrong API endpoint."}}
RUN     azure-di-env-key  async deadline=180s
FAIL    azure-di-env-key  async AuthenticationError: Azure_aiException - {"error":{"code":"401","message":"Access denied due to invalid subscription key or wrong API endpoint."}}
SUMMARY pass=0 fail=2 timeout=0 skip=0

After (2c10639)

  1. Run python /tmp/ocr_auth_matrix.py --case azure-di-env-key --modes both --jobs 1 --delay 5
  2. Observe both real SDK calls authenticate and parse the one-page PDF:
BACKEND {"mode": "python"}
RUN     azure-di-env-key  sync  deadline=180s
PASS    azure-di-env-key  sync  model=doc-intelligence/prebuilt-layout pages=1 elapsed=6.21s
RUN     azure-di-env-key  async deadline=180s
PASS    azure-di-env-key  async model=doc-intelligence/prebuilt-layout pages=1 elapsed=6.01s
SUMMARY pass=2 fail=0 timeout=0 skip=0 elapsed=12.22s

Type

🐛 Bug Fix

Caveats (if any)

Final Attestation

  • The tests check the right things, including the edge cases, and regressions in the respective real-world customer use-cases are not possible after this PR

@yujonglee-berri
yujonglee-berri requested a review from a team September 10, 2026 00:59
@codspeed

codspeed Bot commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 31 untouched benchmarks


Comparing litellm_fix_azure_di_ocr_env_key (2c10639) with litellm_internal_staging (f0fac55)

Open in CodSpeed

@greptile-apps

greptile-apps Bot commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR moves OCR connection-parameter resolution behind provider adapters so Azure Document Intelligence can ignore inferred generic Azure AI credentials while preserving explicit credentials and other providers’ existing behavior.

  • Adds a provider-specific connection-resolution override for Azure Document Intelligence.
  • Adds a default resolution hook for other OCR adapters.
  • Covers dedicated environment credentials and explicit credentials in synchronous and asynchronous request preparation.
  • Confirms that proxy requests still cannot supply api_base without the existing administrator opt-in.

Confidence Score: 5/5

The PR appears safe to merge; the adapter refactor preserves existing behavior while correctly isolating Azure Document Intelligence credential defaults.

No actionable regressions or outstanding previous findings remain. The earlier provider-specific-logic concern is fully addressed because Document Intelligence branching now resides in its provider adapter.

Important Files Changed

Filename Overview
litellm/llms/azure_ai/ocr/document_intelligence/transformation.py Adds adapter-local suppression of inferred generic Azure credentials when Document Intelligence credentials were not explicitly supplied.
litellm/llms/base_llm/ocr/transformation.py Adds a default connection-resolution hook that preserves existing credential precedence for other OCR providers.
litellm/ocr/main.py Delegates connection resolution to the selected OCR adapter and consistently propagates the resulting values.
tests/test_litellm/ocr/test_ocr_azure_document_intelligence_api_base.py Covers dedicated environment credentials, explicit credentials, secret references, and sync/async request preparation.
tests/test_litellm/proxy/auth/test_auth_utils.py Adds regression coverage confirming that request-supplied OCR endpoints remain blocked by the proxy’s existing policy.

Reviews (2): Last reviewed commit: "refactor(ocr): clarify credential resolu..." | Re-trigger Greptile

Comment thread litellm/ocr/main.py Outdated
Comment thread litellm/ocr/main.py Outdated
@veria-ai

veria-ai Bot commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

PR overview

All previously flagged issues have been addressed. No open security concerns remain on this pull request.

Security review

No open security issues remain on this pull request.

Fixed/addressed: 1 · PR risk: 0/10

@codecov

codecov Bot commented Sep 10, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@yujonglee-berri

Copy link
Copy Markdown
Contributor Author

@greptileai Please review the adapter refactor, simplified credential tests, and regression coverage for the existing proxy endpoint restriction

@yujonglee-berri yujonglee-berri changed the title fix(ocr): prefer Azure Document Intelligence environment key fix(ocr): isolate Azure Document Intelligence credential resolution Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant