From ce421df1ef477bf8c1035fbb574fd484a9dffbb4 Mon Sep 17 00:00:00 2001 From: Neel Harsola <81899840+skylarkoo7@users.noreply.github.com> Date: Wed, 11 Feb 2026 12:17:03 +0530 Subject: [PATCH] fix(azure): preserve content_policy_violation error details from Azure OpenAI (#20883) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add opus 4.5 and 4.6 to use outout_format param * generate poetry lock with 2.3.2 poetry * restore poetry lock * e2e tests, key delete, update tpm rpm, and regenerate * Split e2e ui testing for browser * new login with sso button in login page * option to hide usage indicator * fix(cloudzero): update CBF field mappings per LIT-1907 (#20906) * fix(cloudzero): update CBF field mappings per LIT-1907 Phase 1 field updates for CloudZero integration: ADD/UPDATE: - resource/account: Send concat(api_key_alias, '|', api_key_prefix) - resource/service: Send model_group instead of service_type - resource/usage_family: Send provider instead of hardcoded 'llm-usage' - action/operation: NEW - Send team_id - resource/id: Send model name instead of CZRN - resource/tag:organization_alias: Add if exists - resource/tag:project_alias: Add if exists - resource/tag:user_alias: Add if exists REMOVE: - resource/tag:total_tokens: Removed - resource/tag:team_id: Removed (team_id now in action/operation) Fixes LIT-1907 * Update litellm/integrations/cloudzero/transform.py Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * fix: define api_key_alias variable, update CBFRecord docstring - Fix F821 lint error: api_key_alias was used but not defined - Update CBFRecord docstring to reflect LIT-1907 field mappings - Remove unused Optional import --------- Co-authored-by: Ishaan Jaff Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * Add banner notifying of breaking change * Add semgrep & Fix OOMs (#20912) * [Feat] Policies - Allow connecting Policies to Tags, Simulating Policies, Viewing how many keys, teams it applies on (#20904) * init schema with TAGS * ui: add policy test * resolvePoliciesCall * add_policy_sources_to_metadata + headers * types Policy * preview Impact * def _describe_match_reason( * match based on TAGs * TestTagBasedAttachments * test fixes * add policy_resolve_router * add_guardrails_from_policy_engine * TestMatchAttribution * refactor * fix * fix: address Greptile review feedback on policy resolve endpoints - Track unnamed keys/teams as separate counts instead of inflating affected_keys_count with duplicate "(unnamed key)" placeholders. Added unnamed_keys_count and unnamed_teams_count to response. - Push alias pattern matching to DB via _build_alias_where() which converts exact patterns to Prisma "in" and suffix wildcards to "startsWith" filters. - Gate sync_policies_from_db/sync_attachments_from_db behind force_sync query param (default false) to avoid 2 DB round-trips on every /policies/resolve request. - Remove worktree-only conftest.py that cleared sys.modules at import time — no longer needed since code moved to main repo. - Rename MAX_ESTIMATE_IMPACT_ROWS → MAX_POLICY_ESTIMATE_IMPACT_ROWS. Co-Authored-By: Claude Opus 4.6 * fix: eliminate duplicate DB queries and fix header delimiter ambiguity - Fetch teams table once in estimate_attachment_impact and reuse for both tag-based and alias-based lookups (was querying teams twice when both tag_patterns and team_patterns were provided). - Convert tag/team filter functions from async DB queries to sync filters that operate on pre-fetched data (_filter_keys_by_tags, _filter_teams_by_tags). - Fix comma ambiguity in x-litellm-policy-sources header: use '; ' as entry delimiter since matched_via values can contain commas. - Use '+' as the within-value separator in matched_via reason strings (e.g. "tag:healthcare+team:health-team") to avoid conflict with header delimiters. Co-Authored-By: Claude Opus 4.6 * Update litellm/proxy/policy_engine/policy_resolve_endpoints.py Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --------- Co-authored-by: Claude Opus 4.6 Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * fix: type error & better error handling (#20689) * [Docs] Add docs guide for using policies (#20914) * init schema with TAGS * ui: add policy test * resolvePoliciesCall * add_policy_sources_to_metadata + headers * types Policy * preview Impact * def _describe_match_reason( * match based on TAGs * TestTagBasedAttachments * test fixes * add policy_resolve_router * add_guardrails_from_policy_engine * TestMatchAttribution * refactor * fix * fix: address Greptile review feedback on policy resolve endpoints - Track unnamed keys/teams as separate counts instead of inflating affected_keys_count with duplicate "(unnamed key)" placeholders. Added unnamed_keys_count and unnamed_teams_count to response. - Push alias pattern matching to DB via _build_alias_where() which converts exact patterns to Prisma "in" and suffix wildcards to "startsWith" filters. - Gate sync_policies_from_db/sync_attachments_from_db behind force_sync query param (default false) to avoid 2 DB round-trips on every /policies/resolve request. - Remove worktree-only conftest.py that cleared sys.modules at import time — no longer needed since code moved to main repo. - Rename MAX_ESTIMATE_IMPACT_ROWS → MAX_POLICY_ESTIMATE_IMPACT_ROWS. Co-Authored-By: Claude Opus 4.6 * fix: eliminate duplicate DB queries and fix header delimiter ambiguity - Fetch teams table once in estimate_attachment_impact and reuse for both tag-based and alias-based lookups (was querying teams twice when both tag_patterns and team_patterns were provided). - Convert tag/team filter functions from async DB queries to sync filters that operate on pre-fetched data (_filter_keys_by_tags, _filter_teams_by_tags). - Fix comma ambiguity in x-litellm-policy-sources header: use '; ' as entry delimiter since matched_via values can contain commas. - Use '+' as the within-value separator in matched_via reason strings (e.g. "tag:healthcare+team:health-team") to avoid conflict with header delimiters. Co-Authored-By: Claude Opus 4.6 * docs v1 guide with UI imgs * docs fix --------- Co-authored-by: Claude Opus 4.6 * feat: add dashscope/qwen3-max model with tiered pricing (#20919) Add support for Alibaba Cloud's Qwen3-Max model with: - 258K input tokens, 65K output tokens - Tiered pricing based on context window usage (0-32K, 32K-128K, 128K-252K) - Function calling and tool choice support - Reasoning capabilities enabled Co-authored-by: Claude Sonnet 4.5 * fix linting * docs: add Greptile review requirement to PR template (#20762) * fix(azure): preserve content_policy_violation error details from Azure OpenAI Closes #20811 Azure OpenAI returns rich error payloads for content policy violations (inner_error with ResponsibleAIPolicyViolation, content_filter_results, revised_prompt). Previously these details were lost when: 1. The top-level error code was not "content_policy_violation" but the inner_error.code was "ResponsibleAIPolicyViolation" -- the structured check only examined the top-level code. 2. The DALL-E image generation polling path stringified the error JSON into the message field instead of setting the structured body, making it impossible for exception_type() to extract error details. 3. The string-based fallback detector used "invalid_request_error" as a content-policy indicator, which is too broad and could misclassify regular bad-request errors. Changes: - exception_mapping_utils.py: Check inner_error.code for ResponsibleAIPolicyViolation when top-level code is not content_policy_violation. Replace overly broad "invalid_request_error" string match with specific Azure safety-system messages. - azure.py: Set structured body on AzureOpenAIError in both async and sync DALL-E polling paths so exception_type() can inspect error details. - test_azure_exception_mapping.py: Add regression tests covering the exact error payloads from issue #20811. - Fix pre-existing lint: duplicate PerplexityResponsesConfig dict key, unused RouteChecks top-level import. --------- Co-authored-by: Kelvin Tran Co-authored-by: yuneng-jiang Co-authored-by: shin-bot-litellm Co-authored-by: Ishaan Jaff Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Co-authored-by: Alexsander Hamir Co-authored-by: Claude Opus 4.6 Co-authored-by: Harshit Jain <48647625+Harshit28j@users.noreply.github.com> Co-authored-by: ken <122603020@qq.com> Co-authored-by: Sameer Kankute --- .circleci/config.yml | 54 ++- .github/pull_request_template.md | 1 + .semgrep/rules/README.md | 22 + .semgrep/rules/python/unbounded-memory.yml | 14 + cookbook/nova_sonic_realtime.py | 6 +- docs/my-website/docs/proxy/config_settings.md | 3 +- .../proxy/guardrails/guardrail_policies.md | 115 ++++- .../docs/proxy/guardrails/policy_tags.md | 139 ++++++ docs/my-website/img/policy_team_attach.png | Bin 0 -> 230432 bytes docs/my-website/img/policy_test_matching.png | Bin 0 -> 205179 bytes docs/my-website/sidebars.js | 91 ++-- .../litellm_proxy_extras/schema.prisma | 1 + litellm/constants.py | 7 + litellm/integrations/cloudzero/transform.py | 30 +- litellm/integrations/gcs_bucket/gcs_bucket.py | 14 +- .../exception_mapping_utils.py | 19 +- litellm/llms/anthropic/chat/transformation.py | 4 + litellm/llms/azure/azure.py | 30 +- ...odel_prices_and_context_window_backup.json | 48 +++ litellm/proxy/common_utils/callback_utils.py | 29 ++ .../db_transaction_queue/base_update_queue.py | 8 +- .../daily_spend_update_queue.py | 3 +- .../spend_update_queue.py | 5 +- .../ui_discovery_endpoints.py | 6 +- .../guardrails/guardrail_hooks/presidio.py | 61 ++- litellm/proxy/litellm_pre_call_utils.py | 36 +- .../policy_engine/attachment_registry.py | 62 ++- .../proxy/policy_engine/policy_endpoints.py | 34 +- litellm/proxy/policy_engine/policy_matcher.py | 13 + .../proxy/policy_engine/policy_registry.py | 1 + .../policy_engine/policy_resolve_endpoints.py | 405 ++++++++++++++++++ litellm/proxy/proxy_server.py | 4 + litellm/proxy/schema.prisma | 1 + litellm/types/integrations/cloudzero.py | 16 +- .../ui_discovery_endpoints.py | 1 + litellm/types/proxy/policy_engine/__init__.py | 9 + .../types/proxy/policy_engine/policy_types.py | 31 +- .../proxy/policy_engine/resolver_types.py | 88 ++++ model_prices_and_context_window.json | 37 ++ schema.prisma | 1 + .../test_anthropic_chat_transformation.py | 87 +++- .../azure/test_azure_exception_mapping.py | 147 ++++++- .../test_ui_discovery_endpoints.py | 31 ++ .../guardrail_hooks/test_presidio.py | 175 ++++++-- .../policy_engine/test_attachment_registry.py | 133 ++++++ .../policy_engine/test_policy_matcher.py | 64 +++ ui/litellm-dashboard/e2e_tests/constants.ts | 5 + .../e2e_tests/tests/keys/deleteKey.spec.ts | 25 ++ .../tests/keys/regenerateKey.spec.ts | 21 + .../tests/keys/updateKeyLimits.spec.ts | 27 ++ .../scripts/e2e_tests/neonHelperScripts.ts | 17 +- .../app/(dashboard)/components/Sidebar2.tsx | 334 +++++++-------- .../hooks/uiConfig/useUIConfig.test.ts | 2 + .../(dashboard)/hooks/useAuthorized.test.ts | 5 + .../hooks/useDisableUsageIndicator.test.ts | 190 ++++++++ .../hooks/useDisableUsageIndicator.ts | 33 ++ .../src/app/login/LoginPage.test.tsx | 98 ++++- .../src/app/login/LoginPage.tsx | 33 +- .../components/AIHub/ModelHubTable.test.tsx | 2 + .../Navbar/UserDropdown/UserDropdown.tsx | 19 + .../src/components/UsageIndicator.test.tsx | 186 ++++++++ ...usage_indicator.tsx => UsageIndicator.tsx} | 7 +- .../src/components/leftnav.tsx | 4 +- .../src/components/networking.tsx | 63 +++ .../policies/add_attachment_form.tsx | 120 ++++-- .../components/policies/attachment_table.tsx | 29 ++ .../policies/build_attachment_data.ts | 25 ++ .../components/policies/impact_popover.tsx | 84 ++++ .../policies/impact_preview_alert.tsx | 61 +++ .../src/components/policies/index.tsx | 10 +- .../components/policies/policy_test_panel.tsx | 254 +++++++++++ .../src/components/policies/types.ts | 2 + .../src/components/usage_indicator.test.tsx | 86 ---- 73 files changed, 3371 insertions(+), 457 deletions(-) create mode 100644 .semgrep/rules/README.md create mode 100644 .semgrep/rules/python/unbounded-memory.yml create mode 100644 docs/my-website/docs/proxy/guardrails/policy_tags.md create mode 100644 docs/my-website/img/policy_team_attach.png create mode 100644 docs/my-website/img/policy_test_matching.png create mode 100644 litellm/proxy/policy_engine/policy_resolve_endpoints.py create mode 100644 ui/litellm-dashboard/e2e_tests/tests/keys/deleteKey.spec.ts create mode 100644 ui/litellm-dashboard/e2e_tests/tests/keys/regenerateKey.spec.ts create mode 100644 ui/litellm-dashboard/e2e_tests/tests/keys/updateKeyLimits.spec.ts create mode 100644 ui/litellm-dashboard/src/app/(dashboard)/hooks/useDisableUsageIndicator.test.ts create mode 100644 ui/litellm-dashboard/src/app/(dashboard)/hooks/useDisableUsageIndicator.ts create mode 100644 ui/litellm-dashboard/src/components/UsageIndicator.test.tsx rename ui/litellm-dashboard/src/components/{usage_indicator.tsx => UsageIndicator.tsx} (98%) create mode 100644 ui/litellm-dashboard/src/components/policies/build_attachment_data.ts create mode 100644 ui/litellm-dashboard/src/components/policies/impact_popover.tsx create mode 100644 ui/litellm-dashboard/src/components/policies/impact_preview_alert.tsx create mode 100644 ui/litellm-dashboard/src/components/policies/policy_test_panel.tsx delete mode 100644 ui/litellm-dashboard/src/components/usage_indicator.test.tsx diff --git a/.circleci/config.yml b/.circleci/config.yml index 34c3f05cd2..6c7bbddb9f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -112,6 +112,24 @@ jobs: python -m mypy . cd .. no_output_timeout: 10m + + semgrep: + docker: + - image: cimg/python:3.12 + auth: + username: ${DOCKERHUB_USERNAME} + password: ${DOCKERHUB_PASSWORD} + working_directory: ~/project + steps: + - checkout + - setup_google_dns + - run: + name: Install Semgrep + command: pip install semgrep + - run: + name: Run Semgrep (custom rules only) + command: semgrep scan --config .semgrep/rules . --error + local_testing_part1: docker: - image: cimg/python:3.12 @@ -3932,6 +3950,9 @@ jobs: image: ubuntu-2204:2023.10.1 resource_class: xlarge working_directory: ~/project + parameters: + browser: + type: string steps: - checkout - setup_google_dns @@ -3961,7 +3982,7 @@ jobs: echo "Expires at: $EXPIRES_AT" neon branches create \ --project-id $NEON_PROJECT_ID \ - --name preview/commit-${CIRCLE_SHA1:0:7} \ + --name preview/commit-${CIRCLE_SHA1:0:7}-<< parameters.browser >> \ --expires-at $EXPIRES_AT \ --parent br-fancy-paper-ad1olsb3 \ --api-key $NEON_API_KEY || true @@ -3971,7 +3992,7 @@ jobs: E2E_UI_TEST_DATABASE_URL=$(neon connection-string \ --project-id $NEON_PROJECT_ID \ --api-key $NEON_API_KEY \ - --branch preview/commit-${CIRCLE_SHA1:0:7} \ + --branch preview/commit-${CIRCLE_SHA1:0:7}-<< parameters.browser >> \ --database-name yuneng-trial-db \ --role neondb_owner) echo $E2E_UI_TEST_DATABASE_URL @@ -3983,7 +4004,7 @@ jobs: -e UI_USERNAME="admin" \ -e UI_PASSWORD="gm" \ -e LITELLM_LICENSE=$LITELLM_LICENSE \ - --name litellm-docker-database \ + --name litellm-docker-database-<< parameters.browser >> \ -v $(pwd)/litellm/proxy/example_config_yaml/simple_config.yaml:/app/config.yaml \ litellm-docker-database:ci \ --config /app/config.yaml \ @@ -3999,7 +4020,7 @@ jobs: sudo rm dockerize-linux-amd64-v0.6.1.tar.gz - run: name: Start outputting logs - command: docker logs -f litellm-docker-database + command: docker logs -f litellm-docker-database-<< parameters.browser >> background: true - run: name: Wait for app to be ready @@ -4008,6 +4029,7 @@ jobs: name: Run Playwright Tests command: | npx playwright test \ + --project << parameters.browser >> \ --config ui/litellm-dashboard/e2e_tests/playwright.config.ts \ --reporter=html \ --output=test-results @@ -4114,6 +4136,12 @@ workflows: only: - main - /litellm_.*/ + - semgrep: + filters: + branches: + only: + - main + - /litellm_.*/ - local_testing_part1: filters: branches: @@ -4213,6 +4241,20 @@ workflows: - main - /litellm_.*/ - e2e_ui_testing: + name: e2e_ui_testing_chromium + browser: chromium + context: e2e_ui_tests + requires: + - ui_build + - build_docker_database_image + filters: + branches: + only: + - main + - /litellm_.*/ + - e2e_ui_testing: + name: e2e_ui_testing_firefox + browser: firefox context: e2e_ui_tests requires: - ui_build @@ -4492,6 +4534,7 @@ workflows: - publish_to_pypi: requires: - mypy_linting + - semgrep - local_testing_part1 - local_testing_part2 - build_and_test @@ -4524,7 +4567,8 @@ workflows: - litellm_assistants_api_testing - auth_ui_unit_tests - db_migration_disable_update_check - - e2e_ui_testing + - e2e_ui_testing_chromium + - e2e_ui_testing_firefox - litellm_proxy_unit_testing_key_generation - litellm_proxy_unit_testing_part1 - litellm_proxy_unit_testing_part2 diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index b91b16c955..f13039f451 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -9,6 +9,7 @@ - [ ] I have Added testing in the [`tests/litellm/`](https://github.com/BerriAI/litellm/tree/main/tests/litellm) directory, **Adding at least 1 test is a hard requirement** - [see details](https://docs.litellm.ai/docs/extras/contributing_code) - [ ] My PR passes all unit tests on [`make test-unit`](https://docs.litellm.ai/docs/extras/contributing_code) - [ ] 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) diff --git a/.semgrep/rules/README.md b/.semgrep/rules/README.md new file mode 100644 index 0000000000..0dbb77cdd4 --- /dev/null +++ b/.semgrep/rules/README.md @@ -0,0 +1,22 @@ +# Custom Semgrep rules for LiteLLM + +Add custom rule YAML files here. Semgrep loads all `.yml`/`.yaml` files under this directory. + +**Run only custom rules (CI / fail on findings):** + +```bash +semgrep scan --config .semgrep/rules . --error +``` + +**Run with registry + custom rules:** + +```bash +semgrep scan --config auto --config .semgrep/rules . +``` + +**Layout:** + +- `python/` – Python-specific rules (security, patterns) +- Add more subdirs as needed (e.g. `generic/` for language-agnostic rules) + +See [Semgrep rule syntax](https://semgrep.dev/docs/writing-rules/rule-syntax/). diff --git a/.semgrep/rules/python/unbounded-memory.yml b/.semgrep/rules/python/unbounded-memory.yml new file mode 100644 index 0000000000..811ef68934 --- /dev/null +++ b/.semgrep/rules/python/unbounded-memory.yml @@ -0,0 +1,14 @@ +# Unbounded memory growth – data structures without a clear max limit +# Can lead to OOM under load. + +rules: + - id: unbounded-asyncio-queue + message: asyncio.Queue() with no maxsize can grow unbounded. Use asyncio.Queue(maxsize=N) for integrations (e.g. log queues). + severity: ERROR + languages: [python] + pattern-either: + - pattern: asyncio.Queue() + - pattern: asyncio.Queue(maxsize=0) + metadata: + category: correctness + cwe: "CWE-400: Uncontrolled Resource Consumption" \ No newline at end of file diff --git a/cookbook/nova_sonic_realtime.py b/cookbook/nova_sonic_realtime.py index 0ea0badfb0..c7a73c1d00 100644 --- a/cookbook/nova_sonic_realtime.py +++ b/cookbook/nova_sonic_realtime.py @@ -16,10 +16,14 @@ Usage: import asyncio import base64 import json +import os import pyaudio import websockets from typing import Optional +# Bounded queue size for audio chunks (configurable via env to avoid unbounded memory) +AUDIO_QUEUE_MAXSIZE = int(os.getenv("LITELLM_ASYNCIO_QUEUE_MAXSIZE", 10_000)) + # Audio configuration (matching Nova Sonic requirements) INPUT_SAMPLE_RATE = 16000 # Nova Sonic expects 16kHz input OUTPUT_SAMPLE_RATE = 24000 # Nova Sonic outputs 24kHz @@ -40,7 +44,7 @@ class RealtimeClient: self.api_key = api_key self.ws: Optional[websockets.WebSocketClientProtocol] = None self.is_active = False - self.audio_queue = asyncio.Queue() + self.audio_queue = asyncio.Queue(maxsize=AUDIO_QUEUE_MAXSIZE) self.pyaudio = pyaudio.PyAudio() self.input_stream = None self.output_stream = None diff --git a/docs/my-website/docs/proxy/config_settings.md b/docs/my-website/docs/proxy/config_settings.md index 5b6c6669b9..a6d4e3102a 100644 --- a/docs/my-website/docs/proxy/config_settings.md +++ b/docs/my-website/docs/proxy/config_settings.md @@ -395,7 +395,7 @@ router_settings: | ATHINA_API_KEY | API key for Athina service | ATHINA_BASE_URL | Base URL for Athina service (defaults to `https://log.athina.ai`) | AUTH_STRATEGY | Strategy used for authentication (e.g., OAuth, API key) -| AUTO_REDIRECT_UI_LOGIN_TO_SSO | Flag to enable automatic redirect of UI login page to SSO when SSO is configured. Default is **true** +| AUTO_REDIRECT_UI_LOGIN_TO_SSO | Flag to enable automatic redirect of UI login page to SSO when SSO is configured. Default is **false** | AUDIO_SPEECH_CHUNK_SIZE | Chunk size for audio speech processing. Default is 1024 | ANTHROPIC_API_KEY | API key for Anthropic service | ANTHROPIC_API_BASE | Base URL for Anthropic API. Default is https://api.anthropic.com @@ -784,6 +784,7 @@ router_settings: | LITELLM_USER_AGENT | Custom user agent string for LiteLLM API requests. Used for partner telemetry attribution | LITELLM_PRINT_STANDARD_LOGGING_PAYLOAD | If true, prints the standard logging payload to the console - useful for debugging | LITELM_ENVIRONMENT | Environment for LiteLLM Instance. This is currently only logged to DeepEval to determine the environment for DeepEval integration. +| LITELLM_ASYNCIO_QUEUE_MAXSIZE | Maximum size for asyncio queues (e.g. log queues, spend update queues, and cookbook examples such as realtime audio in `nova_sonic_realtime.py`). Bounds in-memory growth to prevent OOM. Default is 1000. | LOGFIRE_TOKEN | Token for Logfire logging service | LOGFIRE_BASE_URL | Base URL for Logfire logging service (useful for self hosted deployments) | LOGGING_WORKER_CONCURRENCY | Maximum number of concurrent coroutine slots for the logging worker on the asyncio event loop. Default is 100. Setting too high will flood the event loop with logging tasks which will lower the overall latency of the requests. diff --git a/docs/my-website/docs/proxy/guardrails/guardrail_policies.md b/docs/my-website/docs/proxy/guardrails/guardrail_policies.md index 56be11c85a..e2cb839203 100644 --- a/docs/my-website/docs/proxy/guardrails/guardrail_policies.md +++ b/docs/my-website/docs/proxy/guardrails/guardrail_policies.md @@ -1,3 +1,7 @@ +import Image from '@theme/IdealImage'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + # [Beta] Guardrail Policies Use policies to group guardrails and control which ones run for specific teams, keys, or models. @@ -10,6 +14,9 @@ Use policies to group guardrails and control which ones run for specific teams, ## Quick Start + + + ```yaml showLineNumbers title="config.yaml" model_list: - model_name: gpt-4 @@ -43,6 +50,26 @@ policy_attachments: scope: "*" # apply to all requests ``` + + + +**Step 1: Create a Policy** + +Go to **Policies** tab and click **+ Create New Policy**. Fill in the policy name, description, and select guardrails to add. + +![Enter policy name](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/4ba62cc8-d2c4-4af1-a526-686295466928/ascreenshot_401eab3e2081466e8f4d4ffa3bf7bff4_text_export.jpeg) + +![Add a description for the policy](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/51685e47-1d94-4d9c-acb0-3c88dce9f938/ascreenshot_a5cd40066ff34afbb1e4089a3c93d889_text_export.jpeg) + +![Select a parent policy to inherit from](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/1d96c3d3-187a-4f7c-97d2-6ac1f093d51e/ascreenshot_8a3af3b2210547dca3d4709df920d005_text_export.jpeg) + +![Select guardrails to add to the policy](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/23781274-e600-4d5f-a8a6-4a2a977a166c/ascreenshot_a2a45d2c5d064c77ab7cb47b569ad9e9_text_export.jpeg) + +![Click Create Policy to save](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/1d1ae8a8-daa5-451b-9fa2-c5b607ff6220/ascreenshot_218c2dd259714be4aa3c4e1894c96878_text_export.jpeg) + + + + Response headers show what ran: ``` @@ -58,6 +85,9 @@ x-litellm-applied-guardrails: pii_masking,prompt_injection You have a global baseline, but want to add extra guardrails for a specific team. + + + ```yaml showLineNumbers title="config.yaml" policies: global-baseline: @@ -81,6 +111,30 @@ policy_attachments: - finance # team alias from /team/new ``` + + + +**Option 1: Create a team-scoped attachment** + +Go to **Policies** > **Attachments** tab and click **+ Create New Attachment**. Select the policy and the teams to scope it to. + +![Select teams for the attachment](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/50e58f54-3bc3-477e-a106-e58cb65fde7e/ascreenshot_85d2e3d9d8d24842baced92fea170427_text_export.jpeg) + +![Select the teams to attach the policy to](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/f24066bb-0a73-49fb-87b6-c65ad3ca5b2f/ascreenshot_242476fbdac447309f65de78b0ed9fdd_text_export.jpeg) + +**Option 2: Attach from team settings** + +Go to **Teams** > click on a team > **Settings** tab > under **Policies**, select the policies to attach. + +![Open team settings and click Edit Settings](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/c31c3735-4f9d-4c6a-896b-186e97296940/ascreenshot_4749bb24ce5942cca462acc958fd3822_text_export.jpeg) + +![Select policies to attach to this team](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/da8d5d7a-d975-4bfe-acd2-f41dcea29520/ascreenshot_835a33b6cec545cbb2987f017fbaff90_text_export.jpeg) + + + + + + Now the `finance` team gets `pii_masking` + `strict_compliance_check` + `audit_logger`, while everyone else just gets `pii_masking`. ## Remove guardrails for a specific team @@ -201,6 +255,60 @@ policy_attachments: - "test-*" # key alias pattern ``` +**Tag-based** (matches keys/teams by metadata tags, wildcards supported): + +```yaml showLineNumbers title="config.yaml" +policy_attachments: + - policy: hipaa-compliance + tags: + - "healthcare" + - "health-*" # wildcard - matches health-team, health-dev, etc. +``` + +Tags are read from key and team `metadata.tags`. For example, a key created with `metadata: {"tags": ["healthcare"]}` would match the attachment above. + +## Test Policy Matching + +Debug which policies and guardrails apply for a given context. Use this to verify your policy configuration before deploying. + + + + +Go to **Policies** > **Test** tab. Enter a team alias, key alias, model, or tags and click **Test** to see which policies match and what guardrails would be applied. + + + + + + +```bash +curl -X POST "http://localhost:4000/policies/resolve" \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '{ + "tags": ["healthcare"], + "model": "gpt-4" + }' +``` + +Response: + +```json +{ + "effective_guardrails": ["pii_masking"], + "matched_policies": [ + { + "policy_name": "hipaa-compliance", + "matched_via": "tag:healthcare", + "guardrails_added": ["pii_masking"] + } + ] +} +``` + + + + ## Config Reference ### `policies` @@ -233,14 +341,18 @@ policy_attachments: scope: ... teams: [...] keys: [...] + models: [...] + tags: [...] ``` | Field | Type | Description | |-------|------|-------------| | `policy` | `string` | **Required.** Name of the policy to attach. | | `scope` | `string` | Use `"*"` to apply globally. | -| `teams` | `list[string]` | Team aliases (from `/team/new`). | +| `teams` | `list[string]` | Team aliases (from `/team/new`). Supports `*` wildcard. | | `keys` | `list[string]` | Key aliases (from `/key/generate`). Supports `*` wildcard. | +| `models` | `list[string]` | Model names. Supports `*` wildcard. | +| `tags` | `list[string]` | Tag patterns (from key/team `metadata.tags`). Supports `*` wildcard. | ### Response Headers @@ -248,6 +360,7 @@ policy_attachments: |--------|-------------| | `x-litellm-applied-policies` | Policies that matched this request | | `x-litellm-applied-guardrails` | Guardrails that actually ran | +| `x-litellm-policy-sources` | Why each policy matched (e.g., `hipaa=tag:healthcare; baseline=scope:*`) | ## How it works diff --git a/docs/my-website/docs/proxy/guardrails/policy_tags.md b/docs/my-website/docs/proxy/guardrails/policy_tags.md new file mode 100644 index 0000000000..11840116c3 --- /dev/null +++ b/docs/my-website/docs/proxy/guardrails/policy_tags.md @@ -0,0 +1,139 @@ +# Tag-Based Policy Attachments + +Apply guardrail policies automatically to any key or team that has a specific tag. Instead of attaching policies one-by-one, tag your keys and let the policy engine handle the rest. + +**Example:** Your security team requires all healthcare-related keys to run PII masking and PHI detection. Tag those keys with `health`, create a single tag-based attachment, and every matching key gets the guardrails automatically. + +## 1. Create a Policy with Guardrails + +Navigate to **Policies** in the left sidebar. You'll see a list of existing policies along with their guardrails. + +![Policies list page showing existing policies and the + Add New Policy button](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/d7aa1e1f-011e-40bf-a356-6dfe9d5d54f1/ascreenshot_8db95c231a7f4a79a36c2a98ba127542_text_export.jpeg) + +Click **+ Add New Policy**. In the modal, enter a name for your policy (e.g., `high-risk-policy2`). You can also type to search existing policy names if you want to reference them. + +![Create New Policy modal — enter the policy name and optional description](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/18f1ff69-9b83-4a98-9aad-9892a104d3ff/ascreenshot_1c6b85231cad4ec695750b53bbbda52c_text_export.jpeg) + +Scroll down to **Guardrails to Add**. Click the dropdown to see all available guardrails configured on your proxy — select the ones this policy should enforce. + +![Guardrails to Add dropdown showing available guardrails like OAI-moderation, phi-pre-guard, pii-pre-guard](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/55cedad7-9939-44a1-8644-a184cde82ab7/ascreenshot_eab4e55b82b8411893eccb6234d60b82_text_export.jpeg) + +After selecting your guardrails, they appear as chips in the input field. The **Resolved Guardrails** section below shows the final set that will be applied (including any inherited from a parent policy). + +![Selected guardrails shown as chips: testing-pl, phi-pre-guard, pii-pre-guard. Resolved Guardrails preview below.](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/c06d5b08-1c85-4715-b827-3e6864880428/ascreenshot_7a082e55f3ad425f9009346c68afae23_text_export.jpeg) + +Click **Create Policy** to save. + +![Click Create Policy to save the new policy](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/7e6eae64-4bba-4d72-b226-d1308ac576a8/ascreenshot_22d0ed686c594221bbbd2f40df214d75_text_export.jpeg) + +## 2. Add a Tag Attachment for the Policy + +After creating the policy, switch to the **Attachments** tab. This is where you define *where* the policy applies. + +![Switch to the Attachments tab — shows the attachment table and scope documentation](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/871ae6d9-16d1-44e2-baf2-7bb8a9e72087/ascreenshot_76e124619d70462ea0e2fbb46ded1ac9_text_export.jpeg) + +Click **+ Add New Attachment**. The Attachments page explains the available scopes: Global, Teams, Keys, Models, and **Tags**. + +![Attachments page showing scope types including Tags — click + Add New Attachment](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/d45ab8bc-fc1e-425b-8a3f-44d18df810ec/ascreenshot_425824030f3144b7ab3c0ac570349b00_text_export.jpeg) + +In the **Create Policy Attachment** modal, first select the policy you just created from the dropdown. + +![Select the policy to attach from the dropdown (e.g., high-risk-policy2)](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/e0dcac40-e39c-4a6a-9d9c-4bbb9ec0ee91/ascreenshot_445b19894e0b466196a13e20c8e67f2d_text_export.jpeg) + +Choose **Specific (teams, keys, models, or tags)** as the scope type. This expands the form to show fields for Teams, Keys, Models, and Tags. + +![Select "Specific" scope type to reveal the Tags field](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/f685e02a-e22e-4c6c-9742-d5268746214b/ascreenshot_14d63d9d06dd4fc7854cfeb5e8d9ef85_text_export.jpeg) + +Scroll down to the **Tags** field and type the tag to match — here we enter `health`. You can enter any string, or use a wildcard pattern like `health-*` to match all tags starting with `health-` (e.g., `health-team`, `health-dev`). + +![Tags field with "health" entered. Supports wildcards like prod-* matching prod-us, prod-eu.](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/14581df7-732c-4ea5-b36d-58270b00e92c/ascreenshot_e734c81418f046549b61a84b9d352a29_text_export.jpeg) + +## 3. Check the Impact of the Attachment + +Before creating the attachment, click **Estimate Impact** to preview how many keys and teams would be affected. This is your blast-radius check — make sure the scope is what you expect before applying. + +![Click Estimate Impact — the tag "health" is entered and ready to preview](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/6ccb81d7-3d11-48b0-b634-fc4d738aa530/ascreenshot_2eb89e6ff13a4b12b61004660a36c30c_text_export.jpeg) + +The **Impact Preview** appears inline, showing exactly how many keys and teams would be affected. In this example: "This attachment would affect **1 key** and **0 teams**", with the key alias `hi` listed. + +![Impact Preview showing "This attachment would affect 1 key and 0 teams." Keys: hi](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/8834d85a-2c15-48dd-8d6b-810cf11ee5c4/ascreenshot_d814b42ca9f34c23b0c2269bfa3e64fb_text_export.jpeg) + +Once you're satisfied with the impact, click **Create Attachment** to save. + +![Click Create Attachment to finalize](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/4a8918f2-eedb-4f49-a53b-4e46d0387d2a/ascreenshot_b08d490d836d4f46b4e5cbb14f61377a_text_export.jpeg) + +The attachment now appears in the table with the policy name `high-risk-policy2` and tag `health` visible. + +![Attachments table showing the new attachment with policy high-risk-policy2 and tag "health"](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/45867887-0aec-44a4-963b-b6cc6c302e3e/ascreenshot_981caeff98574ec89a8a53cd295e5043_text_export.jpeg) + +## 4. Create a Key with the Tag + +Navigate to **Virtual Keys** in the left sidebar. Click **+ Create New Key**. + +![Virtual Keys page showing existing keys — click + Create New Key](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/4c1f9448-e590-4546-9357-6f68aa395b27/ascreenshot_4a7bc5be9e4347f3a9fe46f78d938d7c_text_export.jpeg) + +Enter a key name and select a model. Then expand **Optional Settings** and scroll down to the **Tags** field. + +![Create New Key modal — enter the key name](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/f84f7a2b-8057-4926-9f80-d68e437c77cf/ascreenshot_a277c8611b6e41059663b0759cd85cab_text_export.jpeg) + +In the **Tags** field, type `health` and press Enter. This is the tag the policy engine will match against. + +![Tags field in key creation — type "health" to add the tag](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/3ad3bf10-76d2-4f15-9a66-ed6c99bb25c4/ascreenshot_8a8773fb65fc49329cb1716da92b2723_text_export.jpeg) + +The tag `health` now appears as a chip in the Tags field. Confirm your settings look correct. + +![Tags field showing "health" selected with a checkmark](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/de3e58a9-6013-4d0c-882e-5517ea286684/ascreenshot_c7eef1736fce4aa894ac3b118b3800a2_text_export.jpeg) + +Click **Create Key** at the bottom of the form. + +![Click Create Key to generate the new virtual key with the health tag](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/51d419ea-ee80-4e24-8e93-b99a844881bc/ascreenshot_097d4564289943a88e30b5d2e3eab262_text_export.jpeg) + +A dialog appears with your new virtual key. Click **Copy Virtual Key** — you'll need this to test in the next step. + +![Save your Key dialog — click Copy Virtual Key to copy it to clipboard](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/e87a0cc1-4d12-4066-bfa2-973159808fd1/ascreenshot_7b616a7291d0497a9c61bdcdb59394d7_text_export.jpeg) + +## 5. Test the Key and Validate the Policy is Applied + +Navigate to **Playground** in the left sidebar to test the key interactively. + +![Navigate to Playground from the sidebar](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/e6f8a3ee-e9e8-4107-93d1-bfca734c5ce9/ascreenshot_539bde38abe646e49148a912fff2d257_text_export.jpeg) + +Under **Virtual Key Source**, select "Virtual Key" and paste the key you just copied into the input field. + +![Paste the virtual key into the Playground configuration](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/a6612c4a-d499-4e54-8019-f54fde674ad9/ascreenshot_e85ebb9051554594bab0da57823fafad_text_export.jpeg) + +Select a model from the **Select Model** dropdown. + +![Select a model (e.g., bedrock-claude-opus-4.5) from the dropdown](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/325e330f-3eff-4c5e-b177-21916138a2f5/ascreenshot_693478f89c034e949e08f3ed0dd05120_text_export.jpeg) + +Type a message and press Enter. If a guardrail blocks the request, you'll see it in the response. In this example, the `testing-pl` guardrail detected an email pattern and returned a 403 error — confirming the policy is working. + +![Guardrail in action — the request was blocked with "Content blocked: email pattern detected"](https://colony-recorder.s3.amazonaws.com/files/2026-02-11/2cf16809-d2e5-4eae-a7dd-6a16dfcca7ce/ascreenshot_727d7d4ed20b4a52b2b41e39fd36eccb_text_export.jpeg) + +**Using curl:** + +You can also verify via the command line. The response headers confirm which policies and guardrails were applied: + +```bash +curl -v http://localhost:4000/chat/completions \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '{ + "model": "gpt-4o", + "messages": [{"role": "user", "content": "say hi"}] + }' +``` + +Check the response headers: + +``` +x-litellm-applied-policies: high-risk-policy2 +x-litellm-applied-guardrails: pii-pre-guard,phi-pre-guard,testing-pl +x-litellm-policy-sources: high-risk-policy2=tag:health +``` + +| Header | What it tells you | +|--------|-------------------| +| `x-litellm-applied-policies` | Which policies matched this request | +| `x-litellm-applied-guardrails` | Which guardrails actually ran | +| `x-litellm-policy-sources` | **Why** each policy matched — `tag:health` confirms it was the tag | diff --git a/docs/my-website/img/policy_team_attach.png b/docs/my-website/img/policy_team_attach.png new file mode 100644 index 0000000000000000000000000000000000000000..4e337931ed8193f6cb14fb25b810eea2450ba28f GIT binary patch literal 230432 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

E?O7eNYVN6^<|Su|Ep0+GMDE9vvjz) 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�|{Z(MoR$JsTdN+tBKJ)^HsBw81jc`x! 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�VX=zpMH7| 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 literal 0 HcmV?d00001 diff --git a/docs/my-website/img/policy_test_matching.png b/docs/my-website/img/policy_test_matching.png new file mode 100644 index 0000000000000000000000000000000000000000..5d024ae78b461b019b022c2f1d257251d6696bf9 GIT binary patch literal 205179 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֭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 literal 0 HcmV?d00001 diff --git a/docs/my-website/sidebars.js b/docs/my-website/sidebars.js index 28e1724ef8..579b569910 100644 --- a/docs/my-website/sidebars.js +++ b/docs/my-website/sidebars.js @@ -42,49 +42,62 @@ const sidebars = { label: "Guardrails", items: [ "proxy/guardrails/quick_start", - "proxy/guardrails/guardrail_policies", "proxy/guardrails/guardrail_load_balancing", + "proxy/guardrails/test_playground", + "proxy/guardrails/litellm_content_filter", { type: "category", - "label": "Contributing to Guardrails", + label: "Providers", + items: [ + ...[ + "proxy/guardrails/qualifire", + "proxy/guardrails/aim_security", + "proxy/guardrails/onyx_security", + "proxy/guardrails/aporia_api", + "proxy/guardrails/azure_content_guardrail", + "proxy/guardrails/bedrock", + "proxy/guardrails/enkryptai", + "proxy/guardrails/ibm_guardrails", + "proxy/guardrails/grayswan", + "proxy/guardrails/hiddenlayer", + "proxy/guardrails/lasso_security", + "proxy/guardrails/guardrails_ai", + "proxy/guardrails/lakera_ai", + "proxy/guardrails/model_armor", + "proxy/guardrails/noma_security", + "proxy/guardrails/dynamoai", + "proxy/guardrails/openai_moderation", + "proxy/guardrails/pangea", + "proxy/guardrails/pillar_security", + "proxy/guardrails/pii_masking_v2", + "proxy/guardrails/panw_prisma_airs", + "proxy/guardrails/secret_detection", + "proxy/guardrails/custom_guardrail", + "proxy/guardrails/custom_code_guardrail", + "proxy/guardrails/prompt_injection", + "proxy/guardrails/tool_permission", + "proxy/guardrails/zscaler_ai_guard", + "proxy/guardrails/javelin" + ].sort(), + ], + }, + { + type: "category", + label: "Contributing to Guardrails", items: [ "adding_provider/generic_guardrail_api", "adding_provider/simple_guardrail_tutorial", "adding_provider/adding_guardrail_support", ] }, - "proxy/guardrails/test_playground", - "proxy/guardrails/litellm_content_filter", - ...[ - "proxy/guardrails/qualifire", - "proxy/guardrails/aim_security", - "proxy/guardrails/onyx_security", - "proxy/guardrails/aporia_api", - "proxy/guardrails/azure_content_guardrail", - "proxy/guardrails/bedrock", - "proxy/guardrails/enkryptai", - "proxy/guardrails/ibm_guardrails", - "proxy/guardrails/grayswan", - "proxy/guardrails/hiddenlayer", - "proxy/guardrails/lasso_security", - "proxy/guardrails/guardrails_ai", - "proxy/guardrails/lakera_ai", - "proxy/guardrails/model_armor", - "proxy/guardrails/noma_security", - "proxy/guardrails/dynamoai", - "proxy/guardrails/openai_moderation", - "proxy/guardrails/pangea", - "proxy/guardrails/pillar_security", - "proxy/guardrails/pii_masking_v2", - "proxy/guardrails/panw_prisma_airs", - "proxy/guardrails/secret_detection", - "proxy/guardrails/custom_guardrail", - "proxy/guardrails/custom_code_guardrail", - "proxy/guardrails/prompt_injection", - "proxy/guardrails/tool_permission", - "proxy/guardrails/zscaler_ai_guard", - "proxy/guardrails/javelin" - ].sort(), + ], + }, + { + type: "category", + label: "Policies", + items: [ + "proxy/guardrails/guardrail_policies", + "proxy/guardrails/policy_tags", ], }, { @@ -396,6 +409,16 @@ const sidebars = { ], }, "proxy/caching", + { + type: "link", + label: "Guardrails", + href: "https://docs.litellm.ai/docs/proxy/guardrails/quick_start", + }, + { + type: "link", + label: "Policies", + href: "https://docs.litellm.ai/docs/proxy/guardrails/guardrail_policies", + }, { type: "category", label: "Create Custom Plugins", diff --git a/litellm-proxy-extras/litellm_proxy_extras/schema.prisma b/litellm-proxy-extras/litellm_proxy_extras/schema.prisma index b1ca1f71c9..558dfcc951 100644 --- a/litellm-proxy-extras/litellm_proxy_extras/schema.prisma +++ b/litellm-proxy-extras/litellm_proxy_extras/schema.prisma @@ -914,6 +914,7 @@ model LiteLLM_PolicyAttachmentTable { teams String[] @default([]) // Team aliases or patterns keys String[] @default([]) // Key aliases or patterns models String[] @default([]) // Model names or patterns + tags String[] @default([]) // Tag patterns (e.g., ["healthcare", "prod-*"]) created_at DateTime @default(now()) created_by String? updated_at DateTime @default(now()) @updatedAt diff --git a/litellm/constants.py b/litellm/constants.py index 180315ace0..88c57d3ce4 100644 --- a/litellm/constants.py +++ b/litellm/constants.py @@ -213,6 +213,10 @@ REDIS_DAILY_AGENT_SPEND_UPDATE_BUFFER_KEY = "litellm_daily_agent_spend_update_bu REDIS_DAILY_TAG_SPEND_UPDATE_BUFFER_KEY = "litellm_daily_tag_spend_update_buffer" MAX_REDIS_BUFFER_DEQUEUE_COUNT = int(os.getenv("MAX_REDIS_BUFFER_DEQUEUE_COUNT", 100)) MAX_SIZE_IN_MEMORY_QUEUE = int(os.getenv("MAX_SIZE_IN_MEMORY_QUEUE", 2000)) +# Bounds asyncio.Queue() instances (log queues, spend update queues, etc.) to prevent unbounded memory growth +LITELLM_ASYNCIO_QUEUE_MAXSIZE = int( + os.getenv("LITELLM_ASYNCIO_QUEUE_MAXSIZE", 1000) +) MAX_IN_MEMORY_QUEUE_FLUSH_COUNT = int( os.getenv("MAX_IN_MEMORY_QUEUE_FLUSH_COUNT", 1000) ) @@ -1306,6 +1310,9 @@ DEFAULT_SLACK_ALERTING_THRESHOLD = int( os.getenv("DEFAULT_SLACK_ALERTING_THRESHOLD", 300) ) MAX_TEAM_LIST_LIMIT = int(os.getenv("MAX_TEAM_LIST_LIMIT", 20)) +MAX_POLICY_ESTIMATE_IMPACT_ROWS = int( + os.getenv("MAX_POLICY_ESTIMATE_IMPACT_ROWS", 1000) +) DEFAULT_PROMPT_INJECTION_SIMILARITY_THRESHOLD = float( os.getenv("DEFAULT_PROMPT_INJECTION_SIMILARITY_THRESHOLD", 0.7) ) diff --git a/litellm/integrations/cloudzero/transform.py b/litellm/integrations/cloudzero/transform.py index e06b944a41..c36833a6db 100644 --- a/litellm/integrations/cloudzero/transform.py +++ b/litellm/integrations/cloudzero/transform.py @@ -103,10 +103,15 @@ class CBFTransformer: # Use team_alias if available, otherwise team_id, otherwise fallback to 'unknown' entity_id = str(team_alias) if team_alias else (str(team_id) if team_id else 'unknown') + # Get alias fields if they exist + api_key_alias = row.get('api_key_alias') + organization_alias = row.get('organization_alias') + project_alias = row.get('project_alias') + user_alias = row.get('user_alias') + dimensions = { 'entity_type': CZEntityType.TEAM.value, 'entity_id': entity_id, - 'team_id': str(team_id) if team_id else 'unknown', 'team_alias': str(team_alias) if team_alias else 'unknown', 'model': model, 'model_group': str(row.get('model_group', '')), @@ -119,28 +124,37 @@ class CBFTransformer: 'failed_requests': str(row.get('failed_requests', 0)), 'cache_creation_tokens': str(row.get('cache_creation_input_tokens', 0)), 'cache_read_tokens': str(row.get('cache_read_input_tokens', 0)), + 'organization_alias': str(organization_alias) if organization_alias else '', + 'project_alias': str(project_alias) if project_alias else '', + 'user_alias': str(user_alias) if user_alias else '', } # Extract CZRN components to populate corresponding CBF columns czrn_components = self.czrn_generator.extract_components(resource_id) service_type, provider, region, owner_account_id, resource_type, cloud_local_id = czrn_components + # Build resource/account as concat of api_key_alias and api_key_prefix + resource_account = f"{api_key_alias}|{api_key_hash}" if api_key_alias else api_key_hash + # CloudZero CBF format with proper column names cbf_record = { # Required CBF fields 'time/usage_start': usage_date.isoformat() if usage_date else None, # Required: ISO-formatted UTC datetime 'cost/cost': float(row.get('spend', 0.0)), # Required: billed cost - 'resource/id': resource_id, # Required when resource tags are present + 'resource/id': model, # Send model name # Usage metrics for token consumption 'usage/amount': total_tokens, # Numeric value of tokens consumed 'usage/units': 'tokens', # Description of token units - # CBF fields that correspond to CZRN components - 'resource/service': service_type, # Maps to CZRN service-type (litellm) - 'resource/account': owner_account_id, # Maps to CZRN owner-account-id (entity_id) + # CBF fields - updated per LIT-1907 + 'resource/service': str(row.get('model_group', '')), # Send model_group + 'resource/account': resource_account, # Send api_key_alias|api_key_prefix 'resource/region': region, # Maps to CZRN region (cross-region) - 'resource/usage_family': resource_type, # Maps to CZRN resource-type (llm-usage) + 'resource/usage_family': str(row.get('custom_llm_provider', '')), # Send provider + + # Action field + 'action/operation': str(team_id) if team_id else '', # Send team_id # Line item details 'lineitem/type': 'Usage', # Standard usage line item @@ -155,13 +169,11 @@ class CBFTransformer: if value and value != 'N/A' and value != 'unknown': # Only add meaningful tags cbf_record[f'resource/tag:{key}'] = str(value) - # Add token breakdown as resource tags for analysis + # Add token breakdown as resource tags for analysis (excluding total_tokens per LIT-1907) if prompt_tokens > 0: cbf_record['resource/tag:prompt_tokens'] = str(prompt_tokens) if completion_tokens > 0: cbf_record['resource/tag:completion_tokens'] = str(completion_tokens) - if total_tokens > 0: - cbf_record['resource/tag:total_tokens'] = str(total_tokens) return CBFRecord(cbf_record) diff --git a/litellm/integrations/gcs_bucket/gcs_bucket.py b/litellm/integrations/gcs_bucket/gcs_bucket.py index 3cb6290553..0f1ba4a409 100644 --- a/litellm/integrations/gcs_bucket/gcs_bucket.py +++ b/litellm/integrations/gcs_bucket/gcs_bucket.py @@ -9,6 +9,7 @@ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple from urllib.parse import quote from litellm._logging import verbose_logger +from litellm.constants import LITELLM_ASYNCIO_QUEUE_MAXSIZE from litellm.integrations.additional_logging_utils import AdditionalLoggingUtils from litellm.integrations.gcs_bucket.gcs_bucket_base import GCSBucketBase from litellm.proxy._types import CommonProxyErrors @@ -41,7 +42,9 @@ class GCSBucketLogger(GCSBucketBase, AdditionalLoggingUtils): batch_size=self.batch_size, flush_interval=self.flush_interval, ) - self.log_queue: asyncio.Queue[GCSLogQueueItem] = asyncio.Queue() # type: ignore[assignment] + self.log_queue: asyncio.Queue[GCSLogQueueItem] = asyncio.Queue( # type: ignore[assignment] + maxsize=LITELLM_ASYNCIO_QUEUE_MAXSIZE + ) asyncio.create_task(self.periodic_flush()) AdditionalLoggingUtils.__init__(self) @@ -69,6 +72,9 @@ class GCSBucketLogger(GCSBucketBase, AdditionalLoggingUtils): ) if logging_payload is None: raise ValueError("standard_logging_object not found in kwargs") + # When queue is at maxsize, flush immediately to make room (no blocking, no data dropped) + if self.log_queue.full(): + await self.flush_queue() await self.log_queue.put( GCSLogQueueItem( payload=logging_payload, kwargs=kwargs, response_obj=response_obj @@ -91,9 +97,9 @@ class GCSBucketLogger(GCSBucketBase, AdditionalLoggingUtils): ) if logging_payload is None: raise ValueError("standard_logging_object not found in kwargs") - # Add to logging queue - this will be flushed periodically - # Use asyncio.Queue.put() for thread-safe concurrent access - # If queue is full, this will block until space is available (backpressure) + # When queue is at maxsize, flush immediately to make room (no blocking, no data dropped) + if self.log_queue.full(): + await self.flush_queue() await self.log_queue.put( GCSLogQueueItem( payload=logging_payload, kwargs=kwargs, response_obj=response_obj diff --git a/litellm/litellm_core_utils/exception_mapping_utils.py b/litellm/litellm_core_utils/exception_mapping_utils.py index 3ddcae6931..03fbdd463d 100644 --- a/litellm/litellm_core_utils/exception_mapping_utils.py +++ b/litellm/litellm_core_utils/exception_mapping_utils.py @@ -98,16 +98,18 @@ class ExceptionCheckers: """ Check if an error string indicates a content policy violation error. """ + _lower = error_str.lower() known_exception_substrings = [ - "invalid_request_error", "content_policy_violation", + "responsibleaipolicyviolation", "the response was filtered due to the prompt triggering azure openai's content management", "your task failed as a result of our safety system", "the model produced invalid content", "content_filter_policy", + "your request was rejected as a result of our safety system", ] for substring in known_exception_substrings: - if substring in error_str.lower(): + if substring in _lower: return True return False @@ -2060,6 +2062,19 @@ def exception_type( # type: ignore # noqa: PLR0915 if isinstance(body_dict, dict): if isinstance(body_dict.get("error"), dict): azure_error_code = body_dict["error"].get("code") # type: ignore[index] + # Also check inner_error for + # ResponsibleAIPolicyViolation which indicates a + # content policy violation even when the top-level + # code is generic (e.g. "invalid_request_error"). + if azure_error_code != "content_policy_violation": + _inner = ( + body_dict["error"].get("inner_error") # type: ignore[index] + or body_dict["error"].get("innererror") # type: ignore[index] + ) + if isinstance(_inner, dict) and _inner.get( + "code" + ) == "ResponsibleAIPolicyViolation": + azure_error_code = "content_policy_violation" else: azure_error_code = body_dict.get("code") except Exception: diff --git a/litellm/llms/anthropic/chat/transformation.py b/litellm/llms/anthropic/chat/transformation.py index 4a701e1d28..53458b46fa 100644 --- a/litellm/llms/anthropic/chat/transformation.py +++ b/litellm/llms/anthropic/chat/transformation.py @@ -833,6 +833,10 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): "sonnet-4-5", "opus-4.1", "opus-4-1", + "opus-4.5", + "opus-4-5", + "opus-4.6", + "opus-4-6", } ): _output_format = ( diff --git a/litellm/llms/azure/azure.py b/litellm/llms/azure/azure.py index 76fa713ca8..44ee51d14a 100644 --- a/litellm/llms/azure/azure.py +++ b/litellm/llms/azure/azure.py @@ -901,7 +901,20 @@ class AzureChatCompletion(BaseAzureLLM, BaseLLM): if response.json()["status"] == "failed": error_data = response.json() - raise AzureOpenAIError(status_code=400, message=json.dumps(error_data)) + # Preserve Azure error details (e.g. content_policy_violation, + # inner_error, content_filter_results) as structured body so + # exception_type() can route them correctly. + _error_body = error_data.get("error", error_data) + _error_msg = ( + _error_body.get("message", "Image generation failed") + if isinstance(_error_body, dict) + else json.dumps(error_data) + ) + raise AzureOpenAIError( + status_code=400, + message=_error_msg, + body=error_data, + ) result = response.json()["result"] return httpx.Response( @@ -999,7 +1012,20 @@ class AzureChatCompletion(BaseAzureLLM, BaseLLM): if response.json()["status"] == "failed": error_data = response.json() - raise AzureOpenAIError(status_code=400, message=json.dumps(error_data)) + # Preserve Azure error details (e.g. content_policy_violation, + # inner_error, content_filter_results) as structured body so + # exception_type() can route them correctly. + _error_body = error_data.get("error", error_data) + _error_msg = ( + _error_body.get("message", "Image generation failed") + if isinstance(_error_body, dict) + else json.dumps(error_data) + ) + raise AzureOpenAIError( + status_code=400, + message=_error_msg, + body=error_data, + ) result = response.json()["result"] return httpx.Response( diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index d794aa50d2..9b5a7b42d0 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -6115,6 +6115,17 @@ "supports_function_calling": true, "supports_reasoning": true }, + "bedrock/moonshotai.kimi-k2-thinking": { + "input_cost_per_token": 7.3e-07, + "litellm_provider": "bedrock", + "max_input_tokens": 262144, + "max_output_tokens": 262144, + "max_tokens": 262144, + "mode": "chat", + "output_cost_per_token": 3.03e-06, + "supports_function_calling": true, + "supports_reasoning": true + }, "bedrock/moonshotai.kimi-k2.5": { "input_cost_per_token": 7.3e-07, "litellm_provider": "bedrock", @@ -9035,6 +9046,43 @@ } ] }, + "dashscope/qwen3-max": { + "litellm_provider": "dashscope", + "max_input_tokens": 258048, + "max_output_tokens": 65536, + "max_tokens": 65536, + "mode": "chat", + "source": "https://www.alibabacloud.com/help/en/model-studio/models", + "supports_function_calling": true, + "supports_reasoning": true, + "supports_tool_choice": true, + "tiered_pricing": [ + { + "input_cost_per_token": 1.2e-06, + "output_cost_per_token": 6e-06, + "range": [ + 0, + 32000.0 + ] + }, + { + "input_cost_per_token": 2.4e-06, + "output_cost_per_token": 1.2e-05, + "range": [ + 32000.0, + 128000.0 + ] + }, + { + "input_cost_per_token": 3e-06, + "output_cost_per_token": 1.5e-05, + "range": [ + 128000.0, + 252000.0 + ] + } + ] + }, "dashscope/qwq-plus": { "input_cost_per_token": 8e-07, "litellm_provider": "dashscope", diff --git a/litellm/proxy/common_utils/callback_utils.py b/litellm/proxy/common_utils/callback_utils.py index faeca9b2ae..62ca6dc2ae 100644 --- a/litellm/proxy/common_utils/callback_utils.py +++ b/litellm/proxy/common_utils/callback_utils.py @@ -394,6 +394,14 @@ def get_logging_caching_headers(request_data: Dict) -> Optional[Dict]: _metadata["applied_policies"] ) + if "policy_sources" in _metadata: + sources = _metadata["policy_sources"] + if isinstance(sources, dict) and sources: + # Use ';' as delimiter — matched_via reasons may contain commas + headers["x-litellm-policy-sources"] = "; ".join( + f"{name}={reason}" for name, reason in sources.items() + ) + if "semantic-similarity" in _metadata: headers["x-litellm-semantic-similarity"] = str(_metadata["semantic-similarity"]) @@ -441,6 +449,27 @@ def add_policy_to_applied_policies_header( request_data["metadata"] = _metadata +def add_policy_sources_to_metadata( + request_data: Dict, policy_sources: Dict[str, str] +): + """ + Store policy match reasons in metadata for x-litellm-policy-sources header. + + Args: + request_data: The request data dict + policy_sources: Map of policy_name -> matched_via reason + """ + if not policy_sources: + return + _metadata = request_data.get("metadata", None) or {} + existing = _metadata.get("policy_sources", {}) + if not isinstance(existing, dict): + existing = {} + existing.update(policy_sources) + _metadata["policy_sources"] = existing + request_data["metadata"] = _metadata + + def add_guardrail_response_to_standard_logging_object( litellm_logging_obj: Optional["LiteLLMLogging"], guardrail_response: StandardLoggingGuardrailInformation, diff --git a/litellm/proxy/db/db_transaction_queue/base_update_queue.py b/litellm/proxy/db/db_transaction_queue/base_update_queue.py index 202829b78b..a5ec1c3eaf 100644 --- a/litellm/proxy/db/db_transaction_queue/base_update_queue.py +++ b/litellm/proxy/db/db_transaction_queue/base_update_queue.py @@ -10,14 +10,18 @@ from litellm._service_logger import ServiceLogging service_logger_obj = ( ServiceLogging() ) # used for tracking metrics for In memory buffer, redis buffer, pod lock manager -from litellm.constants import MAX_IN_MEMORY_QUEUE_FLUSH_COUNT, MAX_SIZE_IN_MEMORY_QUEUE +from litellm.constants import ( + LITELLM_ASYNCIO_QUEUE_MAXSIZE, + MAX_IN_MEMORY_QUEUE_FLUSH_COUNT, + MAX_SIZE_IN_MEMORY_QUEUE, +) class BaseUpdateQueue: """Base class for in memory buffer for database transactions""" def __init__(self): - self.update_queue = asyncio.Queue() + self.update_queue = asyncio.Queue(maxsize=LITELLM_ASYNCIO_QUEUE_MAXSIZE) self.MAX_SIZE_IN_MEMORY_QUEUE = MAX_SIZE_IN_MEMORY_QUEUE async def add_update(self, update): diff --git a/litellm/proxy/db/db_transaction_queue/daily_spend_update_queue.py b/litellm/proxy/db/db_transaction_queue/daily_spend_update_queue.py index c3074e641b..5ba8fb1359 100644 --- a/litellm/proxy/db/db_transaction_queue/daily_spend_update_queue.py +++ b/litellm/proxy/db/db_transaction_queue/daily_spend_update_queue.py @@ -3,6 +3,7 @@ from copy import deepcopy from typing import Dict, List, Optional from litellm._logging import verbose_proxy_logger +from litellm.constants import LITELLM_ASYNCIO_QUEUE_MAXSIZE from litellm.proxy._types import BaseDailySpendTransaction from litellm.proxy.db.db_transaction_queue.base_update_queue import ( BaseUpdateQueue, @@ -54,7 +55,7 @@ class DailySpendUpdateQueue(BaseUpdateQueue): def __init__(self): super().__init__() self.update_queue: asyncio.Queue[Dict[str, BaseDailySpendTransaction]] = ( - asyncio.Queue() + asyncio.Queue(maxsize=LITELLM_ASYNCIO_QUEUE_MAXSIZE) ) async def add_update(self, update: Dict[str, BaseDailySpendTransaction]): diff --git a/litellm/proxy/db/db_transaction_queue/spend_update_queue.py b/litellm/proxy/db/db_transaction_queue/spend_update_queue.py index 2ed3f151a9..b41ff12162 100644 --- a/litellm/proxy/db/db_transaction_queue/spend_update_queue.py +++ b/litellm/proxy/db/db_transaction_queue/spend_update_queue.py @@ -2,6 +2,7 @@ import asyncio from typing import Dict, List, Optional from litellm._logging import verbose_proxy_logger +from litellm.constants import LITELLM_ASYNCIO_QUEUE_MAXSIZE from litellm.proxy._types import ( DBSpendUpdateTransactions, Litellm_EntityType, @@ -21,7 +22,9 @@ class SpendUpdateQueue(BaseUpdateQueue): def __init__(self): super().__init__() - self.update_queue: asyncio.Queue[SpendUpdateQueueItem] = asyncio.Queue() + self.update_queue: asyncio.Queue[SpendUpdateQueueItem] = asyncio.Queue( + maxsize=LITELLM_ASYNCIO_QUEUE_MAXSIZE + ) async def flush_and_get_aggregated_db_spend_update_transactions( self, diff --git a/litellm/proxy/discovery_endpoints/ui_discovery_endpoints.py b/litellm/proxy/discovery_endpoints/ui_discovery_endpoints.py index 3cbca27ce0..955067b486 100644 --- a/litellm/proxy/discovery_endpoints/ui_discovery_endpoints.py +++ b/litellm/proxy/discovery_endpoints/ui_discovery_endpoints.py @@ -19,13 +19,15 @@ async def get_ui_config(): from litellm.proxy.utils import get_proxy_base_url, get_server_root_path auto_redirect_ui_login_to_sso = ( - os.getenv("AUTO_REDIRECT_UI_LOGIN_TO_SSO", "true").lower() == "true" + os.getenv("AUTO_REDIRECT_UI_LOGIN_TO_SSO", "false").lower() == "true" ) admin_ui_disabled = os.getenv("DISABLE_ADMIN_UI", "false").lower() == "true" + sso_configured = _has_user_setup_sso() return UiDiscoveryEndpoints( server_root_path=get_server_root_path(), proxy_base_url=get_proxy_base_url(), - auto_redirect_to_sso=_has_user_setup_sso() and auto_redirect_ui_login_to_sso, + auto_redirect_to_sso=sso_configured and auto_redirect_ui_login_to_sso, admin_ui_disabled=admin_ui_disabled, + sso_configured=sso_configured, ) diff --git a/litellm/proxy/guardrails/guardrail_hooks/presidio.py b/litellm/proxy/guardrails/guardrail_hooks/presidio.py index d71b8449f9..3984384aae 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/presidio.py +++ b/litellm/proxy/guardrails/guardrail_hooks/presidio.py @@ -38,7 +38,7 @@ if TYPE_CHECKING: from litellm._uuid import uuid from litellm.caching.caching import DualCache -from litellm.exceptions import BlockedPiiEntityError +from litellm.exceptions import BlockedPiiEntityError, GuardrailRaisedException from litellm.integrations.custom_guardrail import ( CustomGuardrail, log_guardrail_information, @@ -232,6 +232,14 @@ class _OPTIONAL_PresidioPIIMasking(CustomGuardrail): """Cleanup: we try to close, but doing async cleanup in __del__ is risky.""" pass + def _has_block_action(self) -> bool: + """Return True if pii_entities_config has any BLOCK action (fail-closed on analyzer errors).""" + if not self.pii_entities_config: + return False + return any( + action == PiiAction.BLOCK for action in self.pii_entities_config.values() + ) + def _get_presidio_analyze_request_payload( self, text: str, @@ -316,13 +324,30 @@ class _OPTIONAL_PresidioPIIMasking(CustomGuardrail): # Handle error responses from Presidio (e.g., {'error': 'No text provided'}) # Presidio may return a dict instead of a list when errors occur + def _fail_on_invalid_response( + reason: str, + ) -> List[PresidioAnalyzeResponseItem]: + should_fail_closed = ( + bool(self.pii_entities_config) + or self.output_parse_pii + or self.apply_to_output + ) + if should_fail_closed: + raise GuardrailRaisedException( + guardrail_name=self.guardrail_name, + message=f"Presidio analyzer returned invalid response; cannot verify PII when PII protection is configured: {reason}", + should_wrap_with_default_message=False, + ) + verbose_proxy_logger.warning( + "Presidio analyzer %s, returning empty list", reason + ) + return [] + if isinstance(analyze_results, dict): if "error" in analyze_results: - verbose_proxy_logger.warning( - "Presidio analyzer returned error: %s, returning empty list", - analyze_results.get("error"), + return _fail_on_invalid_response( + f"error: {analyze_results.get('error')}" ) - return [] # If it's a dict but not an error, try to process it as a single item verbose_proxy_logger.debug( "Presidio returned dict (not list), attempting to process as single item" @@ -330,23 +355,33 @@ class _OPTIONAL_PresidioPIIMasking(CustomGuardrail): try: return [PresidioAnalyzeResponseItem(**analyze_results)] except Exception as e: - verbose_proxy_logger.warning( - "Failed to parse Presidio dict response: %s, returning empty list", - e, + return _fail_on_invalid_response( + f"failed to parse dict response: {e}" ) - return [] + + # Handle unexpected types (str, None, etc.) - e.g. from malformed/error + if not isinstance(analyze_results, list): + return _fail_on_invalid_response( + f"unexpected type {type(analyze_results).__name__} (expected list or dict), response: {str(analyze_results)[:200]}" + ) # Normal case: list of results final_results = [] for item in analyze_results: + if not isinstance(item, dict): + verbose_proxy_logger.warning( + "Skipping invalid Presidio result item (expected dict, got %s): %s", + type(item).__name__, + str(item)[:100], + ) + continue try: final_results.append(PresidioAnalyzeResponseItem(**item)) - except TypeError as te: - # Handle case where item is not a dict (shouldn't happen, but be defensive) + except Exception as e: verbose_proxy_logger.warning( - "Skipping invalid Presidio result item: %s (error: %s)", + "Failed to parse Presidio result item: %s (error: %s)", item, - te, + e, ) continue return final_results diff --git a/litellm/proxy/litellm_pre_call_utils.py b/litellm/proxy/litellm_pre_call_utils.py index 9be78264e8..49d31c1efe 100644 --- a/litellm/proxy/litellm_pre_call_utils.py +++ b/litellm/proxy/litellm_pre_call_utils.py @@ -1539,8 +1539,15 @@ def add_guardrails_from_policy_engine( """ from litellm._logging import verbose_proxy_logger from litellm.proxy.common_utils.callback_utils import ( + add_policy_sources_to_metadata, add_policy_to_applied_policies_header, ) + from litellm.proxy.common_utils.http_parsing_utils import ( + get_tags_from_request_body, + ) + from litellm.proxy.policy_engine.attachment_registry import ( + get_attachment_registry, + ) from litellm.proxy.policy_engine.policy_matcher import PolicyMatcher from litellm.proxy.policy_engine.policy_registry import get_policy_registry from litellm.proxy.policy_engine.policy_resolver import PolicyResolver @@ -1561,20 +1568,31 @@ def add_guardrails_from_policy_engine( ) return - # Build context from request + # Extract tags using the shared helper (handles metadata / litellm_metadata, + # top-level tags, deduplication, and type filtering). + + all_tags = get_tags_from_request_body(data) or None + context = PolicyMatchContext( team_alias=user_api_key_dict.team_alias, key_alias=user_api_key_dict.key_alias, model=data.get("model"), + tags=all_tags, ) verbose_proxy_logger.debug( f"Policy engine: matching policies for context team_alias={context.team_alias}, " - f"key_alias={context.key_alias}, model={context.model}" + f"key_alias={context.key_alias}, model={context.model}, tags={context.tags}" ) - # Get matching policies via attachments - matching_policy_names = PolicyMatcher.get_matching_policies(context=context) + # Get matching policies via attachments (with match reasons for attribution) + attachment_registry = get_attachment_registry() + matches_with_reasons = attachment_registry.get_attached_policies_with_reasons( + context + ) + matching_policy_names = [m["policy_name"] for m in matches_with_reasons] + # Build reasons map: {"hipaa-policy": "tag:healthcare", ...} + policy_reasons = {m["policy_name"]: m["matched_via"] for m in matches_with_reasons} verbose_proxy_logger.debug( f"Policy engine: matched policies via attachments: {matching_policy_names}" @@ -1607,6 +1625,16 @@ def add_guardrails_from_policy_engine( request_data=data, policy_name=policy_name ) + # Track policy attribution sources for x-litellm-policy-sources header + applied_reasons = { + name: policy_reasons[name] + for name in applied_policy_names + if name in policy_reasons + } + add_policy_sources_to_metadata( + request_data=data, policy_sources=applied_reasons + ) + # Resolve guardrails from matching policies resolved_guardrails = PolicyResolver.resolve_guardrails_for_context(context=context) diff --git a/litellm/proxy/policy_engine/attachment_registry.py b/litellm/proxy/policy_engine/attachment_registry.py index 4a335b5474..69b3b3599f 100644 --- a/litellm/proxy/policy_engine/attachment_registry.py +++ b/litellm/proxy/policy_engine/attachment_registry.py @@ -84,6 +84,7 @@ class AttachmentRegistry: teams=attachment_data.get("teams"), keys=attachment_data.get("keys"), models=attachment_data.get("models"), + tags=attachment_data.get("tags"), ) def get_attached_policies(self, context: PolicyMatchContext) -> List[str]: @@ -96,21 +97,68 @@ class AttachmentRegistry: Returns: List of policy names that are attached to matching scopes """ + return [r["policy_name"] for r in self.get_attached_policies_with_reasons(context)] + + def get_attached_policies_with_reasons( + self, context: PolicyMatchContext + ) -> List[Dict[str, Any]]: + """ + Get list of policy names and match reasons for the given context. + + Returns a list of dicts with 'policy_name' and 'matched_via' keys. + The 'matched_via' describes which dimension caused the match. + """ from litellm.proxy.policy_engine.policy_matcher import PolicyMatcher - attached_policies: List[str] = [] + results: List[Dict[str, Any]] = [] + seen_policies: set = set() for attachment in self._attachments: scope = attachment.to_policy_scope() if PolicyMatcher.scope_matches(scope=scope, context=context): - if attachment.policy not in attached_policies: - attached_policies.append(attachment.policy) + if attachment.policy not in seen_policies: + seen_policies.add(attachment.policy) + matched_via = self._describe_match_reason(attachment, context) + results.append( + { + "policy_name": attachment.policy, + "matched_via": matched_via, + } + ) verbose_proxy_logger.debug( f"Attachment matched: policy={attachment.policy}, " + f"matched_via={matched_via}, " f"context=(team={context.team_alias}, key={context.key_alias}, model={context.model})" ) - return attached_policies + return results + + @staticmethod + def _describe_match_reason( + attachment: PolicyAttachment, context: PolicyMatchContext + ) -> str: + """Describe why an attachment matched the context.""" + from litellm.proxy.policy_engine.policy_matcher import PolicyMatcher + + if attachment.is_global(): + return "scope:*" + + reasons = [] + if attachment.tags and context.tags: + matching_tags = [ + t for t in context.tags + if PolicyMatcher.matches_pattern(t, attachment.tags) + ] + if matching_tags: + reasons.append(f"tag:{matching_tags[0]}") + if attachment.teams and context.team_alias: + reasons.append(f"team:{context.team_alias}") + if attachment.keys and context.key_alias: + reasons.append(f"key:{context.key_alias}") + if attachment.models and context.model: + reasons.append(f"model:{context.model}") + + return "+".join(reasons) if reasons else "scope:default" def is_policy_attached( self, policy_name: str, context: PolicyMatchContext @@ -238,6 +286,7 @@ class AttachmentRegistry: "teams": attachment_request.teams or [], "keys": attachment_request.keys or [], "models": attachment_request.models or [], + "tags": attachment_request.tags or [], "created_at": datetime.now(timezone.utc), "updated_at": datetime.now(timezone.utc), "created_by": created_by, @@ -253,6 +302,7 @@ class AttachmentRegistry: teams=attachment_request.teams, keys=attachment_request.keys, models=attachment_request.models, + tags=attachment_request.tags, ) self.add_attachment(attachment) @@ -263,6 +313,7 @@ class AttachmentRegistry: teams=created_attachment.teams or [], keys=created_attachment.keys or [], models=created_attachment.models or [], + tags=created_attachment.tags or [], created_at=created_attachment.created_at, updated_at=created_attachment.updated_at, created_by=created_attachment.created_by, @@ -344,6 +395,7 @@ class AttachmentRegistry: teams=attachment.teams or [], keys=attachment.keys or [], models=attachment.models or [], + tags=attachment.tags or [], created_at=attachment.created_at, updated_at=attachment.updated_at, created_by=attachment.created_by, @@ -381,6 +433,7 @@ class AttachmentRegistry: teams=a.teams or [], keys=a.keys or [], models=a.models or [], + tags=a.tags or [], created_at=a.created_at, updated_at=a.updated_at, created_by=a.created_by, @@ -415,6 +468,7 @@ class AttachmentRegistry: teams=attachment_response.teams if attachment_response.teams else None, keys=attachment_response.keys if attachment_response.keys else None, models=attachment_response.models if attachment_response.models else None, + tags=attachment_response.tags if attachment_response.tags else None, ) self._attachments.append(attachment) diff --git a/litellm/proxy/policy_engine/policy_endpoints.py b/litellm/proxy/policy_engine/policy_endpoints.py index 615e153862..3bd893b003 100644 --- a/litellm/proxy/policy_engine/policy_endpoints.py +++ b/litellm/proxy/policy_engine/policy_endpoints.py @@ -23,10 +23,6 @@ from litellm.types.proxy.policy_engine import ( router = APIRouter() -# Get singleton instances -POLICY_REGISTRY = get_policy_registry() -ATTACHMENT_REGISTRY = get_attachment_registry() - # ───────────────────────────────────────────────────────────────────────────── # Policy CRUD Endpoints @@ -75,7 +71,7 @@ async def list_policies(): raise HTTPException(status_code=500, detail="Database not connected") try: - policies = await POLICY_REGISTRY.get_all_policies_from_db(prisma_client) + policies = await get_policy_registry().get_all_policies_from_db(prisma_client) return PolicyListDBResponse(policies=policies, total_count=len(policies)) except Exception as e: verbose_proxy_logger.exception(f"Error listing policies: {e}") @@ -130,7 +126,7 @@ async def create_policy( try: created_by = user_api_key_dict.user_id - result = await POLICY_REGISTRY.add_policy_to_db( + result = await get_policy_registry().add_policy_to_db( policy_request=request, prisma_client=prisma_client, created_by=created_by, @@ -168,7 +164,7 @@ async def get_policy(policy_id: str): raise HTTPException(status_code=500, detail="Database not connected") try: - result = await POLICY_REGISTRY.get_policy_by_id_from_db( + result = await get_policy_registry().get_policy_by_id_from_db( policy_id=policy_id, prisma_client=prisma_client, ) @@ -216,7 +212,7 @@ async def update_policy( try: # Check if policy exists - existing = await POLICY_REGISTRY.get_policy_by_id_from_db( + existing = await get_policy_registry().get_policy_by_id_from_db( policy_id=policy_id, prisma_client=prisma_client, ) @@ -226,7 +222,7 @@ async def update_policy( ) updated_by = user_api_key_dict.user_id - result = await POLICY_REGISTRY.update_policy_in_db( + result = await get_policy_registry().update_policy_in_db( policy_id=policy_id, policy_request=request, prisma_client=prisma_client, @@ -269,7 +265,7 @@ async def delete_policy(policy_id: str): try: # Check if policy exists - existing = await POLICY_REGISTRY.get_policy_by_id_from_db( + existing = await get_policy_registry().get_policy_by_id_from_db( policy_id=policy_id, prisma_client=prisma_client, ) @@ -278,7 +274,7 @@ async def delete_policy(policy_id: str): status_code=404, detail=f"Policy with ID {policy_id} not found" ) - result = await POLICY_REGISTRY.delete_policy_from_db( + result = await get_policy_registry().delete_policy_from_db( policy_id=policy_id, prisma_client=prisma_client, ) @@ -324,7 +320,7 @@ async def get_resolved_guardrails(policy_id: str): try: # Get the policy - policy = await POLICY_REGISTRY.get_policy_by_id_from_db( + policy = await get_policy_registry().get_policy_by_id_from_db( policy_id=policy_id, prisma_client=prisma_client, ) @@ -334,7 +330,7 @@ async def get_resolved_guardrails(policy_id: str): ) # Resolve guardrails - resolved = await POLICY_REGISTRY.resolve_guardrails_from_db( + resolved = await get_policy_registry().resolve_guardrails_from_db( policy_name=policy.policy_name, prisma_client=prisma_client, ) @@ -399,7 +395,7 @@ async def list_policy_attachments(): raise HTTPException(status_code=500, detail="Database not connected") try: - attachments = await ATTACHMENT_REGISTRY.get_all_attachments_from_db( + attachments = await get_attachment_registry().get_all_attachments_from_db( prisma_client ) return PolicyAttachmentListResponse( @@ -466,7 +462,7 @@ async def create_policy_attachment( try: # Verify the policy exists - policy = await POLICY_REGISTRY.get_all_policies_from_db(prisma_client) + policy = await get_policy_registry().get_all_policies_from_db(prisma_client) policy_names = [p.policy_name for p in policy] if request.policy_name not in policy_names: raise HTTPException( @@ -475,7 +471,7 @@ async def create_policy_attachment( ) created_by = user_api_key_dict.user_id - result = await ATTACHMENT_REGISTRY.add_attachment_to_db( + result = await get_attachment_registry().add_attachment_to_db( attachment_request=request, prisma_client=prisma_client, created_by=created_by, @@ -510,7 +506,7 @@ async def get_policy_attachment(attachment_id: str): raise HTTPException(status_code=500, detail="Database not connected") try: - result = await ATTACHMENT_REGISTRY.get_attachment_by_id_from_db( + result = await get_attachment_registry().get_attachment_by_id_from_db( attachment_id=attachment_id, prisma_client=prisma_client, ) @@ -556,7 +552,7 @@ async def delete_policy_attachment(attachment_id: str): try: # Check if attachment exists - existing = await ATTACHMENT_REGISTRY.get_attachment_by_id_from_db( + existing = await get_attachment_registry().get_attachment_by_id_from_db( attachment_id=attachment_id, prisma_client=prisma_client, ) @@ -566,7 +562,7 @@ async def delete_policy_attachment(attachment_id: str): detail=f"Attachment with ID {attachment_id} not found", ) - result = await ATTACHMENT_REGISTRY.delete_attachment_from_db( + result = await get_attachment_registry().delete_attachment_from_db( attachment_id=attachment_id, prisma_client=prisma_client, ) diff --git a/litellm/proxy/policy_engine/policy_matcher.py b/litellm/proxy/policy_engine/policy_matcher.py index ab73970bfa..888981f85f 100644 --- a/litellm/proxy/policy_engine/policy_matcher.py +++ b/litellm/proxy/policy_engine/policy_matcher.py @@ -81,6 +81,19 @@ class PolicyMatcher: if not PolicyMatcher.matches_pattern(context.model, scope.get_models()): return False + # Check tags (only if scope specifies tags) + # Unlike teams/keys/models, empty tags means "do not check" rather than "match all" + scope_tags = scope.get_tags() + if scope_tags: + if not context.tags: + return False + # Match if ANY context tag matches ANY scope tag pattern + if not any( + PolicyMatcher.matches_pattern(tag, scope_tags) + for tag in context.tags + ): + return False + return True @staticmethod diff --git a/litellm/proxy/policy_engine/policy_registry.py b/litellm/proxy/policy_engine/policy_registry.py index 5fb5084f64..a2431977b2 100644 --- a/litellm/proxy/policy_engine/policy_registry.py +++ b/litellm/proxy/policy_engine/policy_registry.py @@ -484,6 +484,7 @@ class PolicyRegistry: ) self.add_policy(policy_response.policy_name, policy) + self._initialized = True verbose_proxy_logger.info( f"Synced {len(policies)} policies from DB to in-memory registry" ) diff --git a/litellm/proxy/policy_engine/policy_resolve_endpoints.py b/litellm/proxy/policy_engine/policy_resolve_endpoints.py new file mode 100644 index 0000000000..318e990ff1 --- /dev/null +++ b/litellm/proxy/policy_engine/policy_resolve_endpoints.py @@ -0,0 +1,405 @@ +""" +Policy resolve and attachment impact estimation endpoints. + +- /policies/resolve — debug which guardrails apply for a given context +- /policies/attachments/estimate-impact — preview blast radius before creating an attachment +""" + +import json + +from fastapi import APIRouter, Depends, HTTPException, Query + +from litellm._logging import verbose_proxy_logger +from litellm.constants import MAX_POLICY_ESTIMATE_IMPACT_ROWS +from litellm.proxy._types import UserAPIKeyAuth +from litellm.proxy.auth.route_checks import RouteChecks +from litellm.proxy.auth.user_api_key_auth import user_api_key_auth +from litellm.proxy.policy_engine.attachment_registry import get_attachment_registry +from litellm.proxy.policy_engine.policy_registry import get_policy_registry +from litellm.types.proxy.policy_engine import ( + AttachmentImpactResponse, + PolicyAttachmentCreateRequest, + PolicyMatchContext, + PolicyMatchDetail, + PolicyResolveRequest, + PolicyResolveResponse, +) + +router = APIRouter() + + +def _build_alias_where(field: str, patterns: list) -> dict: + """Build a Prisma ``where`` clause for alias patterns. + + Supports exact matches and suffix wildcards (``prefix*``). + Returns something like: + {"OR": [{"field": {"in": ["a","b"]}}, {"field": {"startsWith": "dev-"}}]} + """ + exact: list = [] + prefix_conditions: list = [] + for pat in patterns: + if pat.endswith("*"): + prefix_conditions.append({field: {"startsWith": pat[:-1]}}) + else: + exact.append(pat) + + conditions: list = [] + if exact: + conditions.append({field: {"in": exact}}) + conditions.extend(prefix_conditions) + + if not conditions: + return {field: {"not": None}} + if len(conditions) == 1: + return conditions[0] + return {"OR": conditions} + + +def _parse_metadata(raw_metadata: object) -> dict: + """Parse metadata that may be a dict, JSON string, or None.""" + if raw_metadata is None: + return {} + if isinstance(raw_metadata, str): + try: + return json.loads(raw_metadata) + except (json.JSONDecodeError, TypeError): + return {} + return raw_metadata if isinstance(raw_metadata, dict) else {} + + +def _get_tags_from_metadata(metadata: object, json_metadata: object = None) -> list: + """Extract tags list from a metadata field (or metadata_json fallback).""" + raw = json_metadata if json_metadata is not None else metadata + parsed = _parse_metadata(raw) + return parsed.get("tags", []) or [] + + +async def _fetch_all_teams(prisma_client: object) -> list: + """Fetch teams from DB once. Reuse the result across tag and alias lookups.""" + return await prisma_client.db.litellm_teamtable.find_many( # type: ignore + where={}, order={"created_at": "desc"}, take=MAX_POLICY_ESTIMATE_IMPACT_ROWS, + ) + + +def _filter_keys_by_tags(keys: list, tag_patterns: list) -> tuple: + """Filter key rows whose metadata.tags match any of the given patterns. + + Returns (named_aliases, unnamed_count). + """ + + affected: list = [] + unnamed_count = 0 + for key in keys: + key_alias = key.key_alias or "" + key_tags = _get_tags_from_metadata( + key.metadata, getattr(key, "metadata_json", None) + ) + if key_tags and any( + RouteChecks._route_matches_wildcard_pattern(route=tag, pattern=pat) + for tag in key_tags + for pat in tag_patterns + ): + if key_alias: + affected.append(key_alias) + else: + unnamed_count += 1 + return affected, unnamed_count + + +def _filter_teams_by_tags(teams: list, tag_patterns: list) -> tuple: + """Filter pre-fetched team rows whose metadata.tags match any patterns. + + Returns (named_aliases, unnamed_count). + """ + + affected: list = [] + unnamed_count = 0 + for team in teams: + team_alias = team.team_alias or "" + team_tags = _get_tags_from_metadata(team.metadata) + if team_tags and any( + RouteChecks._route_matches_wildcard_pattern(route=tag, pattern=pat) + for tag in team_tags + for pat in tag_patterns + ): + if team_alias: + affected.append(team_alias) + else: + unnamed_count += 1 + return affected, unnamed_count + + +async def _find_affected_by_team_patterns( + prisma_client: object, + all_teams: list, + team_patterns: list, + existing_teams: list, + existing_keys: list, +) -> tuple: + """Filter pre-fetched teams by alias patterns, then fetch their keys. + + Returns (new_teams, new_keys, unnamed_keys_count). + """ + + new_teams: list = [] + matched_team_ids: list = [] + + for team in all_teams: + team_alias = team.team_alias or "" + if team_alias and any( + RouteChecks._route_matches_wildcard_pattern(route=team_alias, pattern=pat) + for pat in team_patterns + ): + if team_alias not in existing_teams: + new_teams.append(team_alias) + matched_team_ids.append(str(team.team_id)) + + new_keys: list = [] + unnamed_keys_count = 0 + if matched_team_ids: + keys = await prisma_client.db.litellm_verificationtoken.find_many( # type: ignore + where={"team_id": {"in": matched_team_ids}}, + order={"created_at": "desc"}, take=MAX_POLICY_ESTIMATE_IMPACT_ROWS, + ) + for key in keys: + key_alias = key.key_alias or "" + if key_alias: + if key_alias not in existing_keys: + new_keys.append(key_alias) + else: + unnamed_keys_count += 1 + + return new_teams, new_keys, unnamed_keys_count + + +async def _find_affected_keys_by_alias( + prisma_client: object, key_patterns: list, existing_keys: list +) -> list: + """Find keys whose alias matches the given patterns.""" + + affected: list = [] + + keys = await prisma_client.db.litellm_verificationtoken.find_many( # type: ignore + where=_build_alias_where("key_alias", key_patterns), + order={"created_at": "desc"}, take=MAX_POLICY_ESTIMATE_IMPACT_ROWS, + ) + for key in keys: + key_alias = key.key_alias or "" + if key_alias and any( + RouteChecks._route_matches_wildcard_pattern(route=key_alias, pattern=pat) + for pat in key_patterns + ): + if key_alias not in existing_keys: + affected.append(key_alias) + return affected + + +# ───────────────────────────────────────────────────────────────────────────── +# Policy Resolve Endpoint +# ───────────────────────────────────────────────────────────────────────────── + + +@router.post( + "/policies/resolve", + tags=["Policies"], + dependencies=[Depends(user_api_key_auth)], + response_model=PolicyResolveResponse, +) +async def resolve_policies_for_context( + request: PolicyResolveRequest, + force_sync: bool = Query( + default=False, + description="Force a DB sync before resolving. Default uses in-memory cache.", + ), + user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), +): + """ + Resolve which policies and guardrails apply for a given context. + + Use this endpoint to debug "what guardrails would apply to a request + with this team/key/model/tags combination?" + + Example Request: + ```bash + curl -X POST "http://localhost:4000/policies/resolve" \\ + -H "Authorization: Bearer " \\ + -H "Content-Type: application/json" \\ + -d '{ + "tags": ["healthcare"], + "model": "gpt-4" + }' + ``` + """ + from litellm.proxy.policy_engine.policy_matcher import PolicyMatcher + from litellm.proxy.policy_engine.policy_resolver import PolicyResolver + from litellm.proxy.proxy_server import prisma_client + + if prisma_client is None: + raise HTTPException(status_code=500, detail="Database not connected") + + try: + # Only sync from DB when explicitly requested; otherwise use in-memory cache + if force_sync: + await get_policy_registry().sync_policies_from_db(prisma_client) + await get_attachment_registry().sync_attachments_from_db(prisma_client) + + # Build context from request + context = PolicyMatchContext( + team_alias=request.team_alias, + key_alias=request.key_alias, + model=request.model, + tags=request.tags, + ) + + # Get matching policies with reasons + match_results = get_attachment_registry().get_attached_policies_with_reasons( + context=context + ) + + if not match_results: + return PolicyResolveResponse( + effective_guardrails=[], + matched_policies=[], + ) + + # Filter by conditions + policy_names = [r["policy_name"] for r in match_results] + applied_policy_names = PolicyMatcher.get_policies_with_matching_conditions( + policy_names=policy_names, + context=context, + ) + + # Resolve guardrails for each applied policy + matched_policies = [] + all_guardrails: set = set() + for result in match_results: + pname = result["policy_name"] + if pname not in applied_policy_names: + continue + resolved = PolicyResolver.resolve_policy_guardrails( + policy_name=pname, + policies=get_policy_registry().get_all_policies(), + context=context, + ) + guardrails = resolved.guardrails if resolved else [] + all_guardrails.update(guardrails) + matched_policies.append( + PolicyMatchDetail( + policy_name=pname, + matched_via=result["matched_via"], + guardrails_added=guardrails, + ) + ) + + return PolicyResolveResponse( + effective_guardrails=sorted(all_guardrails), + matched_policies=matched_policies, + ) + except HTTPException: + raise + except Exception as e: + verbose_proxy_logger.exception(f"Error resolving policies: {e}") + raise HTTPException(status_code=500, detail=str(e)) + + +# ───────────────────────────────────────────────────────────────────────────── +# Attachment Impact Estimation Endpoint +# ───────────────────────────────────────────────────────────────────────────── + + +@router.post( + "/policies/attachments/estimate-impact", + tags=["Policies"], + dependencies=[Depends(user_api_key_auth)], + response_model=AttachmentImpactResponse, +) +async def estimate_attachment_impact( + request: PolicyAttachmentCreateRequest, + user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), +): + """ + Estimate how many keys and teams would be affected by a policy attachment. + + Use this before creating an attachment to preview the blast radius. + + Example Request: + ```bash + curl -X POST "http://localhost:4000/policies/attachments/estimate-impact" \\ + -H "Authorization: Bearer " \\ + -H "Content-Type: application/json" \\ + -d '{ + "policy_name": "hipaa-compliance", + "tags": ["healthcare", "health-*"] + }' + ``` + """ + from litellm.proxy.proxy_server import prisma_client + + if prisma_client is None: + raise HTTPException(status_code=500, detail="Database not connected") + + try: + # If global scope, everything is affected — not useful to enumerate + if request.scope == "*": + return AttachmentImpactResponse( + affected_keys_count=-1, + affected_teams_count=-1, + sample_keys=["(global scope — affects all keys)"], + sample_teams=["(global scope — affects all teams)"], + ) + + affected_keys: list = [] + affected_teams: list = [] + unnamed_keys = 0 + unnamed_teams = 0 + + tag_patterns = request.tags or [] + team_patterns = request.teams or [] + + # Fetch teams once — reused by both tag-based and alias-based lookups + all_teams: list = [] + if tag_patterns or team_patterns: + all_teams = await _fetch_all_teams(prisma_client) + + # Tag-based impact + if tag_patterns: + keys = await prisma_client.db.litellm_verificationtoken.find_many( # type: ignore + where={}, order={"created_at": "desc"}, + take=MAX_POLICY_ESTIMATE_IMPACT_ROWS, + ) + affected_keys, unnamed_keys = _filter_keys_by_tags(keys, tag_patterns) + affected_teams, unnamed_teams = _filter_teams_by_tags( + all_teams, tag_patterns, + ) + + # Team-based impact (alias matching + keys belonging to those teams) + if team_patterns: + new_teams, new_keys, new_unnamed = await _find_affected_by_team_patterns( + prisma_client, all_teams, team_patterns, + affected_teams, affected_keys, + ) + affected_teams.extend(new_teams) + affected_keys.extend(new_keys) + unnamed_keys += new_unnamed + + # Key-based impact (direct alias matching) + key_patterns = request.keys or [] + if key_patterns: + new_keys = await _find_affected_keys_by_alias( + prisma_client, key_patterns, affected_keys, + ) + affected_keys.extend(new_keys) + + return AttachmentImpactResponse( + affected_keys_count=len(affected_keys) + unnamed_keys, + affected_teams_count=len(affected_teams) + unnamed_teams, + unnamed_keys_count=unnamed_keys, + unnamed_teams_count=unnamed_teams, + sample_keys=affected_keys[:10], + sample_teams=affected_teams[:10], + ) + except HTTPException: + raise + except Exception as e: + verbose_proxy_logger.exception(f"Error estimating attachment impact: {e}") + raise HTTPException(status_code=500, detail=str(e)) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index a0ebe277fe..b507beef4f 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -427,6 +427,9 @@ from litellm.proxy.pass_through_endpoints.pass_through_endpoints import ( router as pass_through_router, ) from litellm.proxy.policy_engine.policy_endpoints import router as policy_crud_router +from litellm.proxy.policy_engine.policy_resolve_endpoints import ( + router as policy_resolve_router, +) from litellm.proxy.prompts.prompt_endpoints import router as prompts_router from litellm.proxy.public_endpoints import router as public_endpoints_router from litellm.proxy.rag_endpoints.endpoints import router as rag_router @@ -11750,6 +11753,7 @@ app.include_router(analytics_router) app.include_router(guardrails_router) app.include_router(policy_router) app.include_router(policy_crud_router) +app.include_router(policy_resolve_router) app.include_router(search_tool_management_router) app.include_router(prompts_router) app.include_router(callback_management_endpoints_router) diff --git a/litellm/proxy/schema.prisma b/litellm/proxy/schema.prisma index 1750efed92..37ed018266 100644 --- a/litellm/proxy/schema.prisma +++ b/litellm/proxy/schema.prisma @@ -911,6 +911,7 @@ model LiteLLM_PolicyAttachmentTable { teams String[] @default([]) // Team aliases or patterns keys String[] @default([]) // Key aliases or patterns models String[] @default([]) // Model names or patterns + tags String[] @default([]) // Tag patterns (e.g., ["healthcare", "prod-*"]) created_at DateTime @default(now()) created_by String? updated_at DateTime @default(now()) @updatedAt diff --git a/litellm/types/integrations/cloudzero.py b/litellm/types/integrations/cloudzero.py index aeda76aa5f..e79500e08d 100644 --- a/litellm/types/integrations/cloudzero.py +++ b/litellm/types/integrations/cloudzero.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Optional +from typing import Any, Dict class CBFRecord(Dict[str, Any]): @@ -9,19 +9,23 @@ class CBFRecord(Dict[str, Any]): (e.g., 'time/usage_start', 'cost/cost'), we use a Dict base class rather than TypedDict to accommodate the special characters in field names. - Expected CBF fields: + Expected CBF fields (per LIT-1907): - time/usage_start: ISO-formatted UTC datetime (Optional[str]) - cost/cost: Billed cost (float) - - resource/id: CloudZero Resource Name (CZRN) (str) + - resource/id: Model name (str) - usage/amount: Numeric value of tokens consumed (int) - usage/units: Description of units, e.g., 'tokens' (str) - - resource/service: Maps to CZRN service-type, e.g., 'litellm' (str) - - resource/account: Maps to CZRN owner-account-id (entity_id) (str) + - resource/service: Model group (str) + - resource/account: api_key_alias|api_key_prefix (str) - resource/region: Maps to CZRN region, e.g., 'cross-region' (str) - - resource/usage_family: Maps to CZRN resource-type, e.g., 'llm-usage' (str) + - resource/usage_family: Provider (str) + - action/operation: Team ID (str) - lineitem/type: Standard usage line item, e.g., 'Usage' (str) - resource/tag:provider: CZRN provider component (str) - resource/tag:model: CZRN cloud-local-id component (model) (str) + - resource/tag:organization_alias: Organization alias if available (Optional[str]) + - resource/tag:project_alias: Project alias if available (Optional[str]) + - resource/tag:user_alias: User alias if available (Optional[str]) - resource/tag:{key}: Various resource tags for dimensions and metrics (Optional[str]) """ pass diff --git a/litellm/types/proxy/discovery_endpoints/ui_discovery_endpoints.py b/litellm/types/proxy/discovery_endpoints/ui_discovery_endpoints.py index dc167667bc..4a4cdaa2ba 100644 --- a/litellm/types/proxy/discovery_endpoints/ui_discovery_endpoints.py +++ b/litellm/types/proxy/discovery_endpoints/ui_discovery_endpoints.py @@ -8,3 +8,4 @@ class UiDiscoveryEndpoints(BaseModel): proxy_base_url: Optional[str] auto_redirect_to_sso: bool admin_ui_disabled: bool + sso_configured: bool diff --git a/litellm/types/proxy/policy_engine/__init__.py b/litellm/types/proxy/policy_engine/__init__.py index bc54c3eb36..42490c2edd 100644 --- a/litellm/types/proxy/policy_engine/__init__.py +++ b/litellm/types/proxy/policy_engine/__init__.py @@ -19,6 +19,7 @@ from litellm.types.proxy.policy_engine.policy_types import ( PolicyScope, ) from litellm.types.proxy.policy_engine.resolver_types import ( + AttachmentImpactResponse, PolicyAttachmentCreateRequest, PolicyAttachmentDBResponse, PolicyAttachmentListResponse, @@ -30,6 +31,9 @@ from litellm.types.proxy.policy_engine.resolver_types import ( PolicyListDBResponse, PolicyListResponse, PolicyMatchContext, + PolicyMatchDetail, + PolicyResolveRequest, + PolicyResolveResponse, PolicyScopeResponse, PolicySummaryItem, PolicyTestResponse, @@ -75,4 +79,9 @@ __all__ = [ "PolicyAttachmentCreateRequest", "PolicyAttachmentDBResponse", "PolicyAttachmentListResponse", + # Resolve types + "PolicyResolveRequest", + "PolicyResolveResponse", + "PolicyMatchDetail", + "AttachmentImpactResponse", ] diff --git a/litellm/types/proxy/policy_engine/policy_types.py b/litellm/types/proxy/policy_engine/policy_types.py index 1c01f89e8b..f221ba7e03 100644 --- a/litellm/types/proxy/policy_engine/policy_types.py +++ b/litellm/types/proxy/policy_engine/policy_types.py @@ -73,13 +73,15 @@ class PolicyScope(BaseModel): Used internally by PolicyAttachment to define WHERE a policy applies. Scope Fields: - | Field | What it matches | Wildcard support | - |--------|-----------------|----------------------| - | teams | Team aliases | *, healthcare-* | - | keys | Key aliases | *, dev-key-* | - | models | Model names | *, bedrock/*, gpt-* | + | Field | What it matches | Wildcard support | Default behavior | + |--------|-----------------|----------------------|---------------------| + | teams | Team aliases | *, healthcare-* | None → matches all | + | keys | Key aliases | *, dev-key-* | None → matches all | + | models | Model names | *, bedrock/*, gpt-* | None → matches all | + | tags | Key/team tags | *, health-*, prod-* | None → not checked | - If a field is None or empty, it defaults to matching everything (["*"]). + If teams/keys/models is None or empty, it defaults to matching everything (["*"]). + If tags is None or empty, the tag dimension is NOT checked (matches all). A request must match ALL specified scope fields for the attachment to apply. """ @@ -95,6 +97,10 @@ class PolicyScope(BaseModel): default=None, description="Model names or wildcard patterns. Use '*' for all models.", ) + tags: Optional[List[str]] = Field( + default=None, + description="Tag patterns to match against key/team tags. Supports wildcards (e.g., health-*).", + ) model_config = ConfigDict(extra="forbid") @@ -110,6 +116,14 @@ class PolicyScope(BaseModel): """Returns models list, defaulting to ['*'] if not specified.""" return self.models if self.models else ["*"] + def get_tags(self) -> List[str]: + """Returns tags list, defaulting to empty list if not specified. + + Unlike teams/keys/models, empty tags means 'do not check tags' + rather than 'match all'. This is because tags are opt-in scoping. + """ + return self.tags if self.tags else [] + # ───────────────────────────────────────────────────────────────────────────── # Policy Guardrails @@ -266,6 +280,10 @@ class PolicyAttachment(BaseModel): default=None, description="Model names or patterns this attachment applies to.", ) + tags: Optional[List[str]] = Field( + default=None, + description="Tag patterns this attachment applies to. Supports wildcards (e.g., health-*).", + ) model_config = ConfigDict(extra="forbid") @@ -281,6 +299,7 @@ class PolicyAttachment(BaseModel): teams=self.teams, keys=self.keys, models=self.models, + tags=self.tags, ) diff --git a/litellm/types/proxy/policy_engine/resolver_types.py b/litellm/types/proxy/policy_engine/resolver_types.py index 9488b8b084..0c2c7336f8 100644 --- a/litellm/types/proxy/policy_engine/resolver_types.py +++ b/litellm/types/proxy/policy_engine/resolver_types.py @@ -30,6 +30,10 @@ class PolicyMatchContext(BaseModel): default=None, description="Model name from the request.", ) + tags: Optional[List[str]] = Field( + default=None, + description="Tags from key/team metadata.", + ) model_config = ConfigDict(extra="forbid") @@ -65,6 +69,7 @@ class PolicyScopeResponse(BaseModel): teams: List[str] = Field(default_factory=list) keys: List[str] = Field(default_factory=list) models: List[str] = Field(default_factory=list) + tags: List[str] = Field(default_factory=list) class PolicyGuardrailsResponse(BaseModel): @@ -242,6 +247,10 @@ class PolicyAttachmentCreateRequest(BaseModel): default=None, description="Model names or patterns this attachment applies to.", ) + tags: Optional[List[str]] = Field( + default=None, + description="Tag patterns this attachment applies to. Supports wildcards (e.g., health-*).", + ) class PolicyAttachmentDBResponse(BaseModel): @@ -253,6 +262,7 @@ class PolicyAttachmentDBResponse(BaseModel): teams: List[str] = Field(default_factory=list, description="Team patterns.") keys: List[str] = Field(default_factory=list, description="Key patterns.") models: List[str] = Field(default_factory=list, description="Model patterns.") + tags: List[str] = Field(default_factory=list, description="Tag patterns.") created_at: Optional[datetime] = Field( default=None, description="When the attachment was created." ) @@ -274,3 +284,81 @@ class PolicyAttachmentListResponse(BaseModel): default_factory=list, description="List of policy attachments." ) total_count: int = Field(default=0, description="Total number of attachments.") + + +# ───────────────────────────────────────────────────────────────────────────── +# Policy Resolve Types +# ───────────────────────────────────────────────────────────────────────────── + + +class PolicyResolveRequest(BaseModel): + """Request body for resolving effective policies/guardrails for a context.""" + + team_alias: Optional[str] = Field( + default=None, description="Team alias to resolve for." + ) + key_alias: Optional[str] = Field( + default=None, description="Key alias to resolve for." + ) + model: Optional[str] = Field( + default=None, description="Model name to resolve for." + ) + tags: Optional[List[str]] = Field( + default=None, description="Tags to resolve for." + ) + + +class PolicyMatchDetail(BaseModel): + """Details about why a specific policy matched.""" + + policy_name: str = Field(description="Name of the matched policy.") + matched_via: str = Field( + description="How the policy was matched (e.g., 'tag:healthcare', 'team:health-team', 'scope:*')." + ) + guardrails_added: List[str] = Field( + default_factory=list, + description="Guardrails this policy contributes.", + ) + + +class PolicyResolveResponse(BaseModel): + """Response for resolving effective policies/guardrails for a context.""" + + effective_guardrails: List[str] = Field( + default_factory=list, + description="Final list of guardrails that would be applied.", + ) + matched_policies: List[PolicyMatchDetail] = Field( + default_factory=list, + description="Details about each matched policy and why it matched.", + ) + + +# ───────────────────────────────────────────────────────────────────────────── +# Attachment Impact Estimation Types +# ───────────────────────────────────────────────────────────────────────────── + + +class AttachmentImpactResponse(BaseModel): + """Response for estimating the impact of a policy attachment.""" + + affected_keys_count: int = Field( + default=0, description="Number of keys that would be affected (named + unnamed)." + ) + affected_teams_count: int = Field( + default=0, description="Number of teams that would be affected (named + unnamed)." + ) + unnamed_keys_count: int = Field( + default=0, description="Number of affected keys without an alias." + ) + unnamed_teams_count: int = Field( + default=0, description="Number of affected teams without an alias." + ) + sample_keys: List[str] = Field( + default_factory=list, + description="Sample of affected key aliases (up to 10).", + ) + sample_teams: List[str] = Field( + default_factory=list, + description="Sample of affected team aliases (up to 10).", + ) diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 35538ab100..9b5a7b42d0 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -9046,6 +9046,43 @@ } ] }, + "dashscope/qwen3-max": { + "litellm_provider": "dashscope", + "max_input_tokens": 258048, + "max_output_tokens": 65536, + "max_tokens": 65536, + "mode": "chat", + "source": "https://www.alibabacloud.com/help/en/model-studio/models", + "supports_function_calling": true, + "supports_reasoning": true, + "supports_tool_choice": true, + "tiered_pricing": [ + { + "input_cost_per_token": 1.2e-06, + "output_cost_per_token": 6e-06, + "range": [ + 0, + 32000.0 + ] + }, + { + "input_cost_per_token": 2.4e-06, + "output_cost_per_token": 1.2e-05, + "range": [ + 32000.0, + 128000.0 + ] + }, + { + "input_cost_per_token": 3e-06, + "output_cost_per_token": 1.5e-05, + "range": [ + 128000.0, + 252000.0 + ] + } + ] + }, "dashscope/qwq-plus": { "input_cost_per_token": 8e-07, "litellm_provider": "dashscope", diff --git a/schema.prisma b/schema.prisma index 9a87a491cf..4329f939a7 100644 --- a/schema.prisma +++ b/schema.prisma @@ -913,6 +913,7 @@ model LiteLLM_PolicyAttachmentTable { teams String[] @default([]) // Team aliases or patterns keys String[] @default([]) // Key aliases or patterns models String[] @default([]) // Model names or patterns + tags String[] @default([]) // Tag patterns (e.g., ["healthcare", "prod-*"]) created_at DateTime @default(now()) created_by String? updated_at DateTime @default(now()) @updatedAt diff --git a/tests/test_litellm/llms/anthropic/chat/test_anthropic_chat_transformation.py b/tests/test_litellm/llms/anthropic/chat/test_anthropic_chat_transformation.py index e3bd7d2bb3..57e0dd494e 100644 --- a/tests/test_litellm/llms/anthropic/chat/test_anthropic_chat_transformation.py +++ b/tests/test_litellm/llms/anthropic/chat/test_anthropic_chat_transformation.py @@ -3,7 +3,6 @@ import os import sys import pytest -from fastapi.testclient import TestClient sys.path.insert( 0, os.path.abspath("../../../../..") @@ -855,6 +854,92 @@ def test_anthropic_structured_output_beta_header(): ) +@pytest.mark.parametrize( + "model_name", + [ + "claude-opus-4-6-20250918", + "claude-opus-4.6-20250918", + "claude-opus-4-5-20251101", + "claude-opus-4.5-20251101", + ], +) +def test_opus_uses_native_structured_output(model_name): + """ + Test that Opus 4.5 and 4.6 models use native Anthropic structured outputs + (output_format) rather than the tool-based workaround. + """ + config = AnthropicConfig() + + response_format = { + "type": "json_schema", + "json_schema": { + "name": "test_schema", + "schema": { + "type": "object", + "properties": { + "name": {"type": "string"}, + "age": {"type": "integer"}, + }, + "required": ["name", "age"], + "additionalProperties": False, + }, + }, + } + + optional_params = config.map_openai_params( + non_default_params={"response_format": response_format}, + optional_params={}, + model=model_name, + drop_params=False, + ) + + # Should use output_format (native structured outputs) + assert "output_format" in optional_params + assert optional_params["output_format"]["type"] == "json_schema" + + # Should NOT create a tool-based workaround + assert "tools" not in optional_params + assert "tool_choice" not in optional_params + + # Should set json_mode + assert optional_params.get("json_mode") is True + + +def test_non_structured_output_model_uses_tool_workaround(): + """ + Test that models NOT in the native structured output list still use the + tool-based workaround for response_format. + """ + config = AnthropicConfig() + + response_format = { + "type": "json_schema", + "json_schema": { + "name": "test_schema", + "schema": { + "type": "object", + "properties": {"result": {"type": "string"}}, + "required": ["result"], + "additionalProperties": False, + }, + }, + } + + optional_params = config.map_openai_params( + non_default_params={"response_format": response_format}, + optional_params={}, + model="claude-3-5-sonnet-20241022", + drop_params=False, + ) + + # Should NOT use output_format + assert "output_format" not in optional_params + + # Should use tool-based workaround + assert "tools" in optional_params + assert "tool_choice" in optional_params + + # ============ Tool Search Tests ============ diff --git a/tests/test_litellm/llms/azure/test_azure_exception_mapping.py b/tests/test_litellm/llms/azure/test_azure_exception_mapping.py index f4abe7f2b9..495ca958cf 100644 --- a/tests/test_litellm/llms/azure/test_azure_exception_mapping.py +++ b/tests/test_litellm/llms/azure/test_azure_exception_mapping.py @@ -239,4 +239,149 @@ class TestAzureExceptionMapping: assert e.provider_specific_fields is not None assert e.provider_specific_fields["inner_error"]["code"] == "ResponsibleAIPolicyViolation" assert e.provider_specific_fields["inner_error"]["revised_prompt"] == "revised" - assert e.provider_specific_fields["inner_error"]["content_filter_results"]["violence"]["filtered"] is True \ No newline at end of file + assert e.provider_specific_fields["inner_error"]["content_filter_results"]["violence"]["filtered"] is True + + def test_azure_content_policy_violation_detected_via_inner_error_code(self): + """Regression test for #20811: Azure returns inner_error with + ResponsibleAIPolicyViolation but the top-level error message is + generic. Previously this fell through to the generic + BadRequestError handler and all error details were lost.""" + + mock_exception = Exception("Bad request") + # This body structure mirrors what Azure OpenAI Images API returns + # for DALL-E 3 content policy violations (issue #20811). + mock_exception.body = { + "error": { + "code": "content_policy_violation", + "inner_error": { + "code": "ResponsibleAIPolicyViolation", + "content_filter_results": { + "hate": {"filtered": False, "severity": "safe"}, + "profanity": {"detected": False, "filtered": False}, + "self_harm": {"filtered": False, "severity": "safe"}, + "sexual": {"filtered": False, "severity": "safe"}, + "violence": {"filtered": True, "severity": "low"}, + }, + "revised_prompt": ( + "A dark and intense illustration of a man " + "in a dramatic action scene." + ), + }, + "message": ( + "Your request was rejected as a result of our safety system." + ), + "type": "invalid_request_error", + } + } + + mock_response = MagicMock() + mock_response.status_code = 400 + mock_exception.response = mock_response + + with pytest.raises(ContentPolicyViolationError) as exc_info: + exception_type( + model="azure/dall-e-3", + original_exception=mock_exception, + custom_llm_provider="azure", + ) + + e = exc_info.value + # Must surface as ContentPolicyViolationError, not generic BadRequestError + assert "safety system" in str(e) + assert e.provider_specific_fields is not None + inner = e.provider_specific_fields["inner_error"] + assert inner["code"] == "ResponsibleAIPolicyViolation" + assert inner["content_filter_results"]["violence"]["filtered"] is True + assert inner["revised_prompt"] is not None + + def test_azure_policy_violation_detected_via_inner_error_without_top_code(self): + """When the top-level code is NOT 'content_policy_violation' but + inner_error.code IS 'ResponsibleAIPolicyViolation', the error + should still be recognized as a content policy violation.""" + + mock_exception = Exception("Some error") + mock_exception.body = { + "error": { + "code": "BadRequest", + "inner_error": { + "code": "ResponsibleAIPolicyViolation", + "content_filter_results": { + "violence": {"filtered": True, "severity": "medium"}, + }, + }, + "message": "The request was rejected.", + "type": "invalid_request_error", + } + } + + mock_response = MagicMock() + mock_response.status_code = 400 + mock_exception.response = mock_response + + with pytest.raises(ContentPolicyViolationError) as exc_info: + exception_type( + model="azure/dall-e-3", + original_exception=mock_exception, + custom_llm_provider="azure", + ) + + e = exc_info.value + assert e.provider_specific_fields is not None + assert e.provider_specific_fields["inner_error"]["code"] == "ResponsibleAIPolicyViolation" + + def test_azure_image_polling_error_preserves_body(self): + """Verify that AzureOpenAIError raised from the DALL-E polling path + carries the structured body so exception_type() can inspect it.""" + from litellm.llms.azure.common_utils import AzureOpenAIError + + error_payload = { + "status": "failed", + "error": { + "code": "content_policy_violation", + "message": "Your request was rejected.", + "inner_error": { + "code": "ResponsibleAIPolicyViolation", + "content_filter_results": { + "violence": {"filtered": True, "severity": "low"}, + }, + }, + }, + } + + # Simulate what the fixed polling path now does + _error_body = error_payload.get("error", error_payload) + _error_msg = ( + _error_body.get("message", "Image generation failed") + if isinstance(_error_body, dict) + else json.dumps(error_payload) + ) + exc = AzureOpenAIError( + status_code=400, + message=_error_msg, + body=error_payload, + ) + + assert exc.body is not None + assert isinstance(exc.body, dict) + assert exc.body["error"]["code"] == "content_policy_violation" + assert "Your request was rejected" in exc.message + + def test_azure_safety_system_message_detected_as_policy_violation(self): + """Azure's rejection message 'Your request was rejected as a result + of our safety system' should be detected by string matching even + when the structured body is unavailable.""" + + mock_exception = Exception( + "Your request was rejected as a result of our safety system. " + "The revised prompt may contain text that is not allowed." + ) + mock_response = MagicMock() + mock_response.status_code = 400 + mock_exception.response = mock_response + + with pytest.raises(ContentPolicyViolationError): + exception_type( + model="azure/dall-e-3", + original_exception=mock_exception, + custom_llm_provider="azure", + ) \ No newline at end of file diff --git a/tests/test_litellm/proxy/discovery_endpoints/test_ui_discovery_endpoints.py b/tests/test_litellm/proxy/discovery_endpoints/test_ui_discovery_endpoints.py index 88d31e993d..9d0c771e1d 100644 --- a/tests/test_litellm/proxy/discovery_endpoints/test_ui_discovery_endpoints.py +++ b/tests/test_litellm/proxy/discovery_endpoints/test_ui_discovery_endpoints.py @@ -31,6 +31,7 @@ def test_ui_discovery_endpoints_with_defaults(): assert data["proxy_base_url"] is None assert data["auto_redirect_to_sso"] is False assert data["admin_ui_disabled"] is False + assert data["sso_configured"] is False def test_ui_discovery_endpoints_with_custom_server_root_path(): @@ -50,6 +51,7 @@ def test_ui_discovery_endpoints_with_custom_server_root_path(): assert data["server_root_path"] == "/litellm" assert data["proxy_base_url"] is None assert data["auto_redirect_to_sso"] is False + assert data["sso_configured"] is False def test_ui_discovery_endpoints_with_proxy_base_url_when_set(): @@ -69,6 +71,7 @@ def test_ui_discovery_endpoints_with_proxy_base_url_when_set(): assert data["server_root_path"] == "/" assert data["proxy_base_url"] == "https://proxy.example.com" assert data["auto_redirect_to_sso"] is False + assert data["sso_configured"] is False def test_ui_discovery_endpoints_with_sso_configured_and_auto_redirect_enabled(): @@ -88,6 +91,30 @@ def test_ui_discovery_endpoints_with_sso_configured_and_auto_redirect_enabled(): assert data["server_root_path"] == "/litellm" assert data["proxy_base_url"] == "https://proxy.example.com" assert data["auto_redirect_to_sso"] is True + assert data["sso_configured"] is True + + +def test_ui_discovery_endpoints_with_sso_configured_and_auto_redirect_not_set_defaults_to_false(): + """When SSO is configured but AUTO_REDIRECT_UI_LOGIN_TO_SSO is not set, defaults to False.""" + app = FastAPI() + app.include_router(router) + client = TestClient(app) + + with patch("litellm.proxy.utils.get_server_root_path", return_value="/litellm"), \ + patch("litellm.proxy.utils.get_proxy_base_url", return_value="https://proxy.example.com"), \ + patch("litellm.proxy.auth.auth_utils._has_user_setup_sso", return_value=True), \ + patch.dict(os.environ, {"DISABLE_ADMIN_UI": "false"}, clear=False): + # Ensure AUTO_REDIRECT_UI_LOGIN_TO_SSO is not set (simulate default) + os.environ.pop("AUTO_REDIRECT_UI_LOGIN_TO_SSO", None) + + response = client.get("/.well-known/litellm-ui-config") + + assert response.status_code == 200 + data = response.json() + assert data["server_root_path"] == "/litellm" + assert data["proxy_base_url"] == "https://proxy.example.com" + assert data["auto_redirect_to_sso"] is False + assert data["sso_configured"] is True def test_ui_discovery_endpoints_with_sso_configured_but_auto_redirect_disabled(): @@ -107,6 +134,7 @@ def test_ui_discovery_endpoints_with_sso_configured_but_auto_redirect_disabled() assert data["server_root_path"] == "/litellm" assert data["proxy_base_url"] == "https://proxy.example.com" assert data["auto_redirect_to_sso"] is False + assert data["sso_configured"] is True def test_ui_discovery_endpoints_with_sso_not_configured_but_auto_redirect_enabled(): @@ -126,6 +154,7 @@ def test_ui_discovery_endpoints_with_sso_not_configured_but_auto_redirect_enable assert data["server_root_path"] == "/" assert data["proxy_base_url"] is None assert data["auto_redirect_to_sso"] is False + assert data["sso_configured"] is False def test_ui_discovery_endpoints_both_routes_return_same_data(): @@ -164,6 +193,7 @@ def test_ui_discovery_endpoints_with_admin_ui_disabled(): assert data["proxy_base_url"] is None assert data["auto_redirect_to_sso"] is False assert data["admin_ui_disabled"] is True + assert data["sso_configured"] is False def test_ui_discovery_endpoints_with_admin_ui_enabled(): @@ -184,4 +214,5 @@ def test_ui_discovery_endpoints_with_admin_ui_enabled(): assert data["proxy_base_url"] is None assert data["auto_redirect_to_sso"] is False assert data["admin_ui_disabled"] is False + assert data["sso_configured"] is False diff --git a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_presidio.py b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_presidio.py index 5ec9b13408..f01c23f711 100644 --- a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_presidio.py +++ b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_presidio.py @@ -6,6 +6,7 @@ Tests PII detection and masking for different message formats import asyncio import os import sys +from contextlib import asynccontextmanager from unittest.mock import MagicMock, patch import pytest @@ -18,10 +19,41 @@ from litellm.proxy._types import UserAPIKeyAuth from litellm.proxy.guardrails.guardrail_hooks.presidio import ( _OPTIONAL_PresidioPIIMasking, ) +from litellm.exceptions import GuardrailRaisedException from litellm.types.guardrails import LitellmParams, PiiAction, PiiEntityType from litellm.types.utils import Choices, Message, ModelResponse +def _make_mock_session_iterator(json_response): + """Create a mock _get_session_iterator that yields a session returning json_response.""" + + @asynccontextmanager + async def mock_iterator(): + class MockResponse: + async def json(self): + return json_response + + async def __aenter__(self): + return self + + async def __aexit__(self, *args): + pass + + class MockSession: + def post(self, *args, **kwargs): + return MockResponse() + + async def __aenter__(self): + return self + + async def __aexit__(self, *args): + pass + + yield MockSession() + + return mock_iterator + + @pytest.fixture def presidio_guardrail(): """Create a Presidio guardrail instance for testing""" @@ -889,39 +921,134 @@ async def test_analyze_text_error_dict_handling(): output_parse_pii=False, ) - # Mock the HTTP response to return error dict - class MockResponse: - async def json(self): - return {"error": "No text provided"} - - async def __aenter__(self): - return self - - async def __aexit__(self, *args): - pass - - class MockSession: - def post(self, *args, **kwargs): - return MockResponse() - - async def __aenter__(self): - return self - - async def __aexit__(self, *args): - pass - - with patch("aiohttp.ClientSession", return_value=MockSession()): + with patch.object( + presidio, + "_get_session_iterator", + _make_mock_session_iterator({"error": "No text provided"}), + ): result = await presidio.analyze_text( text="some text", presidio_config=None, request_data={}, ) - # Should return empty list when error dict is received - assert result == [], "Error dict should be handled gracefully" + assert result == [], "Error dict should be handled gracefully" print("✓ analyze_text error dict handling test passed") +@pytest.mark.asyncio +async def test_analyze_text_string_response_handling(): + """ + Test that analyze_text handles string responses from Presidio API. + + When Presidio returns a string (e.g. error message from websearch/hosted models), + should handle gracefully instead of crashing with TypeError about mapping vs str. + """ + presidio = _OPTIONAL_PresidioPIIMasking( + presidio_analyzer_api_base="http://mock-presidio:5002/", + presidio_anonymizer_api_base="http://mock-presidio:5001/", + output_parse_pii=False, + ) + + with patch.object( + presidio, + "_get_session_iterator", + _make_mock_session_iterator("Internal Server Error"), + ): + result = await presidio.analyze_text( + text="some text", + presidio_config=None, + request_data={}, + ) + assert result == [], "String response should be handled gracefully" + + +@pytest.mark.asyncio +async def test_analyze_text_invalid_response_raises_when_block_configured(): + """ + When pii_entities_config has BLOCK and Presidio returns invalid response, + should raise GuardrailRaisedException (fail-closed) rather than silently allowing content. + """ + presidio = _OPTIONAL_PresidioPIIMasking( + presidio_analyzer_api_base="http://mock-presidio:5002/", + presidio_anonymizer_api_base="http://mock-presidio:5001/", + output_parse_pii=False, + pii_entities_config={PiiEntityType.CREDIT_CARD: PiiAction.BLOCK}, + ) + + with patch.object( + presidio, + "_get_session_iterator", + _make_mock_session_iterator("Internal Server Error"), + ): + with pytest.raises(GuardrailRaisedException) as exc_info: + await presidio.analyze_text( + text="some text", + presidio_config=None, + request_data={}, + ) + assert "BLOCK" in str(exc_info.value) or "Presidio" in str(exc_info.value) + + +@pytest.mark.asyncio +async def test_analyze_text_invalid_response_raises_when_mask_configured(): + """ + When pii_entities_config has MASK and Presidio returns invalid response, + should raise GuardrailRaisedException (fail-closed) because PII masking is expected. + """ + presidio = _OPTIONAL_PresidioPIIMasking( + presidio_analyzer_api_base="http://mock-presidio:5002/", + presidio_anonymizer_api_base="http://mock-presidio:5001/", + output_parse_pii=False, + pii_entities_config={PiiEntityType.CREDIT_CARD: PiiAction.MASK}, + ) + + with patch.object( + presidio, + "_get_session_iterator", + _make_mock_session_iterator("Internal Server Error"), + ): + with pytest.raises(GuardrailRaisedException) as exc_info: + await presidio.analyze_text( + text="some text", + presidio_config=None, + request_data={}, + ) + assert "PII protection is configured" in str(exc_info.value) + + +@pytest.mark.asyncio +async def test_analyze_text_list_with_non_dict_items(): + """ + Test that analyze_text skips non-dict items in the result list. + + When Presidio returns a list containing strings (malformed response), + should skip invalid items and return parsed valid ones. + """ + presidio = _OPTIONAL_PresidioPIIMasking( + presidio_analyzer_api_base="http://mock-presidio:5002/", + presidio_anonymizer_api_base="http://mock-presidio:5001/", + output_parse_pii=False, + ) + + json_response = [ + {"entity_type": "PERSON", "start": 0, "end": 5, "score": 0.9}, + "invalid_string_item", + {"entity_type": "EMAIL", "start": 10, "end": 25, "score": 0.85}, + ] + with patch.object( + presidio, "_get_session_iterator", _make_mock_session_iterator(json_response) + ): + result = await presidio.analyze_text( + text="some text", + presidio_config=None, + request_data={}, + ) + assert len(result) == 2, "Should parse 2 valid dict items and skip the string" + assert result[0].get("entity_type") == "PERSON" + assert result[1].get("entity_type") == "EMAIL" + + @pytest.mark.asyncio async def test_tool_calling_complete_scenario( presidio_guardrail, mock_user_api_key, mock_cache diff --git a/tests/test_litellm/proxy/policy_engine/test_attachment_registry.py b/tests/test_litellm/proxy/policy_engine/test_attachment_registry.py index 1ed956fe99..c853253eed 100644 --- a/tests/test_litellm/proxy/policy_engine/test_attachment_registry.py +++ b/tests/test_litellm/proxy/policy_engine/test_attachment_registry.py @@ -192,6 +192,139 @@ class TestGetAttachedPolicies: assert "strict-policy" not in registry.get_attached_policies(context_wrong_team) +class TestTagBasedAttachments: + """Test tag-based policy attachment matching.""" + + def test_tag_matching_and_wildcards(self): + """Test tag matching: exact match, wildcard match, and no-match cases.""" + registry = AttachmentRegistry() + registry.load_attachments([ + {"policy": "hipaa-policy", "tags": ["healthcare"]}, + {"policy": "health-policy", "tags": ["health-*"]}, + ]) + + # Exact tag match + context = PolicyMatchContext( + team_alias="team", key_alias="key", model="gpt-4", + tags=["healthcare"], + ) + attached = registry.get_attached_policies(context) + assert "hipaa-policy" in attached + assert "health-policy" not in attached # "healthcare" doesn't match "health-*" + + # Wildcard tag match + context_wildcard = PolicyMatchContext( + team_alias="team", key_alias="key", model="gpt-4", + tags=["health-prod"], + ) + attached_wildcard = registry.get_attached_policies(context_wildcard) + assert "health-policy" in attached_wildcard + assert "hipaa-policy" not in attached_wildcard + + # No match — wrong tag + context_no_match = PolicyMatchContext( + team_alias="team", key_alias="key", model="gpt-4", + tags=["finance"], + ) + assert registry.get_attached_policies(context_no_match) == [] + + # No match — no tags on context + context_no_tags = PolicyMatchContext( + team_alias="team", key_alias="key", model="gpt-4", + tags=None, + ) + assert registry.get_attached_policies(context_no_tags) == [] + + def test_tag_combined_with_team(self): + """Test attachment with both tags and teams requires BOTH to match (AND logic).""" + registry = AttachmentRegistry() + registry.load_attachments([ + {"policy": "strict-policy", "teams": ["team-a"], "tags": ["healthcare"]}, + ]) + + # Match — both team and tag match + context = PolicyMatchContext( + team_alias="team-a", key_alias="key", model="gpt-4", + tags=["healthcare"], + ) + assert "strict-policy" in registry.get_attached_policies(context) + + # No match — tag matches but team doesn't + context_wrong_team = PolicyMatchContext( + team_alias="team-b", key_alias="key", model="gpt-4", + tags=["healthcare"], + ) + assert "strict-policy" not in registry.get_attached_policies(context_wrong_team) + + # No match — team matches but tag doesn't + context_wrong_tag = PolicyMatchContext( + team_alias="team-a", key_alias="key", model="gpt-4", + tags=["finance"], + ) + assert "strict-policy" not in registry.get_attached_policies(context_wrong_tag) + + +class TestMatchAttribution: + """Test get_attached_policies_with_reasons — the attribution logic that + powers response headers and the Policy Simulator UI.""" + + def test_reasons_for_global_tag_team_attachments(self): + """Test that match reasons correctly describe WHY each policy matched.""" + registry = AttachmentRegistry() + registry.load_attachments([ + {"policy": "global-baseline", "scope": "*"}, + {"policy": "hipaa-policy", "tags": ["healthcare"]}, + {"policy": "team-policy", "teams": ["health-team"]}, + ]) + + context = PolicyMatchContext( + team_alias="health-team", key_alias="key", model="gpt-4", + tags=["healthcare"], + ) + results = registry.get_attached_policies_with_reasons(context) + reasons = {r["policy_name"]: r["matched_via"] for r in results} + + assert reasons["global-baseline"] == "scope:*" + assert "tag:healthcare" in reasons["hipaa-policy"] + assert "team:health-team" in reasons["team-policy"] + + def test_tags_only_attachment_matches_any_team_key_model(self): + """Test the primary use case: tags-only attachment with no team/key/model + constraint matches any request that carries the tag.""" + registry = AttachmentRegistry() + registry.load_attachments([ + {"policy": "hipaa-guardrails", "tags": ["healthcare"]}, + ]) + + # Should match regardless of team/key/model + context = PolicyMatchContext( + team_alias="random-team", key_alias="random-key", model="claude-3", + tags=["healthcare"], + ) + attached = registry.get_attached_policies(context) + assert "hipaa-guardrails" in attached + + # Should not match without the tag + context_no_tag = PolicyMatchContext( + team_alias="random-team", key_alias="random-key", model="claude-3", + ) + assert registry.get_attached_policies(context_no_tag) == [] + + def test_attachment_with_no_scope_matches_everything(self): + """Test that an attachment with no scope/teams/keys/models/tags + matches everything because teams/keys/models default to ['*'].""" + registry = AttachmentRegistry() + registry.load_attachments([ + {"policy": "catch-all"}, + ]) + + context = PolicyMatchContext( + team_alias="any-team", key_alias="any-key", model="gpt-4", + ) + attached = registry.get_attached_policies(context) + assert "catch-all" in attached + + class TestAttachmentRegistrySingleton: """Test global singleton behavior.""" diff --git a/tests/test_litellm/proxy/policy_engine/test_policy_matcher.py b/tests/test_litellm/proxy/policy_engine/test_policy_matcher.py index c011f31af6..fccb26496a 100644 --- a/tests/test_litellm/proxy/policy_engine/test_policy_matcher.py +++ b/tests/test_litellm/proxy/policy_engine/test_policy_matcher.py @@ -64,6 +64,70 @@ class TestPolicyMatcherScopeMatching: assert PolicyMatcher.scope_matches(scope, context) is True +class TestPolicyMatcherScopeMatchingWithTags: + """Test scope matching with tag patterns.""" + + def test_scope_tag_matching(self): + """Test scope tag matching: exact, wildcard, no-match, and empty context tags.""" + # Exact match + scope = PolicyScope(teams=["*"], keys=["*"], models=["*"], tags=["healthcare"]) + context = PolicyMatchContext( + team_alias="team", key_alias="key", model="gpt-4", + tags=["healthcare", "internal"], + ) + assert PolicyMatcher.scope_matches(scope, context) is True + + # Wildcard match + scope_wc = PolicyScope(teams=["*"], keys=["*"], models=["*"], tags=["health-*"]) + context_wc = PolicyMatchContext( + team_alias="team", key_alias="key", model="gpt-4", + tags=["health-prod"], + ) + assert PolicyMatcher.scope_matches(scope_wc, context_wc) is True + + # No match — wrong tag + context_wrong = PolicyMatchContext( + team_alias="team", key_alias="key", model="gpt-4", + tags=["finance"], + ) + assert PolicyMatcher.scope_matches(scope, context_wrong) is False + + # No match — context has no tags + context_none = PolicyMatchContext( + team_alias="team", key_alias="key", model="gpt-4", tags=None, + ) + assert PolicyMatcher.scope_matches(scope, context_none) is False + + # Scope without tags matches any context (opt-in semantics) + scope_no_tags = PolicyScope(teams=["*"], keys=["*"], models=["*"]) + assert PolicyMatcher.scope_matches(scope_no_tags, context) is True + + def test_scope_tags_and_team_combined(self): + """Test scope with both tags and team — both must match (AND logic).""" + scope = PolicyScope(teams=["team-a"], keys=["*"], models=["*"], tags=["healthcare"]) + + # Both match + context_both = PolicyMatchContext( + team_alias="team-a", key_alias="key", model="gpt-4", + tags=["healthcare"], + ) + assert PolicyMatcher.scope_matches(scope, context_both) is True + + # Tag matches, team doesn't + context_wrong_team = PolicyMatchContext( + team_alias="team-b", key_alias="key", model="gpt-4", + tags=["healthcare"], + ) + assert PolicyMatcher.scope_matches(scope, context_wrong_team) is False + + # Team matches, tag doesn't + context_wrong_tag = PolicyMatchContext( + team_alias="team-a", key_alias="key", model="gpt-4", + tags=["finance"], + ) + assert PolicyMatcher.scope_matches(scope, context_wrong_tag) is False + + class TestPolicyMatcherWithAttachments: """Test getting matching policies via attachments.""" diff --git a/ui/litellm-dashboard/e2e_tests/constants.ts b/ui/litellm-dashboard/e2e_tests/constants.ts index b07bd68fcf..58b56af0a2 100644 --- a/ui/litellm-dashboard/e2e_tests/constants.ts +++ b/ui/litellm-dashboard/e2e_tests/constants.ts @@ -1 +1,6 @@ export const ADMIN_STORAGE_PATH = "admin.storageState.json"; + +export const E2E_UPDATE_LIMITS_KEY_ID_PREFIX = "102c"; +export const E2E_DELETE_KEY_ID_PREFIX = "94a5"; +export const E2E_DELETE_KEY_NAME = "e2eDeleteKey"; +export const E2E_REGENERATE_KEY_ID_PREFIX = "593a"; diff --git a/ui/litellm-dashboard/e2e_tests/tests/keys/deleteKey.spec.ts b/ui/litellm-dashboard/e2e_tests/tests/keys/deleteKey.spec.ts new file mode 100644 index 0000000000..a584131625 --- /dev/null +++ b/ui/litellm-dashboard/e2e_tests/tests/keys/deleteKey.spec.ts @@ -0,0 +1,25 @@ +import { test, expect } from "@playwright/test"; +import { ADMIN_STORAGE_PATH, E2E_DELETE_KEY_ID_PREFIX, E2E_DELETE_KEY_NAME } from "../../constants"; +import { Page } from "../../fixtures/pages"; +import { navigateToPage } from "../../helpers/navigation"; + +test.describe("Delete Key", () => { + test.use({ storageState: ADMIN_STORAGE_PATH }); + + test("Able to delete a key", async ({ page }) => { + await navigateToPage(page, Page.ApiKeys); + await expect(page.getByRole("button", { name: "Next" })).toBeVisible(); + await page + .locator("button", { + hasText: E2E_DELETE_KEY_ID_PREFIX, + }) + .click(); + await page.getByRole("button", { name: "Delete Key" }).click(); + await page.getByRole("textbox", { name: E2E_DELETE_KEY_NAME }).click(); + await page.getByRole("textbox", { name: E2E_DELETE_KEY_NAME }).fill(E2E_DELETE_KEY_NAME); + const deleteButton = page.getByRole("button", { name: "Delete", exact: true }); + await expect(deleteButton).toBeEnabled(); + await deleteButton.click(); + await expect(page.getByText("Key deleted successfully")).toBeVisible(); + }); +}); diff --git a/ui/litellm-dashboard/e2e_tests/tests/keys/regenerateKey.spec.ts b/ui/litellm-dashboard/e2e_tests/tests/keys/regenerateKey.spec.ts new file mode 100644 index 0000000000..0188a4f81c --- /dev/null +++ b/ui/litellm-dashboard/e2e_tests/tests/keys/regenerateKey.spec.ts @@ -0,0 +1,21 @@ +import { test, expect } from "@playwright/test"; +import { ADMIN_STORAGE_PATH, E2E_REGENERATE_KEY_ID_PREFIX } from "../../constants"; +import { Page } from "../../fixtures/pages"; +import { navigateToPage } from "../../helpers/navigation"; + +test.describe("Regenerate Key", () => { + test.use({ storageState: ADMIN_STORAGE_PATH }); + + test("Able to regenerate a key", async ({ page }) => { + await navigateToPage(page, Page.ApiKeys); + await expect(page.getByRole("button", { name: "Next" })).toBeVisible(); + await page + .locator("button", { + hasText: E2E_REGENERATE_KEY_ID_PREFIX, + }) + .click(); + await page.getByRole("button", { name: "Regenerate Key" }).click(); + await page.getByRole("button", { name: "Regenerate", exact: true }).click(); + await expect(page.getByText("Virtual Key regenerated")).toBeVisible(); + }); +}); diff --git a/ui/litellm-dashboard/e2e_tests/tests/keys/updateKeyLimits.spec.ts b/ui/litellm-dashboard/e2e_tests/tests/keys/updateKeyLimits.spec.ts new file mode 100644 index 0000000000..6cae36272a --- /dev/null +++ b/ui/litellm-dashboard/e2e_tests/tests/keys/updateKeyLimits.spec.ts @@ -0,0 +1,27 @@ +import { test, expect } from "@playwright/test"; +import { ADMIN_STORAGE_PATH, E2E_UPDATE_LIMITS_KEY_ID_PREFIX } from "../../constants"; +import { Page } from "../../fixtures/pages"; +import { navigateToPage } from "../../helpers/navigation"; + +test.describe("Update Key TPM and RPM Limits", () => { + test.use({ storageState: ADMIN_STORAGE_PATH }); + + test("Able to update a key's TPM and RPM limits", async ({ page }) => { + await navigateToPage(page, Page.ApiKeys); + await expect(page.getByRole("button", { name: "Next" })).toBeVisible(); + await page + .locator("button", { + hasText: E2E_UPDATE_LIMITS_KEY_ID_PREFIX, + }) + .click(); + await page.getByRole("tab", { name: "Settings" }).click(); + await page.getByRole("button", { name: "Edit Settings" }).click(); + await page.getByRole("spinbutton", { name: "TPM Limit" }).click(); + await page.getByRole("spinbutton", { name: "TPM Limit" }).fill("123"); + await page.getByRole("spinbutton", { name: "RPM Limit" }).click(); + await page.getByRole("spinbutton", { name: "RPM Limit" }).fill("456"); + await page.getByRole("button", { name: "Save Changes" }).click(); + await expect(page.getByRole("paragraph").filter({ hasText: "TPM: 123" })).toBeVisible(); + await expect(page.getByRole("paragraph").filter({ hasText: "RPM: 456" })).toBeVisible(); + }); +}); diff --git a/ui/litellm-dashboard/scripts/e2e_tests/neonHelperScripts.ts b/ui/litellm-dashboard/scripts/e2e_tests/neonHelperScripts.ts index 3078a0d90d..089ad4e792 100644 --- a/ui/litellm-dashboard/scripts/e2e_tests/neonHelperScripts.ts +++ b/ui/litellm-dashboard/scripts/e2e_tests/neonHelperScripts.ts @@ -1,4 +1,4 @@ -import { createApiClient } from "@neondatabase/api-client"; +import { createApiClient, EndpointType } from "@neondatabase/api-client"; import { config } from "dotenv"; import { resolve } from "path"; @@ -27,6 +27,13 @@ export async function createNeonE2ETestingBranch(projectId: string, parentBranch parent_id: parentBranchId, expires_at: expireAt ?? new Date(Date.now() + 1000 * 60 * 30).toISOString(), }, + endpoints: [ + { + type: EndpointType.ReadWrite, + autoscaling_limit_min_cu: 0.25, + autoscaling_limit_max_cu: 1, + }, + ], }); return response; } catch (error) { @@ -35,13 +42,15 @@ export async function createNeonE2ETestingBranch(projectId: string, parentBranch } export async function getNeonE2ETestingBranchConnectionString() { - await createNeonE2ETestingBranch(PROJECT_ID, PARENT_BRANCH); - + const createBranchResponse = await createNeonE2ETestingBranch(PROJECT_ID, PARENT_BRANCH); + const projectId = createBranchResponse.data.branch.project_id; const response = await apiClient.getConnectionUri({ database_name: NEON_E2E_UI_TEST_DB_NAME, role_name: "neondb_owner", - projectId: PROJECT_ID, + projectId: projectId, }); console.log("connection string:", response.data.uri); return response.data.uri; } + +getNeonE2ETestingBranchConnectionString(); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/components/Sidebar2.tsx b/ui/litellm-dashboard/src/app/(dashboard)/components/Sidebar2.tsx index 405f8329b6..a74d3c108d 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/components/Sidebar2.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/components/Sidebar2.tsx @@ -31,7 +31,7 @@ import { import * as React from "react"; import { useRouter, usePathname } from "next/navigation"; import { all_admin_roles, internalUserRoles, isAdminRole, rolesWithWriteAccess } from "@/utils/roles"; -import UsageIndicator from "@/components/usage_indicator"; +import UsageIndicator from "@/components/UsageIndicator"; import { serverRootPath } from "@/components/networking"; const { Sider } = Layout; @@ -64,7 +64,7 @@ const getBasePath = () => { const raw = process.env.NEXT_PUBLIC_BASE_URL ?? ""; const trimmed = raw.replace(/^\/+|\/+$/g, ""); // strip leading/trailing slashes const uiPath = trimmed ? `/${trimmed}/` : "/"; - + // If serverRootPath is set and not "/", prepend it to the UI path if (serverRootPath && serverRootPath !== "/") { // Remove trailing slash from serverRootPath and ensure uiPath has no leading slash for proper joining @@ -72,7 +72,7 @@ const getBasePath = () => { const cleanUiPath = uiPath.replace(/^\/+/, ""); return `${cleanServerRoot}/${cleanUiPath}`; } - + return uiPath; }; @@ -153,170 +153,170 @@ const toHref = (slugOrPath: string) => { // ----- Menu config (unchanged labels/icons; same appearance) ----- const menuItems: MenuItemCfg[] = [ - { key: "1", page: "api-keys", label: "Virtual Keys", icon: }, - { - key: "3", - page: "llm-playground", - label: "Test Key", - icon: , - roles: rolesWithWriteAccess, - }, - { - key: "2", - page: "models", - label: "Models + Endpoints", - icon: , - roles: rolesWithWriteAccess, - }, - { - key: "12", - page: "new_usage", - label: "Usage", - icon: , - roles: [...all_admin_roles, ...internalUserRoles], - }, - { key: "6", page: "teams", label: "Teams", icon: }, - { - key: "17", - page: "organizations", - label: "Organizations", - icon: , - roles: all_admin_roles, - }, - { - key: "5", - page: "users", - label: "Internal Users", - icon: , - roles: all_admin_roles, - }, - { key: "14", page: "api_ref", label: "API Reference", icon: }, - { - key: "16", - page: "model-hub-table", - label: "Model Hub", - icon: , - }, - { key: "15", page: "logs", label: "Logs", icon: }, - { - key: "11", - page: "guardrails", - label: "Guardrails", - icon: , - roles: all_admin_roles, - }, - { - key: "28", - page: "policies", - label: "Policies", - icon: , - roles: all_admin_roles, - }, - { - key: "26", - page: "tools", - label: "Tools", - icon: , - children: [ - { key: "18", page: "mcp-servers", label: "MCP Servers", icon: }, - { - key: "21", - page: "vector-stores", - label: "Vector Stores", - icon: , - roles: all_admin_roles, - }, - ], - }, - { - key: "experimental", - page: "experimental", - label: "Experimental", - icon: , - children: [ - { - key: "9", - page: "caching", - label: "Caching", - icon: , - roles: all_admin_roles, - }, - { - key: "25", - page: "prompts", - label: "Prompts", - icon: , - roles: all_admin_roles, - }, - { - key: "10", - page: "budgets", - label: "Budgets", - icon: , - roles: all_admin_roles, - }, - { - key: "20", - page: "transform-request", - label: "API Playground", - icon: , - roles: [...all_admin_roles, ...internalUserRoles], - }, - { - key: "19", - page: "tag-management", - label: "Tag Management", - icon: , - roles: all_admin_roles, - }, - { - key: "27", - page: "claude-code-plugins", - label: "Claude Code Plugins", - icon: , - roles: all_admin_roles, - }, - { key: "4", page: "usage", label: "Old Usage", icon: }, - ], - }, - { - key: "settings", - page: "settings", - label: "Settings", - icon: , - roles: all_admin_roles, - children: [ - { - key: "11", - page: "general-settings", - label: "Router Settings", - icon: , - roles: all_admin_roles, - }, - { - key: "8", - page: "settings", - label: "Logging & Alerts", - icon: , - roles: all_admin_roles, - }, - { - key: "13", - page: "admin-panel", - label: "Admin Settings", - icon: , - roles: all_admin_roles, - }, - { - key: "14", - page: "ui-theme", - label: "UI Theme", - icon: , - roles: all_admin_roles, - }, - ], - }, - ]; + { key: "1", page: "api-keys", label: "Virtual Keys", icon: }, + { + key: "3", + page: "llm-playground", + label: "Test Key", + icon: , + roles: rolesWithWriteAccess, + }, + { + key: "2", + page: "models", + label: "Models + Endpoints", + icon: , + roles: rolesWithWriteAccess, + }, + { + key: "12", + page: "new_usage", + label: "Usage", + icon: , + roles: [...all_admin_roles, ...internalUserRoles], + }, + { key: "6", page: "teams", label: "Teams", icon: }, + { + key: "17", + page: "organizations", + label: "Organizations", + icon: , + roles: all_admin_roles, + }, + { + key: "5", + page: "users", + label: "Internal Users", + icon: , + roles: all_admin_roles, + }, + { key: "14", page: "api_ref", label: "API Reference", icon: }, + { + key: "16", + page: "model-hub-table", + label: "Model Hub", + icon: , + }, + { key: "15", page: "logs", label: "Logs", icon: }, + { + key: "11", + page: "guardrails", + label: "Guardrails", + icon: , + roles: all_admin_roles, + }, + { + key: "28", + page: "policies", + label: "Policies", + icon: , + roles: all_admin_roles, + }, + { + key: "26", + page: "tools", + label: "Tools", + icon: , + children: [ + { key: "18", page: "mcp-servers", label: "MCP Servers", icon: }, + { + key: "21", + page: "vector-stores", + label: "Vector Stores", + icon: , + roles: all_admin_roles, + }, + ], + }, + { + key: "experimental", + page: "experimental", + label: "Experimental", + icon: , + children: [ + { + key: "9", + page: "caching", + label: "Caching", + icon: , + roles: all_admin_roles, + }, + { + key: "25", + page: "prompts", + label: "Prompts", + icon: , + roles: all_admin_roles, + }, + { + key: "10", + page: "budgets", + label: "Budgets", + icon: , + roles: all_admin_roles, + }, + { + key: "20", + page: "transform-request", + label: "API Playground", + icon: , + roles: [...all_admin_roles, ...internalUserRoles], + }, + { + key: "19", + page: "tag-management", + label: "Tag Management", + icon: , + roles: all_admin_roles, + }, + { + key: "27", + page: "claude-code-plugins", + label: "Claude Code Plugins", + icon: , + roles: all_admin_roles, + }, + { key: "4", page: "usage", label: "Old Usage", icon: }, + ], + }, + { + key: "settings", + page: "settings", + label: "Settings", + icon: , + roles: all_admin_roles, + children: [ + { + key: "11", + page: "general-settings", + label: "Router Settings", + icon: , + roles: all_admin_roles, + }, + { + key: "8", + page: "settings", + label: "Logging & Alerts", + icon: , + roles: all_admin_roles, + }, + { + key: "13", + page: "admin-panel", + label: "Admin Settings", + icon: , + roles: all_admin_roles, + }, + { + key: "14", + page: "ui-theme", + label: "UI Theme", + icon: , + roles: all_admin_roles, + }, + ], + }, +]; const Sidebar2: React.FC = ({ accessToken, userRole, defaultSelectedKey, collapsed = false }) => { const router = useRouter(); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/hooks/uiConfig/useUIConfig.test.ts b/ui/litellm-dashboard/src/app/(dashboard)/hooks/uiConfig/useUIConfig.test.ts index 6429aeafb5..aba5dddf13 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/hooks/uiConfig/useUIConfig.test.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/hooks/uiConfig/useUIConfig.test.ts @@ -23,6 +23,7 @@ vi.mock("../common/queryKeysFactory", () => ({ // Mock data const mockUIConfig: LiteLLMWellKnownUiConfig = { + sso_configured: true, server_root_path: "/api", proxy_base_url: "https://proxy.example.com", auto_redirect_to_sso: true, @@ -99,6 +100,7 @@ describe("useUIConfig", () => { server_root_path: "/v1", proxy_base_url: null, auto_redirect_to_sso: false, + sso_configured: false, admin_ui_disabled: true, }; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/hooks/useAuthorized.test.ts b/ui/litellm-dashboard/src/app/(dashboard)/hooks/useAuthorized.test.ts index ef4a779b50..76a3129d6d 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/hooks/useAuthorized.test.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/hooks/useAuthorized.test.ts @@ -90,6 +90,7 @@ describe("useAuthorized", () => { proxy_base_url: null, auto_redirect_to_sso: false, admin_ui_disabled: false, + sso_configured: false, }); const decodedPayload = { @@ -131,6 +132,7 @@ describe("useAuthorized", () => { proxy_base_url: null, auto_redirect_to_sso: false, admin_ui_disabled: false, + sso_configured: false, }); decodeTokenMock.mockReturnValue(null); @@ -155,6 +157,7 @@ describe("useAuthorized", () => { proxy_base_url: null, auto_redirect_to_sso: false, admin_ui_disabled: true, + sso_configured: false, }); const decodedPayload = { @@ -190,6 +193,7 @@ describe("useAuthorized", () => { proxy_base_url: null, auto_redirect_to_sso: false, admin_ui_disabled: false, + sso_configured: false, }); decodeTokenMock.mockReturnValue(null); @@ -212,6 +216,7 @@ describe("useAuthorized", () => { proxy_base_url: null, auto_redirect_to_sso: false, admin_ui_disabled: false, + sso_configured: false, }); const decodedPayload = { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/hooks/useDisableUsageIndicator.test.ts b/ui/litellm-dashboard/src/app/(dashboard)/hooks/useDisableUsageIndicator.test.ts new file mode 100644 index 0000000000..bd0e69c0de --- /dev/null +++ b/ui/litellm-dashboard/src/app/(dashboard)/hooks/useDisableUsageIndicator.test.ts @@ -0,0 +1,190 @@ +import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; +import { act, renderHook, waitFor } from "@testing-library/react"; +import { useDisableUsageIndicator } from "./useDisableUsageIndicator"; +import { LOCAL_STORAGE_EVENT } from "@/utils/localStorageUtils"; + +describe("useDisableUsageIndicator", () => { + const STORAGE_KEY = "disableUsageIndicator"; + + beforeEach(() => { + localStorage.clear(); + vi.clearAllMocks(); + }); + + afterEach(() => { + localStorage.clear(); + }); + + it("should return false when localStorage is empty", () => { + const { result } = renderHook(() => useDisableUsageIndicator()); + + expect(result.current).toBe(false); + }); + + it("should return false when localStorage value is not 'true'", () => { + localStorage.setItem(STORAGE_KEY, "false"); + + const { result } = renderHook(() => useDisableUsageIndicator()); + + expect(result.current).toBe(false); + }); + + it("should return true when localStorage value is 'true'", () => { + localStorage.setItem(STORAGE_KEY, "true"); + + const { result } = renderHook(() => useDisableUsageIndicator()); + + expect(result.current).toBe(true); + }); + + it("should return false when localStorage value is an empty string", () => { + localStorage.setItem(STORAGE_KEY, ""); + + const { result } = renderHook(() => useDisableUsageIndicator()); + + expect(result.current).toBe(false); + }); + + it("should update when storage event fires for the correct key", async () => { + const { result } = renderHook(() => useDisableUsageIndicator()); + + expect(result.current).toBe(false); + + await act(async () => { + localStorage.setItem(STORAGE_KEY, "true"); + const storageEvent = new StorageEvent("storage", { + key: STORAGE_KEY, + newValue: "true", + }); + window.dispatchEvent(storageEvent); + }); + + await waitFor(() => { + expect(result.current).toBe(true); + }); + }); + + it("should not update when storage event fires for a different key", () => { + localStorage.setItem(STORAGE_KEY, "false"); + const { result } = renderHook(() => useDisableUsageIndicator()); + + expect(result.current).toBe(false); + + const storageEvent = new StorageEvent("storage", { + key: "otherKey", + newValue: "true", + }); + window.dispatchEvent(storageEvent); + + expect(result.current).toBe(false); + }); + + it("should update when custom LOCAL_STORAGE_EVENT fires for the correct key", async () => { + const { result } = renderHook(() => useDisableUsageIndicator()); + + expect(result.current).toBe(false); + + await act(async () => { + localStorage.setItem(STORAGE_KEY, "true"); + const customEvent = new CustomEvent(LOCAL_STORAGE_EVENT, { + detail: { key: STORAGE_KEY }, + }); + window.dispatchEvent(customEvent); + }); + + await waitFor(() => { + expect(result.current).toBe(true); + }); + }); + + it("should not update when custom LOCAL_STORAGE_EVENT fires for a different key", () => { + localStorage.setItem(STORAGE_KEY, "false"); + const { result } = renderHook(() => useDisableUsageIndicator()); + + expect(result.current).toBe(false); + + const customEvent = new CustomEvent(LOCAL_STORAGE_EVENT, { + detail: { key: "otherKey" }, + }); + window.dispatchEvent(customEvent); + + expect(result.current).toBe(false); + }); + + it("should update when localStorage changes from false to true via custom event", async () => { + localStorage.setItem(STORAGE_KEY, "false"); + const { result } = renderHook(() => useDisableUsageIndicator()); + + expect(result.current).toBe(false); + + await act(async () => { + localStorage.setItem(STORAGE_KEY, "true"); + const customEvent = new CustomEvent(LOCAL_STORAGE_EVENT, { + detail: { key: STORAGE_KEY }, + }); + window.dispatchEvent(customEvent); + }); + + await waitFor(() => { + expect(result.current).toBe(true); + }); + }); + + it("should update when localStorage changes from true to false via storage event", async () => { + localStorage.setItem(STORAGE_KEY, "true"); + const { result } = renderHook(() => useDisableUsageIndicator()); + + expect(result.current).toBe(true); + + await act(async () => { + localStorage.setItem(STORAGE_KEY, "false"); + const storageEvent = new StorageEvent("storage", { + key: STORAGE_KEY, + newValue: "false", + }); + window.dispatchEvent(storageEvent); + }); + + await waitFor(() => { + expect(result.current).toBe(false); + }); + }); + + it("should cleanup event listeners on unmount", () => { + const addEventListenerSpy = vi.spyOn(window, "addEventListener"); + const removeEventListenerSpy = vi.spyOn(window, "removeEventListener"); + + const { unmount } = renderHook(() => useDisableUsageIndicator()); + + expect(addEventListenerSpy).toHaveBeenCalledTimes(2); + expect(addEventListenerSpy).toHaveBeenCalledWith("storage", expect.any(Function)); + expect(addEventListenerSpy).toHaveBeenCalledWith(LOCAL_STORAGE_EVENT, expect.any(Function)); + + unmount(); + + expect(removeEventListenerSpy).toHaveBeenCalledTimes(2); + expect(removeEventListenerSpy).toHaveBeenCalledWith("storage", expect.any(Function)); + expect(removeEventListenerSpy).toHaveBeenCalledWith(LOCAL_STORAGE_EVENT, expect.any(Function)); + }); + + it("should handle multiple hooks independently", async () => { + const { result: result1 } = renderHook(() => useDisableUsageIndicator()); + const { result: result2 } = renderHook(() => useDisableUsageIndicator()); + + expect(result1.current).toBe(false); + expect(result2.current).toBe(false); + + await act(async () => { + localStorage.setItem(STORAGE_KEY, "true"); + const customEvent = new CustomEvent(LOCAL_STORAGE_EVENT, { + detail: { key: STORAGE_KEY }, + }); + window.dispatchEvent(customEvent); + }); + + await waitFor(() => { + expect(result1.current).toBe(true); + expect(result2.current).toBe(true); + }); + }); +}); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/hooks/useDisableUsageIndicator.ts b/ui/litellm-dashboard/src/app/(dashboard)/hooks/useDisableUsageIndicator.ts new file mode 100644 index 0000000000..7f4e229509 --- /dev/null +++ b/ui/litellm-dashboard/src/app/(dashboard)/hooks/useDisableUsageIndicator.ts @@ -0,0 +1,33 @@ +import { getLocalStorageItem, LOCAL_STORAGE_EVENT } from "@/utils/localStorageUtils"; +import { useSyncExternalStore } from "react"; + +function subscribe(callback: () => void) { + const onStorage = (e: StorageEvent) => { + if (e.key === "disableUsageIndicator") { + callback(); + } + }; + + const onCustom = (e: Event) => { + const { key } = (e as CustomEvent).detail; + if (key === "disableUsageIndicator") { + callback(); + } + }; + + window.addEventListener("storage", onStorage); + window.addEventListener(LOCAL_STORAGE_EVENT, onCustom); + + return () => { + window.removeEventListener("storage", onStorage); + window.removeEventListener(LOCAL_STORAGE_EVENT, onCustom); + }; +} + +function getSnapshot() { + return getLocalStorageItem("disableUsageIndicator") === "true"; +} + +export function useDisableUsageIndicator() { + return useSyncExternalStore(subscribe, getSnapshot); +} diff --git a/ui/litellm-dashboard/src/app/login/LoginPage.test.tsx b/ui/litellm-dashboard/src/app/login/LoginPage.test.tsx index 7983451260..ad2dde2da8 100644 --- a/ui/litellm-dashboard/src/app/login/LoginPage.test.tsx +++ b/ui/litellm-dashboard/src/app/login/LoginPage.test.tsx @@ -64,7 +64,12 @@ describe("LoginPage", () => { it("should render", async () => { (useUIConfig as ReturnType).mockReturnValue({ - data: { auto_redirect_to_sso: false, server_root_path: "/", proxy_base_url: null }, + data: { + auto_redirect_to_sso: false, + server_root_path: "/", + proxy_base_url: null, + sso_configured: false, + }, isLoading: false, }); (getCookie as ReturnType).mockReturnValue(null); @@ -84,7 +89,12 @@ describe("LoginPage", () => { it("should call router.replace to dashboard when jwt is valid", async () => { const validToken = "valid-token"; (useUIConfig as ReturnType).mockReturnValue({ - data: { auto_redirect_to_sso: false, server_root_path: "/", proxy_base_url: null }, + data: { + auto_redirect_to_sso: false, + server_root_path: "/", + proxy_base_url: null, + sso_configured: false, + }, isLoading: false, }); (getCookie as ReturnType).mockReturnValue(validToken); @@ -105,7 +115,12 @@ describe("LoginPage", () => { it("should call router.push to SSO when jwt is invalid and auto_redirect_to_sso is true", async () => { const invalidToken = "invalid-token"; (useUIConfig as ReturnType).mockReturnValue({ - data: { auto_redirect_to_sso: true, server_root_path: "/", proxy_base_url: null }, + data: { + auto_redirect_to_sso: true, + server_root_path: "/", + proxy_base_url: null, + sso_configured: true, + }, isLoading: false, }); (getCookie as ReturnType).mockReturnValue(invalidToken); @@ -126,7 +141,12 @@ describe("LoginPage", () => { it("should not call router when jwt is invalid and auto_redirect_to_sso is false", async () => { const invalidToken = "invalid-token"; (useUIConfig as ReturnType).mockReturnValue({ - data: { auto_redirect_to_sso: false, server_root_path: "/", proxy_base_url: null }, + data: { + auto_redirect_to_sso: false, + server_root_path: "/", + proxy_base_url: null, + sso_configured: false, + }, isLoading: false, }); (getCookie as ReturnType).mockReturnValue(invalidToken); @@ -150,7 +170,12 @@ describe("LoginPage", () => { it("should send user to dashboard when jwt is valid even if auto_redirect_to_sso is true", async () => { const validToken = "valid-token"; (useUIConfig as ReturnType).mockReturnValue({ - data: { auto_redirect_to_sso: true, server_root_path: "/", proxy_base_url: null }, + data: { + auto_redirect_to_sso: true, + server_root_path: "/", + proxy_base_url: null, + sso_configured: true, + }, isLoading: false, }); (getCookie as ReturnType).mockReturnValue(validToken); @@ -172,7 +197,12 @@ describe("LoginPage", () => { it("should show alert when admin_ui_disabled is true", async () => { (useUIConfig as ReturnType).mockReturnValue({ - data: { admin_ui_disabled: true, server_root_path: "/", proxy_base_url: null }, + data: { + admin_ui_disabled: true, + server_root_path: "/", + proxy_base_url: null, + sso_configured: false, + }, isLoading: false, }); (getCookie as ReturnType).mockReturnValue(null); @@ -192,4 +222,60 @@ describe("LoginPage", () => { expect(mockPush).not.toHaveBeenCalled(); expect(mockReplace).not.toHaveBeenCalled(); }); + + it("should show Login with SSO button when sso_configured is true", async () => { + (useUIConfig as ReturnType).mockReturnValue({ + data: { + auto_redirect_to_sso: false, + server_root_path: "/", + proxy_base_url: null, + sso_configured: true, + }, + isLoading: false, + }); + (getCookie as ReturnType).mockReturnValue(null); + (isJwtExpired as ReturnType).mockReturnValue(true); + + const queryClient = createQueryClient(); + render( + + + , + ); + + await waitFor(() => { + expect(screen.getByRole("heading", { name: "Login" })).toBeInTheDocument(); + }); + + expect(screen.getByRole("button", { name: "Login with SSO" })).toBeInTheDocument(); + }); + + it("should show disabled Login with SSO button with popover when sso_configured is false", async () => { + (useUIConfig as ReturnType).mockReturnValue({ + data: { + auto_redirect_to_sso: false, + server_root_path: "/", + proxy_base_url: null, + sso_configured: false, + }, + isLoading: false, + }); + (getCookie as ReturnType).mockReturnValue(null); + (isJwtExpired as ReturnType).mockReturnValue(true); + + const queryClient = createQueryClient(); + render( + + + , + ); + + await waitFor(() => { + expect(screen.getByRole("heading", { name: "Login" })).toBeInTheDocument(); + }); + + const ssoButton = screen.getByRole("button", { name: "Login with SSO" }); + expect(ssoButton).toBeInTheDocument(); + expect(ssoButton).toBeDisabled(); + }); }); diff --git a/ui/litellm-dashboard/src/app/login/LoginPage.tsx b/ui/litellm-dashboard/src/app/login/LoginPage.tsx index 620cb41dfe..a05fa4e214 100644 --- a/ui/litellm-dashboard/src/app/login/LoginPage.tsx +++ b/ui/litellm-dashboard/src/app/login/LoginPage.tsx @@ -8,7 +8,7 @@ import { getCookie } from "@/utils/cookieUtils"; import { isJwtExpired } from "@/utils/jwtUtils"; import { InfoCircleOutlined } from "@ant-design/icons"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { Alert, Button, Card, Form, Input, Space, Typography } from "antd"; +import { Alert, Button, Card, Form, Input, Popover, Space, Typography } from "antd"; import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; @@ -179,8 +179,39 @@ function LoginPageContent() { {isLoginLoading ? "Logging in..." : "Login"} + + {!uiConfig?.sso_configured ? ( + + + + ) : ( + + )} + + {uiConfig?.sso_configured && ( + Single Sign-On (SSO) is enabled. LiteLLM no longer automatically redirects to the SSO login flow upon loading this page. To re-enable auto-redirect-to-SSO, set AUTO_REDIRECT_UI_LOGIN_TO_SSO=true in your environment configuration.} + /> + )} ); diff --git a/ui/litellm-dashboard/src/components/AIHub/ModelHubTable.test.tsx b/ui/litellm-dashboard/src/components/AIHub/ModelHubTable.test.tsx index 0a5cd17e57..ee59ac84ec 100644 --- a/ui/litellm-dashboard/src/components/AIHub/ModelHubTable.test.tsx +++ b/ui/litellm-dashboard/src/components/AIHub/ModelHubTable.test.tsx @@ -71,6 +71,7 @@ describe("ModelHubTable", () => { proxy_base_url: "http://localhost:4000", auto_redirect_to_sso: false, admin_ui_disabled: false, + sso_configured: false, }); vi.mocked(networking.modelHubPublicModelsCall).mockResolvedValue([]); vi.mocked(networking.getUiSettings).mockResolvedValue({ @@ -140,6 +141,7 @@ describe("ModelHubTable", () => { proxy_base_url: "http://localhost:4000", auto_redirect_to_sso: false, admin_ui_disabled: false, + sso_configured: false, }); modelHubPublicModelsCallMock.mockResolvedValue([]); vi.mocked(networking.getUiSettings).mockResolvedValue({ diff --git a/ui/litellm-dashboard/src/components/Navbar/UserDropdown/UserDropdown.tsx b/ui/litellm-dashboard/src/components/Navbar/UserDropdown/UserDropdown.tsx index f80af33f9e..90e02ae447 100644 --- a/ui/litellm-dashboard/src/components/Navbar/UserDropdown/UserDropdown.tsx +++ b/ui/litellm-dashboard/src/components/Navbar/UserDropdown/UserDropdown.tsx @@ -1,5 +1,6 @@ import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; import { useDisableShowPrompts } from "@/app/(dashboard)/hooks/useDisableShowPrompts"; +import { useDisableUsageIndicator } from "@/app/(dashboard)/hooks/useDisableUsageIndicator"; import { emitLocalStorageChange, getLocalStorageItem, @@ -27,6 +28,7 @@ interface UserDropdownProps { const UserDropdown: React.FC = ({ onLogout }) => { const { userId, userEmail, userRole, premiumUser } = useAuthorized(); const disableShowPrompts = useDisableShowPrompts(); + const disableUsageIndicator = useDisableUsageIndicator(); const [disableShowNewBadge, setDisableShowNewBadge] = useState(false); useEffect(() => { @@ -129,6 +131,23 @@ const UserDropdown: React.FC = ({ onLogout }) => { aria-label="Toggle hide all prompts" /> + + Hide Usage Indicator + { + if (checked) { + setLocalStorageItem("disableUsageIndicator", "true"); + emitLocalStorageChange("disableUsageIndicator"); + } else { + removeLocalStorageItem("disableUsageIndicator"); + emitLocalStorageChange("disableUsageIndicator"); + } + }} + aria-label="Toggle hide usage indicator" + /> + ); diff --git a/ui/litellm-dashboard/src/components/UsageIndicator.test.tsx b/ui/litellm-dashboard/src/components/UsageIndicator.test.tsx new file mode 100644 index 0000000000..71a3726398 --- /dev/null +++ b/ui/litellm-dashboard/src/components/UsageIndicator.test.tsx @@ -0,0 +1,186 @@ +import React from "react"; +import { describe, it, expect, vi, beforeEach } from "vitest"; +import { render, screen, waitFor } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import UsageIndicator from "./UsageIndicator"; + +vi.mock("./networking", () => ({ + getRemainingUsers: vi.fn(), +})); + +vi.mock("@/app/(dashboard)/hooks/useDisableUsageIndicator", () => ({ + useDisableUsageIndicator: vi.fn(() => false), +})); + +import { getRemainingUsers } from "./networking"; + +const mockGetRemainingUsers = vi.mocked(getRemainingUsers); + +const DEFAULT_USAGE_DATA = { + total_users: 100, + total_users_used: 1, + total_users_remaining: 99, + total_teams: null, + total_teams_used: 0, + total_teams_remaining: null, +}; + +describe("UsageIndicator", () => { + beforeEach(() => { + vi.clearAllMocks(); + mockGetRemainingUsers.mockResolvedValue(DEFAULT_USAGE_DATA); + }); + + it("should render when given access token and usage data loads", async () => { + render(); + + await screen.findByText("Usage"); + + expect(screen.getByText("Usage")).toBeInTheDocument(); + }); + + it("should not show Near limit when users usage is below 80% (1/100 -> 1%)", async () => { + render(); + + await screen.findByText("Usage"); + + expect(screen.queryByText("Near limit")).not.toBeInTheDocument(); + }); + + it("should render nothing when both total_users and total_teams are null", async () => { + mockGetRemainingUsers.mockResolvedValue({ + total_users: null, + total_teams: null, + total_users_used: 520, + total_teams_used: 4, + total_teams_remaining: null, + total_users_remaining: null, + }); + + render(); + + await waitFor(() => { + expect(screen.queryByText("Usage")).not.toBeInTheDocument(); + expect(screen.queryByText("Loading...")).not.toBeInTheDocument(); + }); + }); + + it("should show Near limit for Teams when at 80% usage (4/5)", async () => { + mockGetRemainingUsers.mockResolvedValue({ + total_users: null, + total_users_used: 0, + total_users_remaining: null, + total_teams: 5, + total_teams_used: 4, + total_teams_remaining: 1, + }); + + render(); + + await screen.findByText("Usage"); + + expect(screen.getByText("Teams")).toBeInTheDocument(); + expect(screen.getByText("Near limit")).toBeInTheDocument(); + }); + + it("should show Over limit for Users when usage exceeds 100% (105/100)", async () => { + mockGetRemainingUsers.mockResolvedValue({ + total_users: 100, + total_users_used: 105, + total_users_remaining: -5, + total_teams: null, + total_teams_used: 0, + total_teams_remaining: null, + }); + + render(); + + await screen.findByText("Usage"); + + expect(screen.getByText("Users")).toBeInTheDocument(); + expect(screen.getByText("Over limit")).toBeInTheDocument(); + }); + + it("should show Over limit for Teams when usage exceeds 100%", async () => { + mockGetRemainingUsers.mockResolvedValue({ + total_users: null, + total_users_used: 0, + total_users_remaining: null, + total_teams: 10, + total_teams_used: 12, + total_teams_remaining: -2, + }); + + render(); + + await screen.findByText("Usage"); + + expect(screen.getByText("Teams")).toBeInTheDocument(); + expect(screen.getByText("Over limit")).toBeInTheDocument(); + }); + + it("should render nothing when accessToken is null", () => { + render(); + + expect(mockGetRemainingUsers).not.toHaveBeenCalled(); + expect(screen.queryByText("Usage")).not.toBeInTheDocument(); + }); + + it("should render nothing when disableUsageIndicator is true", async () => { + const { useDisableUsageIndicator } = await import("@/app/(dashboard)/hooks/useDisableUsageIndicator"); + (useDisableUsageIndicator as ReturnType).mockReturnValue(true); + + render(); + + await waitFor(() => { + expect(screen.queryByText("Usage")).not.toBeInTheDocument(); + }); + + (useDisableUsageIndicator as ReturnType).mockReturnValue(false); + }); + + it("should show Loading while fetching", () => { + mockGetRemainingUsers.mockImplementation(() => new Promise(() => {})); + + render(); + + expect(screen.getByText("Loading...")).toBeInTheDocument(); + }); + + it("should show error message when fetch fails", async () => { + const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {}); + mockGetRemainingUsers.mockRejectedValue(new Error("Network error")); + + render(); + + expect(await screen.findByText("Failed to load usage data")).toBeInTheDocument(); + + consoleSpy.mockRestore(); + }); + + it("should minimize when user clicks minimize button", async () => { + const user = userEvent.setup(); + render(); + + await screen.findByText("Usage"); + + const minimizeButton = screen.getByTitle("Minimize"); + await user.click(minimizeButton); + + expect(screen.queryByText("Users")).not.toBeInTheDocument(); + expect(screen.getByTitle("Show usage details")).toBeInTheDocument(); + }); + + it("should restore from minimized when user clicks restore button", async () => { + const user = userEvent.setup(); + render(); + + await screen.findByText("Usage"); + + await user.click(screen.getByTitle("Minimize")); + await user.click(screen.getByTitle("Show usage details")); + + expect(screen.getByText("Usage")).toBeInTheDocument(); + expect(screen.getByText("Users")).toBeInTheDocument(); + }); +}); diff --git a/ui/litellm-dashboard/src/components/usage_indicator.tsx b/ui/litellm-dashboard/src/components/UsageIndicator.tsx similarity index 98% rename from ui/litellm-dashboard/src/components/usage_indicator.tsx rename to ui/litellm-dashboard/src/components/UsageIndicator.tsx index ed5e8e0755..3976e4d3d6 100644 --- a/ui/litellm-dashboard/src/components/usage_indicator.tsx +++ b/ui/litellm-dashboard/src/components/UsageIndicator.tsx @@ -1,3 +1,4 @@ +import { useDisableUsageIndicator } from "@/app/(dashboard)/hooks/useDisableUsageIndicator"; import { Badge } from "@tremor/react"; import { AlertTriangle, ChevronDown, ChevronUp, Loader2, Minus, TrendingUp, UserCheck, Users } from "lucide-react"; import { useEffect, useState } from "react"; @@ -23,7 +24,7 @@ interface UsageData { } export default function UsageIndicator({ accessToken, width = 220 }: UsageIndicatorProps) { - const position = "bottom-left"; + const disableUsageIndicator = useDisableUsageIndicator(); const [isExpanded, setIsExpanded] = useState(false); const [isMinimized, setIsMinimized] = useState(false); const [data, setData] = useState(null); @@ -541,8 +542,8 @@ export default function UsageIndicator({ accessToken, width = 220 }: UsageIndica ); }; - // Don't render anything if no access token or if both total_users and total_teams are null - if (!accessToken || (data?.total_users === null && data?.total_teams === null)) { + // Don't render anything if disabled, no access token, or if both total_users and total_teams are null + if (disableUsageIndicator || !accessToken || (data?.total_users === null && data?.total_teams === null)) { return null; } diff --git a/ui/litellm-dashboard/src/components/leftnav.tsx b/ui/litellm-dashboard/src/components/leftnav.tsx index b26590989d..e35d70a65b 100644 --- a/ui/litellm-dashboard/src/components/leftnav.tsx +++ b/ui/litellm-dashboard/src/components/leftnav.tsx @@ -29,9 +29,9 @@ import type { MenuProps } from "antd"; import { ConfigProvider, Layout, Menu } from "antd"; import { useMemo } from "react"; import { all_admin_roles, internalUserRoles, isAdminRole, rolesWithWriteAccess } from "../utils/roles"; -import type { Organization } from "./networking"; -import UsageIndicator from "./usage_indicator"; import NewBadge from "./common_components/NewBadge"; +import type { Organization } from "./networking"; +import UsageIndicator from "./UsageIndicator"; const { Sider } = Layout; // Define the props type diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index aa509c65aa..bee0930dda 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -259,6 +259,7 @@ export interface LiteLLMWellKnownUiConfig { proxy_base_url: string | null; auto_redirect_to_sso: boolean; admin_ui_disabled: boolean; + sso_configured: boolean; } export interface CredentialsResponse { @@ -5654,6 +5655,68 @@ export const getResolvedGuardrails = async (accessToken: string, policyId: strin } }; +export const resolvePoliciesCall = async ( + accessToken: string, + context: { team_alias?: string; key_alias?: string; model?: string; tags?: string[] } +) => { + try { + const url = proxyBaseUrl + ? `${proxyBaseUrl}/policies/resolve` + : `/policies/resolve`; + const response = await fetch(url, { + method: "POST", + headers: { + [globalLitellmHeaderName]: `Bearer ${accessToken}`, + "Content-Type": "application/json", + }, + body: JSON.stringify(context), + }); + + if (!response.ok) { + const errorData = await response.json(); + const errorMessage = deriveErrorMessage(errorData); + handleError(errorMessage); + throw new Error(errorMessage); + } + + return await response.json(); + } catch (error) { + console.error("Failed to resolve policies:", error); + throw error; + } +}; + +export const estimateAttachmentImpactCall = async ( + accessToken: string, + attachmentData: any +) => { + try { + const url = proxyBaseUrl + ? `${proxyBaseUrl}/policies/attachments/estimate-impact` + : `/policies/attachments/estimate-impact`; + const response = await fetch(url, { + method: "POST", + headers: { + [globalLitellmHeaderName]: `Bearer ${accessToken}`, + "Content-Type": "application/json", + }, + body: JSON.stringify(attachmentData), + }); + + if (!response.ok) { + const errorData = await response.json(); + const errorMessage = deriveErrorMessage(errorData); + handleError(errorMessage); + throw new Error(errorMessage); + } + + return await response.json(); + } catch (error) { + console.error("Failed to estimate attachment impact:", error); + throw error; + } +}; + export const getPromptsList = async (accessToken: string): Promise => { try { const url = proxyBaseUrl ? `${proxyBaseUrl}/prompts/list` : `/prompts/list`; diff --git a/ui/litellm-dashboard/src/components/policies/add_attachment_form.tsx b/ui/litellm-dashboard/src/components/policies/add_attachment_form.tsx index 9198eda8a9..7426f4fefa 100644 --- a/ui/litellm-dashboard/src/components/policies/add_attachment_form.tsx +++ b/ui/litellm-dashboard/src/components/policies/add_attachment_form.tsx @@ -1,10 +1,12 @@ import React, { useState, useEffect } from "react"; import { Modal, Form, Select, Radio, Divider, Typography } from "antd"; import { Button } from "@tremor/react"; -import { Policy, PolicyAttachmentCreateRequest } from "./types"; -import { teamListCall, keyInfoCall, modelAvailableCall } from "../networking"; +import { Policy } from "./types"; +import { teamListCall, keyListCall, modelAvailableCall, estimateAttachmentImpactCall } from "../networking"; import NotificationsManager from "../molecules/notifications_manager"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; +import { buildAttachmentData } from "./build_attachment_data"; +import ImpactPreviewAlert from "./impact_preview_alert"; const { Text } = Typography; @@ -34,6 +36,8 @@ const AddAttachmentForm: React.FC = ({ const [isLoadingTeams, setIsLoadingTeams] = useState(false); const [isLoadingKeys, setIsLoadingKeys] = useState(false); const [isLoadingModels, setIsLoadingModels] = useState(false); + const [isEstimating, setIsEstimating] = useState(false); + const [impactResult, setImpactResult] = useState(null); const { userId, userRole } = useAuthorized(); useEffect(() => { @@ -46,33 +50,30 @@ const AddAttachmentForm: React.FC = ({ const loadTeamsKeysAndModels = async () => { if (!accessToken) return; - // Load teams + // Load teams — teamListCall returns a plain array of team objects setIsLoadingTeams(true); try { - // Pass null for organizationID since we're loading all teams the user has access to const teamsResponse = await teamListCall(accessToken, null, userId); - if (teamsResponse?.data) { - const teamAliases = teamsResponse.data - .map((t: any) => t.team_alias) - .filter(Boolean); - setAvailableTeams(teamAliases); - } + const teamsArray = Array.isArray(teamsResponse) ? teamsResponse : (teamsResponse?.data || []); + const teamAliases = teamsArray + .map((t: any) => t.team_alias) + .filter(Boolean); + setAvailableTeams(teamAliases); } catch (error) { console.error("Failed to load teams:", error); } finally { setIsLoadingTeams(false); } - // Load keys + // Load keys — keyListCall returns {keys: [...], total_count, ...} setIsLoadingKeys(true); try { - const keysResponse = await keyInfoCall(accessToken, []); - if (keysResponse?.data) { - const keyAliases = keysResponse.data - .map((k: any) => k.key_alias) - .filter(Boolean); - setAvailableKeys(keyAliases); - } + const keysResponse = await keyListCall(accessToken, null, null, null, null, null, 1, 100); + const keysArray = keysResponse?.keys || keysResponse?.data || []; + const keyAliases = keysArray + .map((k: any) => k.key_alias) + .filter(Boolean); + setAvailableKeys(keyAliases); } catch (error) { console.error("Failed to load keys:", error); } finally { @@ -83,12 +84,11 @@ const AddAttachmentForm: React.FC = ({ setIsLoadingModels(true); try { const modelsResponse = await modelAvailableCall(accessToken, userId || "", userRole || ""); - if (modelsResponse?.data) { - const modelIds = modelsResponse.data - .map((m: any) => m.id || m.model_name) - .filter(Boolean); - setAvailableModels(modelIds); - } + const modelsArray = modelsResponse?.data || (Array.isArray(modelsResponse) ? modelsResponse : []); + const modelIds = modelsArray + .map((m: any) => m.id || m.model_name) + .filter(Boolean); + setAvailableModels(modelIds); } catch (error) { console.error("Failed to load models:", error); } finally { @@ -99,6 +99,28 @@ const AddAttachmentForm: React.FC = ({ const resetForm = () => { form.resetFields(); setScopeType("global"); + setImpactResult(null); + }; + + const getAttachmentData = () => buildAttachmentData(form.getFieldsValue(true), scopeType); + + const handlePreviewImpact = async () => { + if (!accessToken) return; + try { + await form.validateFields(["policy_name"]); + } catch { + return; + } + setIsEstimating(true); + try { + const data = getAttachmentData(); + const result = await estimateAttachmentImpactCall(accessToken, data); + setImpactResult(result); + } catch (error) { + console.error("Failed to estimate impact:", error); + } finally { + setIsEstimating(false); + } }; const handleClose = () => { @@ -110,30 +132,12 @@ const AddAttachmentForm: React.FC = ({ try { setIsSubmitting(true); await form.validateFields(); - const values = form.getFieldsValue(true); if (!accessToken) { throw new Error("No access token available"); } - const data: PolicyAttachmentCreateRequest = { - policy_name: values.policy_name, - }; - - if (scopeType === "global") { - data.scope = "*"; - } else { - if (values.teams && values.teams.length > 0) { - data.teams = values.teams; - } - if (values.keys && values.keys.length > 0) { - data.keys = values.keys; - } - if (values.models && values.models.length > 0) { - data.models = values.models; - } - } - + const data = getAttachmentData(); await createAttachment(accessToken, data); NotificationsManager.success("Attachment created successfully"); @@ -195,8 +199,8 @@ const AddAttachmentForm: React.FC = ({ value={scopeType} onChange={(e) => setScopeType(e.target.value)} > + Specific (teams, keys, models, or tags) Global (applies to all requests) - Specific (teams, keys, or models) @@ -267,13 +271,41 @@ const AddAttachmentForm: React.FC = ({ style={{ width: "100%" }} /> + + + Matches tags from key/team metadata.tags or tags passed dynamically in the request body. Use * as a suffix wildcard (e.g., prod-* matches prod-us, prod-eu). + + } + > + ({ label: t, value: t }))} + filterOption={(input, option) => + (option?.label ?? "").toLowerCase().includes(input.toLowerCase()) + } + /> + + + ({ label: m, value: m }))} + filterOption={(input, option) => + (option?.label ?? "").toLowerCase().includes(input.toLowerCase()) + } + /> + + +