Commit graph

4669 commits

Author SHA1 Message Date
yuneng-jiang
528b21ad1d [Feature] Allow team members to view entire team usage
Add a new team member permission `/team/daily/activity` that allows
non-admin team members to see all team usage data, not just their own.

## Changes
- Add `TEAM_DAILY_ACTIVITY` to `KeyManagementRoutes` enum and available
  team member permissions
- Add `_team_member_has_permission` helper in `common_utils.py`
- Modify `/team/daily/activity` endpoint to skip API key filtering when
  the member has this permission
- Add permission description and method detection in the UI
- Add backend and frontend tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-18 21:38:40 -08:00
jquinter
2c1d2fa948
Merge pull request #21439 from BerriAI/fix/restore-default-internal-user-params
fix(test): restore default_internal_user_params to None instead of delattr
2026-02-19 02:19:10 -03:00
jquinter
53cfb93a06
Merge pull request #21536 from BerriAI/fix/mcp-test-failures-from-user-permissions
fix(tests): update MCP tests broken by user permissions commit (#21462)
2026-02-19 02:17:49 -03:00
Julio Quinteros Pro
a9d3c49572 fix(tests): update MCP tests broken by user permissions commit (#21462)
Four tests were broken by commit e00c181f0c (Mcp user permissions #21462):

1. test_list_tools_single_server_unprefixed_names: The commit changed
   _get_tools_from_mcp_servers to always add server prefixes (add_prefix=True),
   removing the conditional that skipped prefixing for single servers.
   Updated assertion from "toolA" → "zapier-toolA".

2. test_mcp_get_prompt_success: mcp_get_prompt now extracts the server name
   from a prefixed prompt name via split_server_prefix_from_name(). Passing
   unprefixed "hello" returns server_name="" which matches no server → 403.
   Updated call to use "server_a-hello" so the server lookup succeeds.

3. test_e2e_jwt_team_mcp_permissions_enforced &
4. test_e2e_jwt_team_mcp_key_intersection:
   The commit replaced `from typing import List` with
   `from litellm.proxy.proxy_server import general_settings` in
   MCPRequestHandler.get_allowed_mcp_servers(). Both tests mock
   litellm.proxy.proxy_server with a types.ModuleType that lacked
   general_settings, causing ImportError. Added general_settings={} to
   both mock modules.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 02:09:11 -03:00
Julio Quinteros Pro
063238a789 chore: resolve merge conflict with main
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 01:46:13 -03:00
Julio Quinteros Pro
68b83b1376 fix(tests): restore litellm.model_cost after TestPriceDataReloadIntegration tests
test_complete_reload_flow and test_distributed_reload_check_function both
trigger code paths that assign a minimal stub dict to litellm.model_cost
(via the /reload/model_cost_map endpoint and _check_and_reload_model_cost_map).
Without restoring, subsequent tests in the same worker can't find gpt-4o
pricing and calculate spend=0.0 instead of the expected value.

Added try/finally save-and-restore of litellm.model_cost in both tests,
matching the pattern used in test_reload_model_cost_map_admin_access.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 01:42:48 -03:00
Krish Dholakia
e00c181f0c
Mcp user permissions (#21462)
* feat(schema.prisma): add object permissions for end users

allows controlling if end user can call specific mcp servers

* feat: cleanup for customer_endpoints support of object permission id

* fix: cleanup str

* feat(customers/): enforce end user can only call allowed mcps - if configured

* docs: document customer/end user object permission usage

* feat: enforce end user permissions on MCP tool calls

This commit implements end user permission enforcement for MCP servers:

1. Always add server prefixes to MCP tool names
   - Removed conditional logic that only added prefixes when multiple servers existed
   - Now always adds server prefix for consistent tool naming across all scenarios
   - Updated 5 locations in server.py (list_tools, get_prompts, get_resources,
     get_resource_templates, get_prompt)

2. Created MCP End User Permission Guardrail Hook
   - New guardrail hook: litellm/proxy/guardrails/guardrail_hooks/mcp_end_user_permission.py
   - Runs on post_call to validate tool calls in LLM responses
   - Extracts MCP server name from tool names (splits on first '-')
   - Checks if end_user_id has permissions for the MCP server
   - Raises GuardrailRaisedException if end user lacks permission
   - Supports both streaming and non-streaming responses

3. Added comprehensive tests
   - Test file: tests/test_litellm/proxy/guardrails/guardrail_hooks/test_mcp_end_user_permission.py
   - Tests cover: authorized/unauthorized tools, non-MCP tools, no end_user scenarios
   - Tests permission checking logic and exception raising

The hook integrates with the existing MCPRequestHandler._get_allowed_mcp_servers_for_end_user
to fetch end user permissions and enforce access control at the response level.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* refactor: remove redundant add_prefix variable assignments

Simplified the code by removing intermediate `add_prefix` variable
assignments and passing `True` directly to function calls since
we now always add server prefixes.

Changes:
- Removed `add_prefix = True` variable assignments in 5 locations
- Changed `add_prefix=add_prefix` to `add_prefix=True` in function calls
- Added inline comments to clarify the behavior

This makes the code more concise and clearer in intent.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* feat(auth_utils.py): support safety_identifier as a valid way of passing the end user id for responses api

* feat(llms): ensure 'tools' is correctly updated for responses api

* fix: fix greptile feedback

* feat: transformation.py

proper responses api tool handling for guardrail translation layer

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-18 18:53:59 -08:00
Krish Dholakia
a385126a7c Litellm dev compliance UI (#21457)
* feat(ui/): initial commit adding a compliance testing playground

allow proxy admins to test policies and guardrails against datasets

* feat(ui/): make score more friendly

* feat(policy_endpoints.py): new helper function for testing policies

* feat(policy_endpoints.py): expose new endpoint for testing policies and guardrails

enables compliance playground to work as expected

* feat(complianceui.tsx): show returned text
2026-02-18 18:22:43 -08:00
Ishaan Jaff
323aed7211
fix: CI failures - missing env key doc + streaming test (#21510)
* docs: add DATABRICKS_API_KEY to environment settings reference

* fix: streaming test usage check on Pydantic model

* fix: mock litellm.proxy.proxy_server in test_skip_server_startup
2026-02-18 18:20:32 -08:00
yuneng-jiang
2c6095bdf2
Merge pull request #21511 from BerriAI/litellm_store_model_in_db_from_database
[Feature] Allow store_model_in_db to be set via database
2026-02-18 17:43:56 -08:00
yuneng-jiang
64fe3a502e
Merge pull request #21503 from BerriAI/litellm_sso_role_map
fix(sso): preserve SSO role regardless of role_mappings config
2026-02-18 17:31:49 -08:00
yuneng-jiang
6356db560d [Feature] Allow store_model_in_db to be set via database
Users had to set store_model_in_db in the config YAML and restart the proxy,
causing service downtime. This change allows the value to be written to the
LiteLLM_Config table and read from the database at runtime, with DB values
overriding config file values.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-18 17:25:39 -08:00
Shivam Rawat
9ceaa2cbb0
tests and route permissions (#21508) 2026-02-18 16:58:38 -08:00
Ishaan Jaff
6486db3646
fix: improve streaming proxy throughput by fixing middleware and logging bottlenecks (#21501)
* fix(middleware): replace BaseHTTPMiddleware with pure ASGI middleware

BaseHTTPMiddleware wraps streaming responses with receive_or_disconnect
per chunk, blocking the event loop and causing severe throughput
degradation under concurrent streaming load (53% of CPU in profiling).

Converts PrometheusAuthMiddleware to a pure ASGI middleware using the
__call__(scope, receive, send) protocol.

* fix(streaming): remove expensive debug logging and optimize usage stripping

- Remove print_verbose calls that format chunk/response Pydantic objects,
  triggering millions of __repr__ calls (8% of CPU in profiling)
- Guard remaining verbose_logger.debug with isEnabledFor(DEBUG) and use
  lazy %s formatting instead of f-strings
- Replace usage stripping round-trip (model_dump + delete + reconstruct)
  with a _usage_stripped flag, deferring exclusion to serialization time

* fix(proxy): remove per-chunk debug log and use _usage_stripped flag

- Remove verbose_proxy_logger.debug that formatted every streaming chunk
- Honor _usage_stripped flag from streaming handler to exclude usage
  during model_dump_json serialization instead of reconstructing objects

* fix(proxy): remove per-chunk debug log in async_data_generator

Remove verbose_proxy_logger.debug that formatted every streaming chunk,
which triggered expensive Pydantic serialization on the hot path.

* fix indentation and add clarifying comment for usage stripping

* fix: guard calculate_total_usage against None usage in chunks

* fix: store chunk copy to preserve usage for calculate_total_usage
2026-02-18 16:16:49 -08:00
yuneng-jiang
e445694a40 fix(sso): preserve SSO role regardless of role_mappings configuration
When `default_internal_user_params` was set, `insert_sso_user()` only
preserved SSO-provided roles if `role_mappings` was explicitly configured.
Roles from other valid SSO sources (Microsoft app_roles,
GENERIC_USER_ROLE_ATTRIBUTE, custom SSO handlers) were silently
overwritten with the default "internal_user" role, causing admin users
to be downgraded on first login or after user deletion.

Replace the `role_mappings_configured` gate with `_should_use_role_from_sso_response()`
which validates the role is a recognized LitellmUserRoles value regardless
of origin. Also removes an unnecessary DB round-trip to litellm_ssoconfig
on every new SSO user creation.

Fixes: admin users seeing internal-user UI after SSO login
Fixes: test_get_redirect_url_for_sso flaking due to local env vars

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-18 15:24:18 -08:00
jquinter
a9058bb584
Merge pull request #21483 from BerriAI/fix/test-delattr-default-internal-user-params
fix(tests): restore default_internal_user_params instead of delattr-ing it
2026-02-18 19:07:47 -03:00
Julio Quinteros Pro
7b6ffbb52a fix(tests): wrap callbacks cleanup in try/finally and resolve merge conflict
- test_litellm_pre_call_utils.py: wrap test body in try/finally so
  litellm.callbacks is always restored even when an assertion fails,
  addressing greptile review comment
- test_langfuse_otel.py: resolve trivial merge conflict in comment
  ("unpatched" vs "unpatch-ed"), keeping correct spelling

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18 18:50:17 -03:00
Julio Quinteros Pro
1c0f4302f8 fix(tests): restore litellm.model_cost after reload endpoint test
test_reload_model_cost_map_admin_access calls the /reload/model_cost_map
HTTP endpoint with get_model_cost_map mocked to return a single-entry
dict. The endpoint handler does a direct module-level assignment
(litellm.model_cost = new_model_cost_map) which persists after the
patch context manager exits, stripping all models except gpt-3.5-turbo
from the in-memory cost map and causing subsequent tests that rely on
models like gemini-1.5-flash, multimodalembedding@001, and gpt-4o to
fail with "model not mapped" errors or zero-cost spend payloads.

Fix: save litellm.model_cost before the test and restore it (along with
invalidating the case-insensitive lookup cache) in a finally block.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18 18:20:48 -03:00
Shivam Rawat
7f655be191
Merge branch 'main' into litellm_budget_tier_enforcement_for_keys 2026-02-18 11:24:24 -08:00
Ryan Crabbe
f0a542c55d fix: add .copy() to create_request_copy and tests for header caching
Protect cached headers from mutation in create_request_copy sites and
add unit tests for _safe_get_request_headers caching behavior.
2026-02-18 09:44:35 -08:00
Julio Quinteros Pro
2e0a8b3cf8 fix(tests): resolve MCP test isolation failures in parallel execution
Three test isolation issues fixed:

1. test_mcp_debug.py: Replace deprecated asyncio.get_event_loop().run_until_complete()
   with asyncio.run() in TestWrapSendWithDebugHeaders. In Python 3.10+,
   get_event_loop() raises RuntimeError when no event loop is set in the
   current thread, causing test_injects_headers and test_body_messages_unchanged
   to fail in isolation.

2. test_mcp_server_manager.py: After _reload_mcp_manager_module() creates a new
   global_mcp_server_manager instance, server.py still holds a stale reference
   to the old instance. Tests in test_mcp_server.py that populate the new
   manager's registry and then call server.py functions (e.g. _get_tools_from_mcp_servers)
   get empty results because server.py reads from the old manager. Fix: update
   server.py's module-level reference after each reload.

3. test_litellm_pre_call_utils.py: test_add_litellm_metadata_from_request_headers
   sets litellm.callbacks without restoring it afterward. Add cleanup to restore
   original callbacks after the test to prevent state leaking to subsequent tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18 14:08:48 -03:00
Julio Quinteros Pro
f542f92026 fix(tests): restore default_internal_user_params instead of delattr-ing it
Four finally blocks in test_internal_user_endpoints.py and one in
test_ui_sso.py used the pattern:

    if original_default_params is not None:
        litellm.default_internal_user_params = original_default_params
    else:
        delattr(litellm, "default_internal_user_params")

Since the attribute is defined in litellm/__init__.py with a default of
None, `getattr(litellm, "default_internal_user_params", None)` returns
None. The else branch then calls delattr(), permanently removing the
attribute from the module for the rest of the process.

Subsequent tests in the same pytest-xdist worker (e.g.
test_add_new_member_* in test_management_helpers_utils.py) then fail
with: AttributeError: module 'litellm' has no attribute
'default_internal_user_params'

Fix: replace all five flawed finally blocks with a simple assignment:
    litellm.default_internal_user_params = original_default_params

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18 13:33:53 -03:00
Harshit Jain
3949aac2d7
fix identation error by greplite 2026-02-18 21:55:56 +05:30
jquinter
34f06c4242
Merge pull request #21473 from BerriAI/fix/test-proxy-admin-expired-key-cleanup
fix(tests): restore proxy_server module attrs after test_proxy_admin_expired_key_from_cache
2026-02-18 11:22:03 -03:00
jquinter
0d11720586
Update tests/test_litellm/proxy/auth/test_user_api_key_auth.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-02-18 11:21:53 -03:00
Sameer Kankute
610bf00768 Fix:test_get_key_object_loads_object_permission 2026-02-18 19:18:15 +05:30
Julio Quinteros Pro
30d4f344ae fix(tests): restore proxy_server module attrs in test_proxy_admin_expired_key_from_cache
The test was using setattr() to set module-level attributes (including
proxy_logging_obj = MagicMock()) on the real litellm.proxy.proxy_server
module, but the finally block only had `pass` — no cleanup.

This left proxy_logging_obj as a MagicMock in subsequent tests running
in the same pytest-xdist worker, causing TypeError when log_db_metrics
decorator called asyncio.create_task(proxy_logging_obj.service_logging_obj
.async_service_success_hook(...)) — a MagicMock is not a coroutine.

Fix: save original attribute values before the test and restore them in
the finally block to ensure test isolation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18 09:33:11 -03:00
Sameer Kankute
aa255c7e63
Merge pull request #21361 from BerriAI/litellm_oss_staging_02_17_2026
Litellm oss staging 02 17 2026
2026-02-18 17:48:15 +05:30
Sameer Kankute
bd0c80406f
Merge pull request #21326 from BerriAI/litellm_oss_staging_02_16_2026
Litellm oss staging 02 16 2026
2026-02-18 17:47:57 +05:30
Sameer Kankute
6a42e997ec
Merge pull request #21469 from BerriAI/revert-21226-fix/pod-lock-atomic-release-delete
Revert "fix: make PodLockManager.release_lock atomic compare-and-delete"
2026-02-18 17:26:43 +05:30
Sameer Kankute
eb8b991260
Merge branch 'main' into litellm_oss_staging_02_17_2026 2026-02-18 17:26:33 +05:30
Sameer Kankute
53dcebc37a
Revert "fix(pod-lock): make release lock compare-and-delete atomic (#21226)"
This reverts commit f162371b93.
2026-02-18 17:24:34 +05:30
Julio Quinteros Pro
77d1f6932e fix(test): restore default_internal_user_params to None instead of delattr
test_role_mappings_override_default_internal_user_params was calling
delattr(litellm, 'default_internal_user_params') in its finally block
when the original value was None. This removes the attribute entirely from
the module, causing subsequent tests in the same xdist worker to get
AttributeError when accessing litellm.default_internal_user_params (because
litellm.__getattr__ has no handler for this name).

Fix: always restore the attribute by assignment (litellm.default_internal_user_params = original_default_params)
rather than deleting it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18 08:18:13 -03:00
Atharva Jaiswal
42afba9cdd
Fix invalid OpenAPI schema for /spend/calculate and /credentials endpoints (#21369)
- /spend/calculate: wrap response in proper OpenAPI 3.x content structure
- /credentials: split stacked route decorators into separate handlers to
  eliminate path parameter conflict between by_name and by_model routes
2026-02-17 22:59:01 -08:00
Krish Dholakia
14a35a12cb
Revert "End users - Allow giving end users access to specific mcp servers (#…" (#21461)
This reverts commit 1f521be0f2.
2026-02-17 22:49:57 -08:00
Krish Dholakia
1f521be0f2
End users - Allow giving end users access to specific mcp servers (#21411)
* feat(schema.prisma): add object permissions for end users

allows controlling if end user can call specific mcp servers

* feat: cleanup for customer_endpoints support of object permission id

* fix: cleanup str

* feat(customers/): enforce end user can only call allowed mcps - if configured

* docs: document customer/end user object permission usage

* feat: address greptile comments
2026-02-17 22:45:49 -08:00
Ishaan Jaff
371cabfebd
Add MCP Security guardrail to block unregistered MCP servers (#21429)
* Add MCP_SECURITY enum to SupportedGuardrailIntegrations

* Add MCP security guardrail initializer

* Add MCPSecurityGuardrail implementation

* Add MCP Security policy template

* Add Type filter to policy templates UI

* Add unit tests for MCP security guardrail

* fix(lint): remove unused Dict import from mcp_security_guardrail

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add French language support for EU AI Act Article 5 guardrail (#21427)

* Add French language support for EU AI Act Article 5 template

- Create eu_ai_act_article5_fr.yaml with comprehensive French keywords
- Includes identifier words: concevoir, créer, développer, noter, classer, etc.
- Includes block words: crédit social, comportement social, émotion des employés, etc.
- Includes always-block keywords for explicit prohibited practices
- Includes exceptions for research, compliance, and legitimate use cases
- Catches circumvention attempts with phrase variations

* Add comprehensive tests for French EU AI Act guardrail

- Test 3 critical scenarios: blocked query, circumvention attempt, safe query
- Test edge cases: case-insensitive, mixed language, research exceptions
- All 7 tests passing
- Validates both blocking and allowing behavior

* Fix content filter to support conditional matching without inherit_from

- Enable conditional matching when identifier_words + additional_block_words are present
- Previously required inherit_from, but EU AI Act templates are self-contained
- Fixes Greptile feedback: conditional matching now works as documented

* Add pure conditional matching test for French guardrail

- Test identifier + block word combinations not in always_block_keywords
- Verifies conditional matching works independently
- Addresses Greptile feedback about test coverage gap

* Fix exception word bypass risk in French template

- Replace short words (film, jeu, juste) with context-specific phrases
- Prevents substring matching bypasses (e.g., enjeu matching jeu)
- Add tests for bypass prevention and legitimate game context
- Addresses Greptile security feedback

* Make conditional match assertion more robust

- Use getattr to safely access exception detail field
- Check if detail is dict before calling .get()
- Addresses Greptile feedback about brittle string assertion

* Add French EU AI Act Article 5 policy template to registry

- Add eu-ai-act-article5-fr template for French language support
- Includes French description and guardrail info
- Matches structure of English template

* Address greptile review feedback (greploop iteration 1)

- Use status_code=400 instead of 403 to match guardrail logging convention
- Use prefix stripping instead of split('/')[-1] for robust server name extraction

* remove French EU AI Act template from policy_templates.json

---------

Co-authored-by: Julio Quinteros Pro <jquinter@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-17 21:19:13 -08:00
Ishaan Jaff
5946a933a0
Add compliance checker endpoints + UI panel (#21432)
* eu_ai_act_article5_prohibited_practices_fr

* add backend for checkers

* test checker

* checkEuAiActCompliance

* register compliance router in proxy_server.py

* add compliance check functions to networking.tsx

* fix useEffect dependency array in CompliancePanel

* ui fixes
2026-02-17 18:22:26 -08:00
Ishaan Jaffer
a6f467e896 fixes - showing content filter on failure 2026-02-17 17:57:52 -08:00
Ishaan Jaff
24fcc9da7c
fix: session grouping broken for dict rows from query_raw (#21435)
* fix: session grouping for dict rows from query_raw

* test: add unit test for session count enrichment with dict rows
2026-02-17 17:21:17 -08:00
Julio Quinteros Pro
e6abb865d3 fix: properly reload litellm in setup_and_teardown fixture
Use importlib.import_module + reload uniformly in both code paths
to ensure fresh module state regardless of whether litellm was
previously in sys.modules. This fixes the inconsistency where the
"not in sys.modules" branch didn't reload the module.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-17 21:29:13 -03:00
Julio Quinteros Pro
77f315eb11 fix: address Greptile review feedback for test isolation
- test_pillar_guardrails.py: Fix fixture to properly update module-level
  litellm reference using global keyword and assignment from reload
- test_anthropic_experimental_pass_through_messages_handler.py: Add missing
  assert keywords to kwargs comparison statements (lines 36, 60-62)
- test_proxy_server.py: Replace silent pytest.skip with explicit assertion
  to catch router initialization regressions

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-17 21:28:23 -03:00
Ryan Crabbe
3275fb09cb fix: add State() to mock requests in http_parsing_utils tests
The _safe_get_request_headers caching uses request.state._cached_headers,
which returns a truthy MagicMock on bare MagicMock() objects instead of
None, breaking content-type detection for form-data tests.
2026-02-17 16:01:08 -08:00
Shivam Rawat
c45a17bad4
Merge branch 'main' into litellm_budget_tier_enforcement_for_keys 2026-02-17 16:00:04 -08:00
Shivam Rawat
c0e87f7ffb
fixed byok models for teams issue (#21408) 2026-02-17 15:26:03 -08:00
Sameer Kankute
1ced47c612 fix tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py 2026-02-17 20:14:56 +05:30
Sameer Kankute
fe20e66a1d Fix : test_exception_without_scanners 2026-02-17 20:12:02 +05:30
Sameer Kankute
8374b4d939 Fix : test_exception_without_scanners 2026-02-17 20:11:20 +05:30
Sameer Kankute
1c2e1148f3
Merge branch 'main' into litellm_oss_staging_02_16_2026 2026-02-17 18:24:56 +05:30
yuneng-jiang
8576683f39
Update tests/test_litellm/proxy/management_endpoints/test_key_management_endpoints.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-02-16 21:09:13 -08:00