Support multiple headers mapped to the customer user role#23664
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR extends the Key changes:
Confidence Score: 4/5
|
| 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]
Comments Outside Diff (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_existonly 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, notx-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. - Config order:
Last reviewed commit: a4db364
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
84e24d8 to
a4db364
Compare
d0fec61
into
BerriAI:litellm_oss_staging_03_14_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>
* 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>
* 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>
Relevant issues
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
tests/test_litellm/directory, Adding at least 1 test is a hard requirement - see detailsmake test-unit@greptileaiand received a Confidence Score of at least 4/5 before requesting a maintainer reviewCI (LiteLLM team)
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
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