OpenRouter supports image editing through its chat completions endpoint
for models like google/gemini-2.5-flash-image, but LiteLLM raised
ValueError("image edit is not supported for openrouter") because
OpenRouter was not registered as an image edit provider.
Add OpenRouterImageEditConfig that routes image edit requests through
the chat completions endpoint with the source image as a base64 data
URL in the message content array.
Fixes https://github.com/BerriAI/litellm/issues/22305
Replace simulated test with one that invokes the real
Logging.failure_handler(), mocks LangFuseHandler to capture kwargs,
and asserts original_response is excluded and session_id is preserved.
This ensures the test catches regressions if the production code changes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix: The Langfuse failure logging path was passing self.model_call_details
(which includes original_response, potentially a coroutine) instead of the
clean local kwargs copy. This aligns the failure path with the success path
behavior (litellm_logging.py:2956).
Reverted the session_id-as-trace_id approach as it causes trace collisions
in Langfuse (multiple calls in the same session would overwrite each other).
Instead, session_id is correctly used only for Langfuse session grouping via
trace_params["session_id"], while each call retains its own unique trace_id.
Added 4 tests:
- session_id correctly passed as trace session_id (not trace_id)
- session_id preserved for ERROR level (failure) logs
- explicit trace_id takes priority over session_id
- failure path kwargs excludes original_response
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add filtering capabilities to /key/list endpoint for project_id and access_group_id parameters. Both filters work globally across all visibility rules and stack with existing sort/pagination params. Added comprehensive unit tests for the new filters.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
* fix(jwt): support OIDC discovery URLs, handle roles array, improve error hints
Three fixes for Azure AD JWT auth:
1. OIDC discovery URL support - JWT_PUBLIC_KEY_URL can now be set to
.well-known/openid-configuration endpoints. The proxy fetches the
discovery doc, extracts jwks_uri, and caches it.
2. Handle roles claim as array - when team_id_jwt_field points to a list
(e.g. AAD's "roles": ["team1"]), auto-unwrap the first element instead
of crashing with 'unhashable type: list'.
3. Better error hint for dot-notation indexing - when team_id_jwt_field is
set to "roles.0" or "roles[0]", the 401 error now explains to use
"roles" instead and that LiteLLM auto-unwraps lists.
* Add integration demo script for JWT auth fixes (OIDC discovery, array roles, dot-notation hints)
Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>
* Add demo_servers.py for manual JWT auth testing with mock JWKS/OIDC endpoints
Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>
* Add demo screenshots for PR comment
Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>
* Add integration test results with screenshots for PR review
Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>
* address greptile review feedback (greploop iteration 1)
- fix: add HTTP status code check in _resolve_jwks_url before parsing JSON
- fix: remove misleading bracket-notation hint from debug log (get_nested_value does not support it)
* Update tests/test_litellm/proxy/auth/test_handle_jwt.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* remove demo scripts and assets
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Fixes#18381: When using both tools and response_format with Bedrock
Converse API, LiteLLM internally adds json_tool_call to handle structured
output. Bedrock may return both this internal tool AND real user-defined
tools, breaking consumers like OpenAI Agents SDK.
Changes:
- Non-streaming: Added _filter_json_mode_tools() to handle 3 scenarios:
only json_tool_call (convert to content), mixed (filter it out), or
no json_tool_call (pass through)
- Streaming: Added json_mode tracking to AWSEventStreamDecoder to suppress
json_tool_call chunks and convert to text content
- Fixed optional_params.pop() mutation issue
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* fix: populate user_id and user_info for admin users in /user/info endpoint
Fixes#22179
When admin users call /user/info without a user_id parameter, the endpoint
was returning null for both user_id and user_info fields. This broke
budgeting tooling that relies on /user/info to look up current budget and spend.
Changes:
- Modified _get_user_info_for_proxy_admin() to accept user_api_key_dict parameter
- Added logic to fetch admin's own user info from database
- Updated function to return admin's user_id and user_info instead of null
- Updated unit test to verify admin user_id is populated
The fix ensures admin users get their own user information just like regular users.
* test: make mock get_data signature match real method
- Updated MockPrismaClientDB.get_data() to accept all parameters that the real method accepts
- Makes mock more robust against future refactors
- Added datetime and Union imports
- Mock now returns None when user_id is not provided
* fix: Add PROXY_ADMIN role to system user for key rotation
The key rotation worker was failing with 'You are not authorized to regenerate this key'
when rotating team keys. This was because the system user created by
get_litellm_internal_jobs_user_api_key_auth() was missing the user_role field.
Without user_role=PROXY_ADMIN, the system user couldn't bypass team permission checks
in can_team_member_execute_key_management_endpoint(), causing authorization failures
for team key rotation.
This fix adds user_role=LitellmUserRoles.PROXY_ADMIN to the system user, allowing
it to bypass team permission checks and successfully rotate keys for all teams.
* test: Add unit test for system user PROXY_ADMIN role
- Verify internal jobs system user has PROXY_ADMIN role
- Critical for key rotation to bypass team permission checks
- Regression test for PR #21896
* fix: enforce RBAC on agent endpoints — block non-admin create/update/delete
- Add /v1/agents/{agent_id} to agent_routes so internal users can
access GET-by-ID (previously returned 403 due to missing route pattern)
- Add _check_agent_management_permission() guard to POST, PUT, PATCH,
DELETE agent endpoints — only PROXY_ADMIN may mutate agents
- Add user_api_key_dict param to delete_agent so the role check works
- Add comprehensive unit tests for RBAC enforcement across all roles
Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>
* fix: mock prisma_client in internal user get-agent-by-id test
Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>
* feat(ui): hide agent create/delete controls for non-admin users
Match MCP servers pattern: wrap '+ Add New Agent' button in
isAdmin conditional so internal users see a read-only agents view.
Delete buttons in card and table were already gated.
Update empty-state copy for non-admin users.
Add 7 Vitest tests covering role-based visibility.
Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>
Commit 99c62ca40e removed "azure" from _RESPONSES_API_PROVIDERS,
routing Azure models through litellm.completion instead of
litellm.responses. The test was not updated to match, causing it
to assert against the wrong mock.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tests were asserting no response.create/conversation.item.create sent to
backend when guardrail blocks, but the implementation intentionally sends
these to have the LLM voice the guardrail violation message to the user.
Updated assertions to verify the correct guardrail flow:
- response.cancel is sent to stop any in-progress response
- conversation.item.create with violation message is injected
- response.create is sent to voice the violation
- original blocked content is NOT forwarded
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tests were mocking the old method name `filter_server_ids_by_ip` but production
code at server.py:774 calls `filter_server_ids_by_ip_with_info` which returns
a (server_ids, blocked_count) tuple. The unmocked method on AsyncMock returned
a coroutine, causing "cannot unpack non-iterable coroutine object" errors.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a gunicorn worker exits (e.g. from max_requests recycling), its
per-process prometheus .db files remain on disk. For gauges using
livesum/liveall mode, this means the dead worker's last-known values
persist as if the process were still alive. Wire gunicorn's child_exit
hook to call mark_process_dead() so live-tracking gauges accurately
reflect only running workers.
* fix: add missing LiteLLM_ClaudeCodePluginTable to schema.prisma
- Claude Code Plugin Marketplace endpoints (/claude-code/marketplace.json,
/claude-code/plugins) were returning 500 errors because
LiteLLM_ClaudeCodePluginTable model was missing from both schema.prisma files
- Prisma client was generated without this table causing AttributeError:
'Prisma' object has no attribute 'litellm_claudecodeplugintable'
- Added missing model definition to root schema.prisma and
litellm/proxy/schema.prisma
Fixes#21310
* test: add regression test for LiteLLM_ClaudeCodePluginTable schema
* fix: address greptile review - add @updatedAt, clean up test imports
The /project/list endpoint was not returning created_at and updated_at timestamps because these fields were not defined in LiteLLM_ProjectTable. Added these fields to the model so FastAPI includes them in the response (values come from the database). This allows the UI to display project creation and last-updated times.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Replace the hardcoded NATIVE_OPENROUTER_MODELS set approach with a
pattern-based check in _get_openai_compatible_provider_info: after
stripping the outer "openrouter/" provider prefix, if the remaining
model name still starts with "openrouter/", return immediately without
further stripping.
This fixes openrouter/openrouter/aurora-alpha, openrouter/openrouter/polaris-alpha,
and any future native OpenRouter models — not just the three hard-coded
ones (auto, free, bodybuilder) from the previous approach.
Fixes#16353
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>