Skip to content

fix(proxy): recognize *.cognitiveservices.azure.com as OpenAI-compatible in pass-through cost tracking - #29726

Closed
8FAX wants to merge 2 commits into
BerriAI:litellm_oss_branchfrom
8FAX:ls/azure-cognitive-services-hostname-recognition
Closed

fix(proxy): recognize *.cognitiveservices.azure.com as OpenAI-compatible in pass-through cost tracking#29726
8FAX wants to merge 2 commits into
BerriAI:litellm_oss_branchfrom
8FAX:ls/azure-cognitive-services-hostname-recognition

Conversation

@8FAX

@8FAX 8FAX commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Azure OpenAI resources created via the newer "Azure AI Foundry" / Cognitive Services pathway live on `*.cognitiveservices.azure.com` subdomains, not the older `openai.azure.com`. Both are valid Azure OpenAI surfaces in production today.

The OpenAI pass-through cost-tracking handler hard-codes only the older hostname in five places:

  • 4× `OpenAIPassthroughLoggingHandler.is_openai_*_route` static methods (chat completions, image generation, image editing, responses) in `openai_passthrough_logging_handler.py`
  • 1× `PassThroughEndpointLogging.is_openai_route` in `success_handler.py`

As a result, calls from newer Azure deployments are silently classified as "not an OpenAI route", the dispatch into the cost-tracking handler is skipped, and tokens / cost never get extracted into `LiteLLM_SpendLogs` — the row is written with `prompt_tokens=0, completion_tokens=0, spend=0, model='unknown'`.

Reproducer

Verified 2026-06-04 against a real Azure OpenAI deployment on `*.cognitiveservices.azure.com` proxied through LiteLLM v1.88.0.

# With AZURE_API_BASE pointing to a *.cognitiveservices.azure.com resource:
curl -X POST "$LITELLM_PROXY/azure/openai/deployments/MY_DEPLOYMENT/chat/completions?api-version=2024-10-21" \\
  -H "Authorization: Bearer \$LITELLM_MASTER_KEY" \\
  -H "Content-Type: application/json" \\
  -d '{"messages":[{"role":"user","content":"hi"}], "max_tokens":5}'

# Call succeeds, but the LiteLLM_SpendLogs row has:
#   prompt_tokens = 0
#   completion_tokens = 0
#   spend = 0
#   model = 'unknown'
# Because is_openai_route() returns False on the new Azure hostname.

Fix

Factor the hostname check into a single helper `_is_openai_compatible_host` listing all three recognized surfaces (`api.openai.com`, `openai.azure.com`, `cognitiveservices.azure.com`), and have all five call sites delegate to it.

Purely additive — never weakens recognition for the originally-supported hostnames. Every test case that passed before continues to pass.

Tests

Adds `test_is_openai_route_recognizes_cognitiveservices_azure_com` that exercises all four `is_openai__route` static methods against `.cognitiveservices.azure.com` URLs:

  • positive cases per route (chat completions, image generation, image editing, responses)
  • a cross-route negative to confirm route-specific path matching still works on the new hostname

All 24 tests in `test_openai_passthrough_logging_handler.py` pass locally including the new one.

$ python -m pytest tests/test_litellm/proxy/pass_through_endpoints/llm_provider_handlers/test_openai_passthrough_logging_handler.py
24 passed in 2.08s

Checklist

  • CLA signed
  • At least 1 test added (`test_is_openai_route_recognizes_cognitiveservices_azure_com`)
  • Unit tests pass: `pytest tests/test_litellm/proxy/pass_through_endpoints/llm_provider_handlers/test_openai_passthrough_logging_handler.py` → 24 passed
  • Ruff lint: `make lint-ruff` → all checks passed
  • Black format-check on touched files: clean (pre-existing format drift in `litellm/proxy/enterprise/` is unrelated and was not touched)
  • Scope isolated to one specific problem (hostname recognition)

Out of scope for this PR

Tracked separately: `openai_passthrough_handler` calls chat/completions `transform_response` on Responses API payloads (which use `output:` not `choices:`), throws inside the dispatch, and drops the SpendLogs row entirely. The right fix is to use the existing `OpenAIResponsesAPIConfig.transform_response_api_response` for the `is_responses` branch — happy to follow up with a separate PR.

@greptile-apps

greptile-apps Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Too many files changed for review. (1087 files found, 100 file limit)

@CLAassistant

CLAassistant commented Jun 5, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@8FAX
8FAX force-pushed the ls/azure-cognitive-services-hostname-recognition branch from 1e1d075 to 245ecec Compare June 5, 2026 02:19
@codspeed-hq

codspeed-hq Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Congrats! CodSpeed is installed 🎉

🆕 16 new benchmarks were detected.

You will start to see performance impacts in the reports once the benchmarks are run from your default branch.

Detected benchmarks


Open in CodSpeed

@codecov

codecov Bot commented Jun 5, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.66667% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...der_handlers/openai_passthrough_logging_handler.py 88.88% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Azure OpenAI resources created via the newer "Azure AI Foundry" /
Cognitive Services pathway live on `*.cognitiveservices.azure.com`
subdomains, not the older `openai.azure.com`. Both are valid Azure
OpenAI surfaces in production today.

The OpenAI pass-through cost-tracking handler hard-codes only the older
hostname in five places (four `is_openai_*_route` methods on
OpenAIPassthroughLoggingHandler, plus is_openai_route on
PassThroughEndpointLogging). As a result, calls from newer Azure
deployments are silently classified as "not an OpenAI route", the
dispatch into the cost-tracking handler is skipped, and tokens/cost
never get extracted into LiteLLM_SpendLogs — the row gets written with
prompt_tokens=0, completion_tokens=0, spend=0, model='unknown'.

Reproduced 2026-06-04 against a real Azure OpenAI deployment on
`*.cognitiveservices.azure.com` proxied through LiteLLM v1.88.0.

Fix: factor the hostname check into a single helper
`_is_openai_compatible_host` listing all three recognized surfaces
(api.openai.com, openai.azure.com, cognitiveservices.azure.com), and
have all five call sites delegate to it. Purely additive — never
weakens recognition for the originally-supported hostnames.

Adds a test
`test_is_openai_route_recognizes_cognitiveservices_azure_com` that
exercises all four `is_openai_*_route` static methods against
`*.cognitiveservices.azure.com` URLs (positive cases per route + a
small cross-route negative to confirm route-specific path matching
still works on the new hostname).

Out of scope for this PR (separate followup):
  - `openai_passthrough_handler` calls chat/completions
    `transform_response` on Responses API payloads (`output:` not
    `choices:`), which throws inside the dispatch and drops the
    SpendLogs row entirely. Recognized + tracked separately.
@8FAX
8FAX force-pushed the ls/azure-cognitive-services-hostname-recognition branch from 245ecec to 99084ae Compare June 5, 2026 02:23
@8FAX
8FAX changed the base branch from main to litellm_oss_branch June 5, 2026 02:23
@8FAX
8FAX requested a review from a team June 5, 2026 02:23
@8FAX

8FAX commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

Heads-up the Verify PR source branch check (from .github/workflows/guard-main-branch.yml) is showing as failing on this PR, but I believe the actual state is correct now:

  • The PR base is litellm_oss_branch (per the CONTRIBUTING fork-PR guidance), not main.
  • That workflow has on: pull_request: branches: [main] so it shouldn't trigger for PRs targeting litellm_oss_branch.
  • The failed run appears to be sticky from when this PR was originally opened against main — GitHub seems to be keeping the prior conclusion visible even after the base change.

Let me know if you'd like me to rebase to a new branch!

@8FAX

8FAX commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

auth-and-jwt job failure looks like an unrelated pre-existing flake — could you re-run when you have a chance?

The failure (run #26991540029, job 79652555702) is three tests in tests/proxy_unit_tests/test_custom_tokenizer_bug.py:

  • test_custom_tokenizer_embedding_model
  • test_custom_tokenizer_from_model_info
  • test_custom_tokenizer_with_llamacpp

All three fail with the same root cause — HuggingFace Hub rate-limited the tokenizer download:

HTTP Error 429 thrown while requesting HEAD
https://huggingface.co/Xenova/llama-3-tokenizer/resolve/main/tokenizer.json
Retrying in 1s [Retry 1/5].
... (all 5 retries hit 429) ...
huggingface_hub.errors.LocalEntryNotFoundError: An error happened while trying
to locate the file on the Hub and we cannot find the requested files in the
local cache.

This PR's scope doesn't touch tokenizers, HuggingFace, or that test file at all. My diff is only in:

  • litellm/proxy/pass_through_endpoints/llm_provider_handlers/openai_passthrough_logging_handler.py (URL hostname checks)
  • litellm/proxy/pass_through_endpoints/success_handler.py (one hostname check)
  • tests/test_litellm/proxy/pass_through_endpoints/llm_provider_handlers/test_openai_passthrough_logging_handler.py (added one new test)

Zero overlap with test_custom_tokenizer_bug.py's code paths.

If a CI re-run after the HF Hub backoff clears doesn't get it green, happy to help triage further — but I don't think there's anything to fix on my side.

(Possible long-term suggestion for the maintainers — separate from this PR — would be to mock or pre-cache the HF tokenizer responses in CI to avoid this flake class, since it'll trip any contributor whose PR happens to overlap with a busy CI window. Happy to open a separate issue for that if useful.)

The auth-and-jwt job's previous failure was a transient HuggingFace Hub
429 rate-limit hitting tokenizer downloads in tests/proxy_unit_tests/test_custom_tokenizer_bug.py
(unrelated to this PR's scope — see prior comment). Empty commit to
re-run all checks; no code change.
@8FAX

8FAX commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

Closing in favor of #29730 — same diff, but opened directly against `litellm_oss_branch` from the start so the `Verify PR source branch` check (sticky from this PR's initial `main` targeting) doesn't shadow real CI signal. Discussion + maintainer review can continue there.

@8FAX 8FAX closed this Jun 5, 2026
@8FAX
8FAX deleted the ls/azure-cognitive-services-hostname-recognition branch June 10, 2026 14:27
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.

2 participants