Skip to content

Support multiple headers mapped to the customer user role#23664

Merged
RheagalFire merged 8 commits intoBerriAI:litellm_oss_staging_03_14_2026from
brtydse100:feat/same_role_multiple_headers_mapping
Mar 15, 2026
Merged

Support multiple headers mapped to the customer user role#23664
RheagalFire merged 8 commits intoBerriAI:litellm_oss_staging_03_14_2026from
brtydse100:feat/same_role_multiple_headers_mapping

Conversation

@brtydse100
Copy link
Contributor

@brtydse100 brtydse100 commented Mar 14, 2026

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

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

Added support for mapping multiple request headers to the same LiteLLM user role

image image

general_settings:
master_key: os.environ/LITELLM_MASTER_KEY
database_url: os.environ/DATABASE_URL
user_header_mappings:
- header_name: X-OpenWebUI-User-Id
litellm_user_role: internal_user
- header_name: X-OpenWebUI-User-Email
litellm_user_role: customer
- header_name: X-User-Id
litellm_user_role: customer

@vercel
Copy link

vercel bot commented Mar 14, 2026

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

Project Deployment Actions Updated (UTC)
litellm Ready Ready Preview, Comment Mar 15, 2026 8:21am

Request Review

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 14, 2026

Greptile Summary

This PR extends the user_header_mappings feature so that multiple request headers can share the customer user role, with the first matching header (in config declaration order) winning. get_customer_user_header_from_mapping now returns a lowercased list of header names instead of a single str, and get_end_user_id_from_request_body gains a new isinstance(list) branch that builds a case-insensitive lookup dict and iterates over the config list to honour priority. The deprecated user_header_name string path is preserved intact in the elif str branch with correct two-sided .lower() comparison.

Key changes:

  • get_customer_user_header_from_mapping return type widened to Optional[list]; headers are lowercased at collection time to enable fast dict lookup later.
  • New isinstance(list) branch iterates over the config list first (not over request headers), correctly enforcing config-declared priority rather than HTTP request ordering.
  • Existing local_testing assertion updated to reflect the new list return type.
  • Eight new unit tests added covering the multi-header path, single-header path, no-customer-role path, and the deprecated user_header_name fallback.
  • One test coverage gap: no test exercises the scenario where the first customer-role header in the config list is absent from the request but a subsequent one is present.

Confidence Score: 4/5

  • Safe to merge; core logic is correct and backward-compatible, with a minor test-coverage gap.
  • The implementation correctly handles config-ordered priority, case-insensitive matching, and preservation of the deprecated string path. All previously flagged issues (priority order, case sensitivity, missing deprecated-path test) appear addressed in the HEAD commit. The only remaining gap is the absence of a test for the "first customer header absent, second present" fallback scenario within the list path — a realistic use-case that the code handles correctly but no test would catch if it regressed.
  • No files require special attention; the minor coverage gap is in tests/test_litellm/proxy/auth/test_auth_utils.py.

Important Files Changed

Filename Overview
litellm/proxy/auth/auth_utils.py Changed get_customer_user_header_from_mapping to return Optional[list] (lowercased names) instead of Optional[str], and updated get_end_user_id_from_request_body to handle the new list path with correct config-priority iteration. Deprecated user_header_name string path preserved with correct case-insensitive comparison on both sides.
tests/test_litellm/proxy/auth/test_auth_utils.py New tests added for the multi-header customer role feature, including config-order priority and deprecated fallback. Missing a test for the "first config header absent, second present" fallback scenario.
tests/local_testing/test_auth_utils.py Existing test updated to reflect that get_customer_user_header_from_mapping now returns a lowercased list instead of a single string; no new concerns.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[get_end_user_id_from_request_body] --> B[_get_customer_id_from_standard_headers]
    B -->|found| Z[Return customer ID]
    B -->|None| C{user_header_mappings configured?}
    C -->|Yes| D[get_customer_user_header_from_mapping]
    D --> E{Any customer-role\nheaders?}
    E -->|Yes| F["Return list of lowercased\nheader names (config order)"]
    E -->|No| G[Return None]
    F --> H[custom_header_name_to_check = list]
    G --> I{user_header_name\nconfigured?}
    C -->|No| I
    I -->|Yes| J[custom_header_name_to_check = str]
    I -->|No| K[custom_header_name_to_check = None]
    H --> L{isinstance list?}
    J --> L
    K --> L
    L -->|list| M[Build headers_lower dict\nlowercase keys]
    M --> N[Iterate config list in order]
    N --> O{Header in request?}
    O -->|Yes, non-empty| Z
    O -->|No / empty| P{More headers\nin config?}
    P -->|Yes| N
    P -->|No| Q[Fall through to request body]
    L -->|str| R[Iterate request headers\ncase-insensitive compare]
    R --> O2{Match found\nand non-empty?}
    O2 -->|Yes| Z
    O2 -->|No| Q
    L -->|None| Q
    Q --> S{user field in\nrequest body?}
    S -->|Yes| Z
    S -->|No| T[Return None]
Loading

Comments Outside Diff (1)

  1. tests/test_litellm/proxy/auth/test_auth_utils.py, line 271-289 (link)

    Missing test: first header absent, second present in config list

    test_get_end_user_id_returns_first_customer_header_when_multiple_mappings_exist only covers the case where the first config-ordered customer header is present in the request. There is no test verifying that the code correctly falls back to the next customer header in config order when the first is absent from the request. For example:

    • Config order: ["x-user-id", "x-openwebui-user-email"]
    • Request contains only x-openwebui-user-email, not x-user-id

    The expected result is "user@example.com", which the current implementation would produce correctly (because it iterates over the config list), but a regression here would go undetected. Adding this case would meaningfully improve coverage of the new multi-header fallback feature.

Last reviewed commit: a4db364

@brtydse100 brtydse100 marked this pull request as draft March 14, 2026 21:26
@brtydse100 brtydse100 marked this pull request as ready for review March 14, 2026 21:40
@codspeed-hq
Copy link
Contributor

codspeed-hq bot commented Mar 15, 2026

Merging this PR will not alter performance

✅ 16 untouched benchmarks


Comparing brtydse100:feat/same_role_multiple_headers_mapping (a4db364) with main (548e7eb)

Open in CodSpeed

@RheagalFire RheagalFire changed the base branch from main to litellm_oss_staging_03_14_2026 March 15, 2026 08:25
@RheagalFire RheagalFire merged commit d0fec61 into BerriAI:litellm_oss_staging_03_14_2026 Mar 15, 2026
37 of 39 checks passed
RheagalFire pushed a commit to RheagalFire/litellm that referenced this pull request Mar 15, 2026
)

* added the header mapping feature

* added tests

* final cleanup

* final cleanup

* added missing test and logic

* fixed header sending bug

* Update litellm/proxy/auth/auth_utils.py

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* added back init file in responses + fixed test_auth_utils.py  int local_testing

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
RheagalFire pushed a commit that referenced this pull request Mar 15, 2026
* added the header mapping feature

* added tests

* final cleanup

* final cleanup

* added missing test and logic

* fixed header sending bug

* Update litellm/proxy/auth/auth_utils.py

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* added back init file in responses + fixed test_auth_utils.py  int local_testing

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
towry added a commit to towry/litellm that referenced this pull request Mar 16, 2026
* fix(streaming): preserve upstream custom fields on final chunk

Ensure final finish_reason chunks retain non-OpenAI attributes from original provider chunks, including the holding_chunk flush path where delta is non-empty. Add regression tests for both final-chunk branches.

Made-with: Cursor

* docs(blog): add WebRTC blog post link

Made-with: Cursor

* fix: merge annotations from all streaming chunks in stream_chunk_builder

Previously, stream_chunk_builder only took annotations from the first
chunk that contained them, losing any annotations from later chunks.

This is a problem because providers like Gemini/Vertex AI send grounding
metadata (converted to annotations) in the final streaming chunk, while
other providers may spread annotations across multiple chunks.

Changes:
- Collect and merge annotations from ALL annotation-bearing chunks
  instead of only using the first one

* Support multiple headers mapped to the customer user role (BerriAI#23664)

* added the header mapping feature

* added tests

* final cleanup

* final cleanup

* added missing test and logic

* fixed header sending bug

* Update litellm/proxy/auth/auth_utils.py

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* added back init file in responses + fixed test_auth_utils.py  int local_testing

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Refactor: Filtering beta header after transformation

* Fix downloading vertex ai files

* fix(video): decode managed character ids robustly

Support missing base64 padding in managed character/video IDs so copied encoded IDs still decode to the original upstream character ID.

Made-with: Cursor

* fix(video): enforce character endpoint video MIME handling

Use typed character response models and video multipart helpers so /videos/characters forwards uploaded MP4 files with video/* content type.

Made-with: Cursor

* fix(types): use direct FileTypes import in video schemas

Avoid the temporary Any alias and use a concrete FileTypes import compatible with type checks.

Made-with: Cursor

* Add new videos endpoints

* Add new videos endpoints

* Add new videos endpoints routing and init

* Add new videos transformation

* Add new videos docs

* fix(greptile-review): address backward compatibility and code quality issues

- Remove duplicate DecodedCharacterId TypedDict from litellm/types/videos/main.py
- Remove dead LITELLM_MANAGED_VIDEO_CHARACTER_COMPLETE_STR constant from litellm/types/utils.py
- Add FastAPI Form validation for name field in video_create_character endpoint

Made-with: Cursor

* fix(critical): add HTTP error checks before parsing response bodies in video handlers

Add response.raise_for_status() before transform_*_response() calls in all eight
video character/edit/extension handler methods (sync and async):

- video_create_character_handler / async_video_create_character_handler
- video_get_character_handler / async_video_get_character_handler
- video_edit_handler / async_video_edit_handler
- video_extension_handler / async_video_extension_handler

Without these checks, httpx does not raise on 4xx/5xx responses, so provider
errors (e.g., 401 Unauthorized) pass directly to Pydantic model constructors,
causing ValidationError instead of meaningful HTTP errors. The raise_for_status()
ensures the exception handler receives proper HTTPStatusError for translation into
actionable messages.

Made-with: Cursor

* fix(routing): include avideo_create_character and avideo_get_character in router-first routing

Add avideo_create_character and avideo_get_character to the list of video endpoints
that use router-first routing when a model is provided (either from decoded IDs or
target_model_names).

Previously only avideo_edit and avideo_extension were in the router-first block.
This ensures both character endpoints benefit from multi-deployment load balancing
and model resolution, making them consistent with the other video operations.

This allows:
- avideo_create_character: Router picks among multiple deployments when target_model_names is set
- avideo_get_character: Router assists with multi-model environments for consistency

Made-with: Cursor

* docs: add concise blog post on reusable video characters

- Clear examples for SDK and proxy usage
- Feature highlights: router support, encoding, error handling
- Best practices for character uploads and prompting
- Available from LiteLLM v1.83.0+
- Troubleshooting guide for common issues

Made-with: Cursor

* docs: add edit/extension curl examples and managed ID explanation

- Add curl examples for avideo_edit and avideo_extension APIs
- Explain how LiteLLM encodes/decodes managed character IDs
- Show metadata included in character IDs (provider, model_id)
- Detail transparent router-first routing benefits

Made-with: Cursor

* Fix docs

* Fix docs

* fix(test): skip new video character endpoints in Azure SDK initialization test

Add avideo_create_character, avideo_get_character, avideo_edit, and avideo_extension
to the skip condition since Azure video calls don't use initialize_azure_sdk_client.

Tests now properly skip with expected behavior instead of failing:
- test_ensure_initialize_azure_sdk_client_always_used[avideo_create_character] ✓
- test_ensure_initialize_azure_sdk_client_always_used[avideo_get_character] ✓
- test_ensure_initialize_azure_sdk_client_always_used[avideo_edit] ✓
- test_ensure_initialize_azure_sdk_client_always_used[avideo_extension] ✓

Made-with: Cursor

* fix(critical): remove @AbstractMethod from video character/edit/extension methods

Convert all 8 new video methods from @AbstractMethod to concrete implementations
that raise NotImplementedError. This prevents breaking external third-party
BaseVideoConfig subclasses at import time.

Methods affected:
- transform_video_create_character_request/response
- transform_video_get_character_request/response
- transform_video_edit_request/response
- transform_video_extension_request/response

External integrators can now upgrade without instantiation errors; NotImplementedError
is only raised when operations are actually called on unsupported providers.

This restores backward compatibility with the project's policy.

Made-with: Cursor

---------

Co-authored-by: Sameer Kankute <sameer@berri.ai>
Co-authored-by: bbarwik <bbarwik@gmail.com>
Co-authored-by: brtydse100 <92057527+brtydse100@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
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