Skip to content

fix(azure/passthrough): populate standard_logging_object via logging hook - #25679

Merged
ishaan-berri merged 1 commit into
BerriAI:litellm_ishaan_april14from
michelligabriele:fix/azure-passthrough-standard-logging-object
Apr 14, 2026
Merged

fix(azure/passthrough): populate standard_logging_object via logging hook#25679
ishaan-berri merged 1 commit into
BerriAI:litellm_ishaan_april14from
michelligabriele:fix/azure-passthrough-standard-logging-object

Conversation

@michelligabriele

Copy link
Copy Markdown
Contributor

Relevant issues

No existing GitHub issue — bug reported directly by a customer using Azure passthrough (use_in_pass_through: true) with the Datadog callback enabled.

Pre-Submission checklist

  • I have added testing in tests/test_litellm/ — new file tests/test_litellm/llms/azure/passthrough/test_azure_passthrough_transformation.py with two unit tests (populated ModelResponse on chat/completions, None fall-through on other endpoints).
  • My PR passes all unit tests on make test-unit — the new tests pass locally via uv run pytest tests/test_litellm/llms/azure/passthrough/test_azure_passthrough_transformation.py -v (2 passed). Full make test-unit was not runnable on my machine due to a pyproject.toml parse error in current main (exclude-newer = "3 days") unrelated to this change; relying on CI for the full sweep.
  • My PR's scope is as isolated as possible — one production file changed, one new test file, one endpoint touched (chat/completions). /openai/responses and /openai/messages still return None from the hook, unchanged from today.
  • I have requested a Greptile review — will do after PR is open.

Screenshots / Proof of Fix

Minimal reproduction: real Azure gpt-4.1-mini deployment with use_in_pass_through: true and callbacks: [\"datadog\"]. Requesting POST /azure/openai/deployments/gpt-4.1-mini/chat/completions?api-version=2025-01-01-preview.

Before fix

Request returns 200 to the client, but three callbacks raise on missing `standard_logging_object` on every request — a silent observability + cost-tracking drop:

```
litellm.router.Router::deployment_callback_on_success(): Exception occured - standard_logging_object is None

Error in tracking cost callback - Cost tracking failed for model=gpt-4.1-mini.
Debug info - standard_logging_object not found

Datadog Layer Error - standard_logging_object not found in kwargs
Traceback (most recent call last):
File ".../integrations/datadog/datadog.py", line 480, in create_datadog_logging_payload
raise ValueError("standard_logging_object not found in kwargs")
ValueError: standard_logging_object not found in kwargs
```

After fix

Same request, same config — all three errors gone. Cost is calculated, and Datadog builds + queues a payload with real usage:

```
response_cost: 1.92e-05

Datadog: Logger - Logging payload = {"id": "chatcmpl-DURD...", "call_type": "allm_passthrough_route", "custom_llm_provider": "azure", "usage_object": {"completion_tokens": 10, "prompt_tokens": 8, "total_tokens": 18, ...}, "response_cost": 1.92e-05, ...}

Datadog, event added to queue. Will flush in 5 seconds...
```

The subsequent `403 Forbidden` on the Datadog intake flush is expected — the repro uses `DD_API_KEY=fake`. It confirms the payload reached the shipping layer, which was never possible before the fix.

Type

🐛 Bug Fix

Changes

Root cause. Requests routed through `Router.allm_passthrough_route` on Azure with `use_in_pass_through: true` never populate `kwargs["standard_logging_object"]`. `_success_handler_helper_fn` in `litellm_logging.py` delegates to the provider's `logging_non_streaming_response` hook to build it; `AzurePassthroughConfig` inherits the base-class no-op which returns `None`, so no standard logging object ever gets attached. Every callback that hard-requires it then raises:

  • `litellm/integrations/datadog/datadog.py:480` — `create_datadog_logging_payload` raises `ValueError`
  • `litellm/proxy/hooks/proxy_track_cost_callback.py:262` — cost tracking aborts
  • `litellm/router.py:6213` — `deployment_callback_on_success` logs `standard_logging_object is None`

Fix. Implement `AzurePassthroughConfig.logging_non_streaming_response` for the `chat/completions` endpoint, mirroring the existing Bedrock pattern in `litellm/llms/bedrock/passthrough/transformation.py:121-162`. When the hook returns a `ModelResponse`, the existing consumer branch in `_success_handler_helper_fn` builds `standard_logging_object` with real usage data and the triple-symptom disappears on one code path.

One deliberate difference from Bedrock: the method parses the raw response via `OpenAIGPTConfig().transform_response` rather than `ProviderConfigManager.get_provider_chat_config(AZURE, model)`. The latter returns `AzureOpenAIConfig`, whose `transform_response` raises `NotImplementedError` because Azure is normally routed through the OpenAI SDK rather than the BaseConfig path. Azure's `chat/completions` body is OpenAI-format JSON, so `OpenAIGPTConfig` parses it cleanly — including for o-series and GPT-5 model names.

Scope:

  • Only `chat/completions` is wired. `/openai/responses` and `/openai/messages` still return `None` from the hook — unchanged from today, strictly additive.
  • VLLM passthrough has the same defect shape and is out of scope for this PR; flagging as a follow-up.

Files changed:

  • `litellm/llms/azure/passthrough/transformation.py` — new `logging_non_streaming_response` method, plus `Response` / `Logging` / `CostResponseTypes` imports.
  • `tests/test_litellm/llms/azure/passthrough/test_azure_passthrough_transformation.py` (new) — two unit tests covering populated `ModelResponse` on `chat/completions` and `None` fall-through on other endpoints.

@vercel

vercel Bot commented Apr 14, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
litellm Ready Ready Preview, Comment Apr 14, 2026 6:15am

Request Review

@codecov

codecov Bot commented Apr 14, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@greptile-apps

greptile-apps Bot commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a real observability gap: AzurePassthroughConfig was missing logging_non_streaming_response, so every Azure passthrough chat/completions request left standard_logging_object as None, causing Datadog, cost-tracking, and router-success callbacks to raise on every call. The fix follows the established Bedrock pattern and is tightly scoped to the chat/completions endpoint, with clean unit tests that use no real network calls.

Confidence Score: 5/5

  • Safe to merge; only P2 style findings remain.
  • The fix is minimal, mirrors an established pattern (Bedrock), and is covered by unit tests. Both remaining findings are P2 style suggestions (inline imports and placeholder message in logs) that do not affect correctness or reliability.
  • No files require special attention.

Important Files Changed

Filename Overview
litellm/llms/azure/passthrough/transformation.py Adds logging_non_streaming_response for chat/completions, mirroring Bedrock's pattern using OpenAIGPTConfig; two style-level findings: inline imports and a placeholder message logged as the user input.
tests/test_litellm/llms/azure/passthrough/test_azure_passthrough_transformation.py New unit tests with no real network calls; covers the happy path (chat/completions → ModelResponse) and the pass-through path (other endpoints → None). Clean and well-scoped.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Router as allm_passthrough_route
    participant Logging as LiteLLMLogging
    participant AzurePT as AzurePassthroughConfig
    participant OpenAI as OpenAIGPTConfig

    Client->>Router: POST /azure/…/chat/completions
    Router->>Logging: success_handler(httpx_response)
    Logging->>Logging: normalize_logging_result(result)
    Logging->>AzurePT: logging_non_streaming_response(model, httpx_response, endpoint)
    alt endpoint contains "chat/completions"
        AzurePT->>OpenAI: transform_response(model, httpx_response, …)
        OpenAI-->>AzurePT: ModelResponse (with usage)
        AzurePT-->>Logging: ModelResponse
        Logging->>Logging: build standard_logging_object
        Logging->>Logging: calculate response_cost
        Logging-->>Router: standard_logging_object populated
    else other endpoint
        AzurePT-->>Logging: None
        Logging-->>Router: "standard_logging_object = None (unchanged)"
    end
Loading

Reviews (1): Last reviewed commit: "fix(azure/passthrough): populate standar..." | Re-trigger Greptile

Comment on lines +100 to +102
from litellm import encoding
from litellm.llms.openai.chat.gpt_transformation import OpenAIGPTConfig
from litellm.types.utils import ModelResponse

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Inline imports violate CLAUDE.md style guide

from litellm import encoding, from litellm.llms.openai.chat.gpt_transformation import OpenAIGPTConfig, and from litellm.types.utils import ModelResponse are placed inside the method body. CLAUDE.md says to avoid imports within methods and only allow it to break circular imports. If circular-import avoidance is genuinely needed here (as it is in the Bedrock counterpart), adding a brief inline comment explaining why would make the intent clear. Consider moving any imports that are not circular-import-driven to the module level.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +109 to +121
litellm_model_response: ModelResponse = openai_chat_config.transform_response(
model=model,
messages=[{"role": "user", "content": "no-message-pass-through-endpoint"}],
raw_response=httpx_response,
model_response=ModelResponse(),
logging_obj=logging_obj,
optional_params={},
litellm_params={},
api_key="",
request_data=request_data,
encoding=encoding,
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Placeholder message shadows real user input in observability logs

messages=[{"role": "user", "content": "no-message-pass-through-endpoint"}] is forwarded to logging_obj.post_call(input=messages, ...). For Azure chat/completions passthrough the real messages are already in request_data["messages"], so every Datadog/cost trace will show the placeholder string as the request input instead of the actual conversation. The actual content is still reachable via additional_args, but tools that surface the primary input field will silently show wrong data. Using request_data.get("messages", [{"role": "user", "content": "no-message-pass-through-endpoint"}]) here would match reality without breaking anything:

Suggested change
litellm_model_response: ModelResponse = openai_chat_config.transform_response(
model=model,
messages=[{"role": "user", "content": "no-message-pass-through-endpoint"}],
raw_response=httpx_response,
model_response=ModelResponse(),
logging_obj=logging_obj,
optional_params={},
litellm_params={},
api_key="",
request_data=request_data,
encoding=encoding,
)
litellm_model_response: ModelResponse = openai_chat_config.transform_response(
model=model,
messages=request_data.get(
"messages",
[{"role": "user", "content": "no-message-pass-through-endpoint"}],
),
raw_response=httpx_response,
model_response=ModelResponse(),
logging_obj=logging_obj,
optional_params={},
litellm_params={},
api_key="",
request_data=request_data,
encoding=encoding,
)

@codspeed-hq

codspeed-hq Bot commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 16 untouched benchmarks


Comparing michelligabriele:fix/azure-passthrough-standard-logging-object (63281e8) with main (e64d98f)

Open in CodSpeed

import httpx
from httpx import Response

from litellm.litellm_core_utils.litellm_logging import Logging
if TYPE_CHECKING:
from httpx import URL

from litellm.types.utils import CostResponseTypes
endpoint: str,
) -> Optional["CostResponseTypes"]:
from litellm import encoding
from litellm.llms.openai.chat.gpt_transformation import OpenAIGPTConfig
) -> Optional["CostResponseTypes"]:
from litellm import encoding
from litellm.llms.openai.chat.gpt_transformation import OpenAIGPTConfig
from litellm.types.utils import ModelResponse
@ishaan-berri
ishaan-berri changed the base branch from main to litellm_ishaan_april14 April 14, 2026 16:38
@ishaan-berri
ishaan-berri merged commit 15ef3fa into BerriAI:litellm_ishaan_april14 Apr 14, 2026
48 of 51 checks passed
fzowl pushed a commit to fzowl/litellm that referenced this pull request Jun 24, 2026
…through-standard-logging-object

fix(azure/passthrough): populate standard_logging_object via logging hook
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.

3 participants