litellm - ✅(Solved) Fix [Bug]: Responses API OTEL spans have `llm.None.*` attribute keys — custom_llm_provider missing from litellm_params [2 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
BerriAI/litellm#25240Fetched 2026-04-08 03:02:33
View on GitHub
Comments
0
Participants
1
Timeline
4
Reactions
0
Participants
Timeline (top)
referenced ×2cross-referenced ×1labeled ×1

Root Cause

In litellm/responses/main.py (line ~951), the litellm_params dict passed to update_from_kwargs does not include custom_llm_provider:

litellm_logging_obj.update_from_kwargs(
    kwargs=kwargs,
    model=model,
    user=user,
    optional_params=dict(responses_api_request_params),
    litellm_params={
        **responses_api_request_params,
        "aresponses": _is_async,
        "litellm_call_id": litellm_call_id,
    },
    custom_llm_provider=custom_llm_provider,  # <-- separate kwarg, not in dict
)

custom_llm_provider is passed as a separate kwarg which lands in model_call_details (via **additional_params in update_environment_variables), but it is never merged into self.litellm_params.

In litellm/integrations/opentelemetry.py, set_raw_request_attributes reads from litellm_params:

custom_llm_provider = litellm_params.get("custom_llm_provider", "Unknown")
# ...
span.set_attribute(f"llm.{custom_llm_provider}.{param}", val)  # → llm.None.id

The same issue affects _record_metrics() which also reads custom_llm_provider from litellm_params.

PR fix notes

PR #25309: fix(responses): include custom_llm_provider in litellm_params for OTEL spans

Description (problem / solution / changelog)

Summary

Fixes #25240

The Responses API endpoints (responses, delete_responses, get_responses, list_input_items, cancel_responses, and the streaming variant) pass custom_llm_provider as a separate kwarg to update_from_kwargs but omit it from the litellm_params dict.

The OTEL integration (opentelemetry.py L1563, L1844) reads custom_llm_provider from litellm_params:

custom_llm_provider = litellm_params.get("custom_llm_provider", "Unknown")
span.set_attribute(f"llm.{custom_llm_provider}.{param}", val)  # → llm.None.id

Since custom_llm_provider is never in litellm_params, it resolves to None → all OTEL span attributes get llm.None.* keys.

Fix: Add "custom_llm_provider": custom_llm_provider to the litellm_params dict in all 6 Responses API call sites in litellm/responses/main.py.

Comparison: The Chat Completions path (litellm/main.py) correctly uses get_litellm_params(custom_llm_provider=...) which includes the provider in the returned dict. This fix aligns the Responses API with that behavior.

Changes

  • litellm/responses/main.py: Added "custom_llm_provider": custom_llm_provider to litellm_params dict in 6 locations (L955, L1142, L1324, L1478, L1642, L1847)

Changed files

  • litellm/responses/main.py (modified, +6/-1)
  • tests/litellm/test_responses_custom_llm_provider.py (added, +123/-0)

PR #25713: fix(main.py): pass custom_llm_provider to get_litellm_params in embedding/transcription/speech

Description (problem / solution / changelog)

Relevant issues

Fixes the root cause behind #25240 and #23347 — custom_llm_provider is None in litellm_params for non-completion call paths.

Pre-Submission checklist

  • I have Added testing in the tests/test_litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

Type

🐛 Bug Fix

Changes

What

embedding(), transcription(), and speech() call get_litellm_params(**kwargs) without passing custom_llm_provider. Since it's extracted as a named parameter in each function signature, it's no longer in **kwargs — so litellm_params["custom_llm_provider"] is always None.

Why it happens

PR #7989 (Jan 2025) refactored these functions to use get_litellm_params(**kwargs) instead of an inline dict. The completion() path passes custom_llm_provider explicitly, but the other three don't — the parameter was lost during the refactor.

Impact

custom_llm_provider=None in litellm_params causes downstream issues in observability integrations that read it:

  • OpenTelemetry: gen_ai.system attribute is None → SDK logs Invalid type NoneType warnings on every request (#25240, #23347)
  • OpenTelemetry: set_raw_request_attributes produces llm.None.* attribute keys
  • Prometheus/Langtrace/Arize: incorrect or missing provider labels

Fix

Pass custom_llm_provider=custom_llm_provider explicitly in all three call sites, matching what completion() already does.

Screenshots / Proof of Fix

Before (embedding request via hosted_vllm):

Invalid type NoneType for attribute 'gen_ai.system' value. Expected one of ['bool', 'str', 'bytes', 'int', 'float'] or a sequence of those types

After: no warnings, gen_ai.system correctly set to "hosted_vllm".

Changed files

  • litellm/main.py (modified, +9/-3)
  • tests/test_litellm/litellm_core_utils/test_get_litellm_params.py (modified, +24/-1)

Code Example

litellm_logging_obj.update_from_kwargs(
    kwargs=kwargs,
    model=model,
    user=user,
    optional_params=dict(responses_api_request_params),
    litellm_params={
        **responses_api_request_params,
        "aresponses": _is_async,
        "litellm_call_id": litellm_call_id,
    },
    custom_llm_provider=custom_llm_provider,  # <-- separate kwarg, not in dict
)

---

custom_llm_provider = litellm_params.get("custom_llm_provider", "Unknown")
# ...
span.set_attribute(f"llm.{custom_llm_provider}.{param}", val)  # → llm.None.id

---

litellm_params={
    **responses_api_request_params,
    "aresponses": _is_async,
    "litellm_call_id": litellm_call_id,
    "custom_llm_provider": custom_llm_provider,  # <-- add this
},
RAW_BUFFERClick to expand / collapse

What happened?

When using the Responses API (/v1/responses), the raw_gen_ai_request OTEL child span has attribute keys like llm.None.id, llm.None.model, llm.None.status instead of llm.azure.id, llm.azure.model, etc.

The None comes from custom_llm_provider being None in litellm_params.

Root Cause

In litellm/responses/main.py (line ~951), the litellm_params dict passed to update_from_kwargs does not include custom_llm_provider:

litellm_logging_obj.update_from_kwargs(
    kwargs=kwargs,
    model=model,
    user=user,
    optional_params=dict(responses_api_request_params),
    litellm_params={
        **responses_api_request_params,
        "aresponses": _is_async,
        "litellm_call_id": litellm_call_id,
    },
    custom_llm_provider=custom_llm_provider,  # <-- separate kwarg, not in dict
)

custom_llm_provider is passed as a separate kwarg which lands in model_call_details (via **additional_params in update_environment_variables), but it is never merged into self.litellm_params.

In litellm/integrations/opentelemetry.py, set_raw_request_attributes reads from litellm_params:

custom_llm_provider = litellm_params.get("custom_llm_provider", "Unknown")
# ...
span.set_attribute(f"llm.{custom_llm_provider}.{param}", val)  # → llm.None.id

The same issue affects _record_metrics() which also reads custom_llm_provider from litellm_params.

Comparison with Completions API (working)

The Chat Completions path in litellm/main.py correctly uses get_litellm_params(custom_llm_provider=custom_llm_provider) which includes the provider in the returned dict. The Responses API path does not call get_litellm_params and constructs the dict inline without the provider.

Suggested Fix

Add "custom_llm_provider": custom_llm_provider to the litellm_params dict in responses/main.py:

litellm_params={
    **responses_api_request_params,
    "aresponses": _is_async,
    "litellm_call_id": litellm_call_id,
    "custom_llm_provider": custom_llm_provider,  # <-- add this
},

The same fix should be applied to all other update_from_kwargs / update_environment_variables call sites in responses/main.py (delete_responses, get_responses, list_input_items, cancel_responses, compact_responses).

Relevant code

  • litellm/responses/main.py — lines ~946-957 (missing provider in litellm_params)
  • litellm/integrations/opentelemetry.pyset_raw_request_attributes() line ~1844
  • litellm/integrations/opentelemetry.py_record_metrics() line ~837
  • litellm/litellm_core_utils/litellm_logging.pyupdate_environment_variables() line ~550-553

Environment

  • LiteLLM version: v1.82.6-nightly (also confirmed on latest main v1.83.3)
  • Provider: Azure (but affects all providers via Responses API)
  • OTEL backend: Langfuse

Reproduction

  1. Configure LiteLLM with langfuse_otel callback
  2. Send a request to /v1/responses with an Azure model
  3. Check the raw_gen_ai_request span in your OTEL backend
  4. Observe attribute keys are llm.None.id, llm.None.model, etc.

Note: PR #19798 (Jan 2026) fixed a related issue where local_vars["custom_llm_provider"] was not updated for provider-specific params, but it did not address the litellm_params dict used by the OTEL integration.

extent analysis

TL;DR

Add the custom_llm_provider to the litellm_params dictionary in litellm/responses/main.py to fix the incorrect OTEL attribute keys.

Guidance

  • Verify the issue by checking the raw_gen_ai_request span in the OTEL backend for attribute keys like llm.None.id and llm.None.model.
  • Apply the suggested fix by adding "custom_llm_provider": custom_llm_provider to the litellm_params dict in responses/main.py.
  • Ensure the same fix is applied to all other relevant update_from_kwargs and update_environment_variables call sites in responses/main.py.
  • Test the fix by sending a request to /v1/responses with an Azure model and checking the OTEL attribute keys.

Example

The corrected litellm_params dictionary should look like this:

litellm_params={
    **responses_api_request_params,
    "aresponses": _is_async,
    "litellm_call_id": litellm_call_id,
    "custom_llm_provider": custom_llm_provider,
}

Notes

This fix assumes that the custom_llm_provider variable is correctly set and passed to the update_from_kwargs function. If the issue persists, verify that the custom_llm_provider value is not None and is correctly merged into the litellm_params dictionary.

Recommendation

Apply the workaround by adding the custom_llm_provider to the litellm_params dictionary, as this fix directly addresses the root cause of the issue.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

litellm - ✅(Solved) Fix [Bug]: Responses API OTEL spans have `llm.None.*` attribute keys — custom_llm_provider missing from litellm_params [2 pull requests, 1 participants]