Skip to content

Litellm internal staging 04 11 2026 - #25562

Merged
yuneng-berri merged 10 commits into
mainfrom
litellm_internal_staging_04_11_2026
Apr 11, 2026
Merged

Litellm internal staging 04 11 2026#25562
yuneng-berri merged 10 commits into
mainfrom
litellm_internal_staging_04_11_2026

Conversation

@krrish-berri-2

Copy link
Copy Markdown
Contributor

Relevant issues

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • 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

Delays in PR merge?

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

CI (LiteLLM team)

CI status guideline:

  • 50-55 passing tests: main is stable with minor issues.
  • 45-49 passing tests: acceptable but needs attention
  • <= 40 passing tests: unstable; be careful with your merges and assess the risk.
  • Branch creation CI run
    Link:

  • CI run for the last commit
    Link:

  • Merge / cherry-pick CI run
    Links:

Type

🆕 New Feature
🐛 Bug Fix
🧹 Refactoring
📖 Documentation
🚄 Infrastructure
✅ Test

Changes

…ming (#25498)

* fix(responses): map refusal stop_reason to incomplete status in streaming

Fixes streaming responses API translation where Anthropic's stop_reason="refusal"
was incorrectly translated to status="completed" instead of "incomplete".

Root cause: build_base_response was unconditionally overwriting finish_reason
with None from later chunks, losing the terminal content_filter value.

Changes:
- streaming_chunk_builder_utils: skip None finish_reason values in build_base_response
- streaming_iterator: snapshot chunks before returning pending events (sync path)
- streaming_handler: treat usage-only chunks as meaningful content
- transformation: map finish_reason=refusal to status=incomplete
- tests: add regression tests for refusal handling

Made-with: Cursor

* Fix test
…nputs (#25481)

* feat(guardrails): optional skip system message in unified guardrail inputs

Made-with: Cursor

* feat(dashboard): skip_system_message_in_guardrail in guardrail UI

Add a tri-state control (inherit / yes / no) when creating or editing
guardrails so admins can set litellm_params.skip_system_message_in_guardrail
without YAML. Table edit merges existing litellm_params before PUT to avoid
wiping content-filter and other provider fields.

Document the dashboard flow in the guardrails quick start with a screenshot.

Made-with: Cursor

* fix(guardrails): type structured_messages as AllMessageValues for mypy

Use AllMessageValues in openai_messages_without_system and cast adapter
request messages so GenericGuardrailAPIInputs matches TypedDict.

Made-with: Cursor
…25419)

When modify_params is true, Bedrock Converse setup no longer prepends or
appends the default user message if the boundary assistant turn has
prefix: true, so OpenAI-style assistant prefill reaches the API unchanged.

Made-with: Cursor
…se parsing (#25287)

* feat(containers): Azure container routing, managed IDs, and delete response wire format

- Add AzureContainerConfig and safe URL joining for paths with api-version query
- Encode/decode managed container IDs in responses, streaming, and proxy handlers
- Accept OpenAI delete response object literal container.file.deleted
- Tests for Azure URL regression and DeleteContainerFileResponse parsing

Made-with: Cursor

* fix(responses): gate response id update on parsed_chunk having response

Delta stream events do not include a response body; Mock-based tests
(and any truthy synthetic .response on transforms) must not trigger
_update_responses_api_response_id_with_model_id. Fixes
test_stop_async_iteration_not_logged_as_failure (TypeError: Mock not iterable).

Made-with: Cursor

* feat(containers): encode container IDs in SDK responses for routing

- Add ContainerRequestUtils.encode_container_id_in_response utility
- Encode container_id in create/retrieve/delete responses (SDK path)
- Fix streaming iterator: gate response ID update on parsed_chunk key
- Follows responses API pattern (encode after handler, not in handler)

Made-with: Cursor

* fix(containers): module-level imports and managed cntr_ ID encoding

- Move ResponsesAPIRequestUtils imports to module scope (utils, main, handler_factory).
- Serialize absent model_id as empty segment instead of literal None; decode empty
  and legacy "None" segments as missing for router affinity.
- Add unit tests for build/decode round-trip and legacy IDs.

Made-with: Cursor

* fix(containers): decode managed IDs in endpoint_factory SDK path

- Add decode_managed_container_id_for_request in containers/utils and reuse from main.
- Strip LiteLLM cntr_ wrappers before generic_container_handler (64-char API limit).
- Resolve provider for logging/errors; add unit test for decode helper.
- Use resolved_custom_llm_provider after decode for mypy-safe provider typing.

Made-with: Cursor

* Fix p1 concern

* Fix p1 concern
@vercel

vercel Bot commented Apr 11, 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 11, 2026 10:46pm

Request Review

@codspeed-hq

codspeed-hq Bot commented Apr 11, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 16 untouched benchmarks


Comparing litellm_internal_staging_04_11_2026 (c40e459) with main (9a43e32)

Open in CodSpeed

…se traces (#25448)

* fix(logging): preserve proxy key-auth metadata on /v1/messages Langfuse traces

update_from_kwargs() overwrites proxy metadata (user_api_key_hash, etc.)
with Anthropic's native metadata when both exist. Merge instead of replace.

* fix(test): update stale assertion for new metadata merge semantics

* test: add explicit conflict-resolution test for metadata merge
* feat(prometheus): reduce default latency bucket cardinality and make configurable

* test(prometheus): add coverage for PrometheusServicesLogger latency buckets

* Revert "test(prometheus): add coverage for PrometheusServicesLogger latency buckets"

This reverts commit 1bfd004.

* test(prometheus): add coverage for PrometheusServicesLogger latency buckets
…errors (#25530)

* fix(s3): add retry with exponential backoff for transient S3 503/500 errors

S3 occasionally returns 503 "Slow Down" during PUT operations when
request rates spike above partition limits. The current code makes a
single upload attempt via httpx — unlike boto3, httpx has no built-in
retry for transient S3 errors. Failed uploads permanently lose the
request's audit/logging data.

Add exponential backoff retry (3 attempts, 1s/2s delays) for S3
500/503 responses in both async_upload_data_to_s3 and
upload_data_to_s3. Logs a warning on each retry with the S3 object
key for observability.

In production we observed ~18 permanent S3 upload failures per day
(124 over 7 days) — all transient 503s that would have succeeded on
a single retry.

* test(s3): add unit tests for S3 upload retry logic

Tests cover:
- Async retry on 503 (succeeds on second attempt)
- Async retry on 500
- Exhausted retries on persistent 503 (calls handle_callback_failure)
- No retry on 4xx errors (403)
- Sync retry on 503

* style(s3): move time import to module level

Address review feedback: move `import time` from inside
upload_data_to_s3 to the top-level imports per project style guide.
@greptile-apps

greptile-apps Bot commented Apr 11, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This staging PR bundles five features: (1) per-guardrail skip_system_message_in_guardrail toggle (UI + backend) for unified guardrails on OpenAI chat and Anthropic messages; (2) structured Bedrock guardrail assessment details in error responses; (3) Azure container API support with LiteLLM-managed container ID encoding for provider routing affinity; (4) customizable Prometheus latency buckets via prometheus_latency_buckets; and (5) S3 upload retry on transient 500/503 errors. Several correctness fixes are also included (finish_reason overwrite, usage-only streaming chunks, Bedrock prefix-message handling).

  • P1 — litellm/types/integrations/prometheus.py: The default LATENCY_BUCKETS constant was silently changed, removing 22 historical bucket boundaries (1.5 s, 2.5 s … 9.5 s, 15 s … 25 s, 180 s, 240 s). Existing Prometheus deployments whose dashboards or alert rules reference those le labels will stop receiving new data after upgrading, with no opt-out path short of manually restoring the old values via the new prometheus_latency_buckets setting.

Confidence Score: 3/5

Mostly safe, but the silent LATENCY_BUCKETS default change will break existing Prometheus dashboards and alerts for any user who upgrades without setting prometheus_latency_buckets.

One P1 finding: the default Prometheus histogram buckets changed in a backwards-incompatible way. This won't crash the proxy but it breaks the monitoring contract for existing deployments — a hard-to-debug production observability regression. All other changes are well-implemented and well-tested.

litellm/types/integrations/prometheus.py — default LATENCY_BUCKETS change; ui/litellm-dashboard/src/components/guardrails/edit_guardrail_form.tsx — fullLitellmParams spread may carry over stale provider-specific keys on provider switch.

Important Files Changed

Filename Overview
litellm/types/integrations/prometheus.py Default LATENCY_BUCKETS significantly changed (22 buckets removed, 3 added); breaks existing Prometheus dashboards/alerts keyed on removed le labels.
litellm/proxy/guardrails/guardrail_hooks/bedrock_guardrails.py Adds _extract_blocked_assessments to surface structured policy-violation details; enriches HTTPException with guardrailIdentifier, guardrailVersion, and assessments.
litellm/proxy/utils.py Adds guardrail context enrichment (name, mode) to HTTPExceptions raised during all hook phases; wraps all guardrail tasks and streaming iterators with enrichment helpers.
litellm/proxy/common_request_processing.py Fixes dict-detail HTTPException serialization for both streaming and non-streaming proxy surfaces; preserves full guardrail payload as structured fields.
litellm/responses/utils.py Adds container ID encoding/decoding with provider and model metadata; wires into _update_container_ids_in_response for non-streaming responses.
litellm/integrations/s3_v2.py Adds 3-attempt exponential-backoff retry (1s, 2s) for transient S3 500/503 errors in both sync and async upload paths.
litellm/llms/base_llm/guardrail_translation/utils.py New helpers effective_skip_system_message_for_guardrail and openai_messages_without_system extract per-guardrail vs global skip logic cleanly.
ui/litellm-dashboard/src/components/guardrails/edit_guardrail_form.tsx Preserves full stored litellm_params on edit (avoids silent loss of content-filter fields); adds skip_system_message_choice. Stale provider-specific params can survive a provider switch.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Guardrail Request] --> B{during_call_hook}
    B --> C[_run_guardrail_task_with_enrichment]
    C --> D{Unified guardrail?}
    D -- Yes --> E[async_moderation_hook]
    D -- No --> F[callback.async_moderation_hook]
    E --> G{skip_system_message?}
    F --> G
    G -- Yes --> H[openai_messages_without_system]
    G -- No --> I[Full messages]
    H --> J[Apply guardrail]
    I --> J
    J -- Blocked --> K[HTTPException with dict detail]
    K --> L[_enrich_http_exception_with_guardrail_context]
    L --> M[ProxyException via _serialize_http_exception_detail]
    M --> N[Structured error response]
    J -- Allowed --> O[Request proceeds]
Loading

Reviews (2): Last reviewed commit: "fix linting" | Re-trigger Greptile

Comment thread litellm/__init__.py
langfuse_default_tags: Optional[List[str]] = None
langsmith_batch_size: Optional[int] = None
prometheus_initialize_budget_metrics: Optional[bool] = False
prometheus_latency_buckets: Optional[List[float]] = None
Comment on lines +616 to +626
# Merge metadata carefully — don't overwrite the merged metadata
# from kwargs/litellm_metadata with the caller's litellm_params metadata.
# e.g. anthropic_messages passes Anthropic's native metadata ({user_id: ...})
# in litellm_params, which would overwrite proxy key-auth fields.
lp_metadata = litellm_params.pop("metadata", None)
base_litellm_params.update(litellm_params)
if lp_metadata and isinstance(lp_metadata, dict):
base_litellm_params.setdefault("metadata", {})
for k, v in lp_metadata.items():
if k not in base_litellm_params["metadata"]:
base_litellm_params["metadata"][k] = v

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 pop() mutates the caller's litellm_params dict

litellm_params.pop("metadata", None) removes the metadata key from the dict that was passed in by the caller. If any upstream caller keeps a reference to the same dict (e.g. for retry logic, follow-up logging, or span attribution), it will silently lose its metadata after this call. CLAUDE.md's "use dict spread for immutable copies" guideline recommends building a new dict instead:

lp_metadata = litellm_params.get("metadata", None)
base_litellm_params.update({k: v for k, v in litellm_params.items() if k != "metadata"})

Context Used: CLAUDE.md (source)

Comment on lines +12 to +14
import litellm

return bool(getattr(litellm, "skip_system_message_in_guardrail", False))

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 import litellm inside function body

CLAUDE.md explicitly says: "Avoid imports within methods — place all imports at the top of the file … The only exception is avoiding circular imports where absolutely necessary." The only reason import litellm is deferred here is to avoid a circular import (litellm/__init__.py imports from litellm.llms.*). If that is the intent, a comment explaining the circular-import rationale would prevent future reviewers from "cleaning it up" back to a module-level import.

def effective_skip_system_message_for_guardrail(guardrail_to_apply: Any) -> bool:
    per = getattr(guardrail_to_apply, "skip_system_message_in_guardrail", None)
    if per is not None:
        return bool(per)
    # Deferred to avoid circular import (litellm.__init__ → litellm.llms.*)
    import litellm  # noqa: PLC0415

    return bool(getattr(litellm, "skip_system_message_in_guardrail", False))

Context Used: CLAUDE.md (source)

Comment on lines +558 to +563
"""
try:
# If it doesn't start with cntr_, it's not a managed ID
if not container_id.startswith("cntr_"):
return DecodedResponseId(
custom_llm_provider=None,

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 replace("cntr_", "") strips all occurrences, not just the prefix

container_id.replace("cntr_", "") would remove every occurrence of the literal string in container_id, not only the leading one. Standard base64 (b64encode) cannot produce cntr_ (underscores don't appear in the standard alphabet), so this is safe today, but the intent is clearly prefix removal. Use removeprefix (Python 3.9+) or an explicit slice to make the intent unambiguous:

Suggested change
"""
try:
# If it doesn't start with cntr_, it's not a managed ID
if not container_id.startswith("cntr_"):
return DecodedResponseId(
custom_llm_provider=None,
cleaned_id = container_id.removeprefix("cntr_")

@yuneng-berri
yuneng-berri merged commit 99cb3af into main Apr 11, 2026
100 of 106 checks passed
@yuneng-berri
yuneng-berri deleted the litellm_internal_staging_04_11_2026 branch April 11, 2026 22:55
fzowl pushed a commit to fzowl/litellm that referenced this pull request Jun 24, 2026
…g_04_11_2026

Litellm internal staging 04 11 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.

7 participants