Keep both sets of tests: upstream's OAuth2 token injection test and
our case-insensitive tool matching tests. Use upstream's version of
the bedrock output_config test (more comprehensive).
* fix(proxy): make async_post_call_response_headers_hook consistent across all endpoints
The response headers hook had 5 gaps that prevented callbacks from
reliably extracting routing metadata across endpoint types:
1. Hook never fired for /audio/transcriptions (endpoint bypasses
base_process_llm_request)
2. custom_llm_provider not accessible in hook data for any endpoint
3. custom_llm_provider not stamped in ResponsesAPIResponse._hidden_params
(unlike chat completions)
4. model_info under inconsistent keys (metadata vs litellm_metadata)
5. request_headers always None at all call sites
This adds a litellm_call_info parameter to the hook that normalizes
routing metadata (custom_llm_provider, model_info, api_base, model_id)
regardless of endpoint type. Also stamps custom_llm_provider on
Responses API responses, adds the hook call to the transcription
handler, and passes request_headers at all call sites.
Supersedes PR #21385.
* fix(proxy): address review feedback — safer backwards compat and None guards
- Replace try/except TypeError with inspect.signature() check for
litellm_call_info backwards compatibility. This avoids masking real
TypeErrors inside callback implementations and prevents double
invocation with inconsistent parameters.
- Use (data.get("key") or {}) instead of data.get("key", {}) to guard
against keys that exist with an explicit None value, which would
cause AttributeError on the subsequent .get() call.
* fix(proxy): cache inspect.signature result for callback compat check
Move the inspect.signature() call into a module-level helper with a
dict cache keyed by callback identity. Avoids repeated introspection
per request per callback in the hot path.
* fix(proxy): use class identity for signature cache key
Key the _CALLBACK_ACCEPTS_CALL_INFO cache by id(type(cb)) instead of
id(cb) to avoid stale entries from Python address reuse after GC.
All instances of the same callback class share the same method
signature, so class identity is both safer and more cache-efficient.
- Thread api_version through HTTP handlers to Azure realtime endpoints
- Make expires_at optional in RealtimeClientSecretResponse
- Fix test token expiry times to be in the future
- Populate user_id and team_id in minimal_auth for spend tracking
Made-with: Cursor
- user_dashboard.tsx: Replace userInfoCall with userGetInfoV2 for spend data,
remove keys/teams logic (keys come from props/useKeys hook, teams from fetchTeams)
- user_info_view.tsx: Replace userInfoCall with userGetInfoV2, flatten data
structure from nested {user_info: {...}} to flat response, fetch team details
separately using teamInfoCall, remove keys display (Virtual Keys section)
- Update user_dashboard.test.tsx and user_info_view.test.tsx mocks
Co-authored-by: yuneng-jiang <yuneng-jiang@users.noreply.github.com>
- Add UserInfoV2Response type and userGetInfoV2() function in networking.tsx
- Migrate useCurrentUser hook from userInfoCall to userGetInfoV2
- Update useCurrentUser.test.ts to test new v2 API integration
- The hook no longer needs userRole since the endpoint handles auth itself
Co-authored-by: yuneng-jiang <yuneng-jiang@users.noreply.github.com>
- Add UserInfoV2Response type in _types.py (returns only user object, no keys/teams)
- Add /v2/user/info endpoint handler with proper access control:
- Proxy admins can query any user
- Team admins can query users in their teams
- Internal users can query themselves only
- Returns 404 for unauthorized/not-found (not 403)
- Add /v2/user/info to info_routes in LiteLLMRoutes
- Add route check passthrough in route_checks.py
- Add get_user_v2() method to Python client
Co-authored-by: yuneng-jiang <yuneng-jiang@users.noreply.github.com>
- Add break after match in user_api_key_auth.py loop to avoid unnecessary
iterations over remaining mapped routes
- Guard against normalized_route being empty when route == root_path exactly,
which would otherwise match every mapped route via startswith("")
- Apply same empty-string guard in pass_through_endpoints.py for consistency